Apr 10, 2014

Android HTTP Basic Authentication Service

Been trying to make a simple HTTP Basic Authentication as an Android app (that is mostly backwards compatible).

I am only including all of the necessary resource files. You can define the  icons strings, colours, buttons and styles yourself (just create the appropriate xml file in res/values or res/drawable).

This is my example code:

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="au.com.example"
    android:versionCode="1100"
    android:versionName="1.1.00" >
    <!-- Our Target android environment -->
    <uses-sdk
        android:minSdkVersion="10"
        android:targetSdkVersion="19" />
    <!-- Allows the APP to open network sockets -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- Device support -->
    <supports-screens
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true"
        android:xlargeScreens="true" />
    <!-- Define our application and associated activities -->
    <application
        android:allowBackup="true"
        android:hardwareAccelerated="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:theme="@style/CustomTheme" >

        <!-- Our activities -->
        <activity
            android:name=".ActivityLog"
            android:windowSoftInputMode="adjustPan|stateVisible" >
        </activity>
        <activity
            android:name=".ActivityPreference"
            android:windowSoftInputMode="adjustResize|stateVisible" >
        </activity>
        <activity
            android:name=".ActivitySignin"
            android:windowSoftInputMode="adjustPan|stateVisible" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!-- Signin service -->
        <service
            android:name=".ServiceSignin"
            android:enabled="true"
            android:exported="false"
            android:icon="@drawable/icon"
            android:permission="android.permission.INTERNET" >
        </service>
    </application>
</manifest>

res/xml/preferences.xml

These are our preferences that they user can set (such as the URL to login to as well as the port). This allows you to change the destination server quickly from the UI.

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory 
        android:title="@string/pref_login_title"
        android:key="pref_key_login_settings">
        <EditTextPreference
            android:key="pref_key_login_url"
            android:summary="@string/pref_login_url_summary"
            android:title="@string/pref_login_url"
            android:defaultValue="example.com.au" />
        <EditTextPreference 
            android:key="pref_key_login_port"
            android:summary="@string/pref_login_port_summary"
            android:title="@string/pref_login_port"
            android:defaultValue="4444" />
    </PreferenceCategory>
</PreferenceScreen>

res/layout/activity_login.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:context=".ActivityLog" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/label_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical|center_horizontal"
            android:text="@string/label_username" />

        <EditText
            android:id="@+id/username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="text" >

            <requestFocus />

        </EditText>

        <TextView
            android:id="@+id/label_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical|center_horizontal"
            android:text="@string/label_password" />

        <EditText
            android:id="@+id/password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="textPassword" >
        </EditText>

        <Button
            android:id="@+id/btn_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:background="@drawable/button_info"
            android:paddingBottom="10dp"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:text="@string/btn_login"
            android:textColor="@color/btn_text" />
    </LinearLayout>
</merge>

res/layout/activity_log.xml

<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:context=".ActivityLog" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/label_time"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/label_time" />

        <TimePicker
            android:id="@+id/time"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/label_time" />

        <TextView
            android:id="@+id/label_log"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/label_log" />

        <EditText
            android:id="@+id/log"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="top"
            android:height="150dp"
            android:hint="@string/hint_log"
            android:inputType="textMultiLine"
            android:minHeight="20dp" >

            <requestFocus />
        </EditText>

        <Button
            android:id="@+id/btn_login"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:background="@drawable/button_info"
            android:paddingBottom="10dp"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="10dp"
            android:text="@string/btn_submit"
            android:textColor="@color/btn_text" />
    </LinearLayout>
</merge>

res/menu/log.xml

This is simply describes the context menu for ActivityLog.
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/action_signout"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_signout"/>
</menu>

res/menu/login.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"/>
</menu>

src/au/com/example/ServiceSignin.java

This is where all the good stuff happens.

package au.com.example;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Base64;
import android.util.Log;
import android.widget.TextView;

/**
 * Service to handle Login.
 */
public class ServiceSignin extends Service {
/**
* The Base URL
*/
private static String url = "http://example.com.au";
/**
* The port to access
*/
private static String port = "80";
/**
* The shared preferences object
*/
private SharedPreferences sharedPref;
/**
* Listener for changes to shared preference
*/
    public OnSharedPreferenceChangeListener sharedListener = new OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            Log.i("Service", "A preference has been changed");
            updatePreferences();
        }
    };
/**
* Key to access the Shared Preference value for sub URL
*/
private static final String PREF_URL = "pref_key_login_url";
/**
* Key to access the Shared Preference value for port
*/
private static final String PREF_PORT = "pref_key_login_port";
/**
* Keeps track of all current registered clients.
*/
private ArrayList<Messenger> mClients = new ArrayList<Messenger>();
/**
     * Command to the service to register a client, receiving callbacks
     * from the service.  The Message's replyTo field must be a Messenger of
     * the client where callbacks should be sent.
     */
static final int MSG_REGISTER_CLIENT = 1;
/**
     * Command to the service to unregister a client, to stop receiving callbacks
     * from the service.  The Message's replyTo field must be a Messenger of
     * the client as previously given with MSG_REGISTER_CLIENT.
     */
    static final int MSG_UNREGISTER_CLIENT = 2;
    
    /**
     * Command to the service to request the URL string for the service
     */
    static final int MSG_REQUEST_URL = 3;
    
    /**
     * Command to the service to request the URL string for the service
     */
    static final int MSG_REQUEST_LOGIN = 4;
    
    /**
     * HTTP Client for communicating with server
     */
    public static DefaultHttpClient httpClient;
/**
* Handler of incoming messages from clients.
*/
@SuppressLint("HandlerLeak")
private class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_REGISTER_CLIENT:
            // Add the client to the list
                mClients.add(msg.replyTo);
                break;
            case MSG_UNREGISTER_CLIENT:
            // Remove the client from the list
                mClients.remove(msg.replyTo);
                break;
            case MSG_REQUEST_URL:
            try {
            // Send the Message back to client
            Bundle b = new Bundle();
               b.putString("url", url + ":" + port + "/");
               Message reply = Message.obtain(null, MSG_REQUEST_URL);
               reply.setData(b);
msg.replyTo.send(reply);
} catch (RemoteException e) {
Log.e("Service", "Reply error", e);
}
            case MSG_REQUEST_LOGIN:
            try {
            LoginOperation task = new LoginOperation();
            task.execute(url+":"+port+"/login/auth",msg.getData().getString("username"),msg.getData().getString("password"));
            // Send the Message back to client
               Message reply = Message.obtain(null, MSG_REQUEST_LOGIN);
               int success = 0;
if (task.get()) {
success = 1;
}
               reply.arg1 = success;
msg.replyTo.send(reply);
            } catch (InterruptedException e) {
            Log.e("Service", "Interrupted", e);
} catch (ExecutionException e) {
Log.e("Service", "Execution Exception", e);
} catch (RemoteException e) {
Log.e("Service", "Remote Exception", e);
}
            default:
                super.handleMessage(msg);
            }
        }
    }
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
private final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* For showing and hiding our notification.
*/
private NotificationManager notifyManager;
/**
* Unique Identification Number for the Notification.
* We use it on Notification start, and to cancel it.
*/
private int NOTIFICATION = 10;
/**
* Task for running the login operation
* @see android.os.AsyncTask
*/
private class LoginOperation extends AsyncTask<String, Void, Boolean> {
/**
* Override this method to perform a computation on a background thread.
* @see android.os.AsyncTask#doInBackground(Params... params)
*/
        @Override
        protected Boolean doInBackground(String... login) {
            // We expect exactly three parameters; url, username and password
        if (login.length == 3) {
        try {
        // Our URL to connect to
            URL website = new URL(login[0]);
            Log.i("Service", "URL: "+website.toString());
            // Encode our login details
            String credentials = login[1] + ":" + login[2];
            String base64Encoded = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
            // Create our client connection
            HttpGet getRequest = new HttpGet(website.toString());
            getRequest.addHeader("Authorization", "Basic "+base64Encoded);
            HttpResponse getResponse = httpClient.execute(getRequest);
            if (getResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
            Log.i("Service", "URL Accepted");
            return true;
            } else {
            Log.e("Service", "Not accepted");
            Log.w("Service", "CODE: " + getResponse.getStatusLine().getStatusCode());
            Log.w("Service", "ENCODE: " + base64Encoded);
            Log.w("Service", "CRED: "+credentials);
            Log.w("Service", "URL: "+website.toString());
            }
            } catch (MalformedURLException e) {
Log.e("Service", "Malformed URL", e);
} catch (ClientProtocolException e) {
Log.e("Service", "Client Protocol Exception", e);
e.printStackTrace();
} catch (IOException e) {
Log.e("Service", "IO Exception", e);
}
            }
        return false;
        }

        /**
         * Runs on the UI thread after doInBackground(Params...). The specified result is the value returned by doInBackground(Params...).
         * 
         * @see android.os.AsyncTask#onPostExecute(Result result)
         */
        @Override
        protected void onPostExecute(Boolean result) {
            // might want to change "executed" for the returned string passed
            // into onPostExecute() but that is up to you
        }
    }
/**
* Return the communication channel to the service.
* @see android.app.Service#onBind(Intent)
*/
@Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
/**
* Update our values when the Service starts
* @see android.app.Service#onCreate()
*/
@Override
public void onCreate() {
super.onCreate();
// Update our preference objects and listen for changes
sharedPref = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
sharedPref.registerOnSharedPreferenceChangeListener(sharedListener);
updatePreferences();
// Grab a notification manager
notifyManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// Show notification
showNotification();
// Initialise our HTTP Client server
httpClient = new DefaultHttpClient();
Log.i("Service", "Service Started.");
}
/**
* Called by the system every time a client explicitly starts the service by calling startService(Intent)
* @see android.app.Service#onStartCommand(Intent, int, int)
*/
@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("Service", "Received start id " + startId + ": " + intent);
        return super.onStartCommand(intent, flags, startId);
    }
/**
* Called by the system to notify a Service that it is no longer used and is being removed.
* @see android.app.Service#onDestroy()
*/
@Override
    public void onDestroy() {
        super.onDestroy();
        // Delete any notifications
        notifyManager.cancel(NOTIFICATION);
        Log.i("Service", "Service Stopped.");
    }
/**
* Reads the Shared preferences and updates our internal values
*/
public void updatePreferences() {
url = sharedPref.getString(PREF_URL, "error");
port = sharedPref.getString(PREF_PORT, "error");
Log.i("Service", "Preferences updated");
}
/**
     * Show a notification while this service is running
     */
    private void showNotification() {
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.btn_submit);

        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, ActivityLog.class), 0);
        
        // Set the icon, scrolling text and timestamp
        Notification notification = new NotificationCompat.Builder(this)
        .setSmallIcon(R.drawable.icon)
        .setContentText(text)
        .setContentTitle("Service")
        .setContentIntent(contentIntent)
        .build();

        // Send the notification.
        // We use a string id because it is a unique number.  We use it later to cancel.
        notifyManager.notify(NOTIFICATION, notification);
    }
}

 src/au/com/example/ActivitySignin.java

This is handles the Login screen.

package au.com.example;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

/**
 * Handles the User Interface for entering and submitting log entries
 * 
 * @author nassar
 */
public class ActivitySignin extends Activity {
/**
     * The username input
     */
    private EditText inputUsername;
    
    /**
     * The password input
     */
    private EditText inputPassword;
    
    /**
     * The Login button
     */
    private Button buttonLogin;
    
    /**
     * Messenger for communicating with service
     */
    private Messenger messageService = null;
    
    /**
     * Flag indicating whether we have called bind on the service.
     */
    private boolean stateIsBound;
    
    /**
     * Controls what happens once the submit button is clicked
     */
    private OnClickListener btnLoginListener = new OnClickListener() {
        public void onClick(View v){
            Log.i("ActivitySignin", "btnLogin has been clicked");
            if (stateIsBound) {
                if (messageService != null) {
                    try {
                    // Send the Message back to client
                Bundle b = new Bundle();
                   b.putString("username", inputUsername.getText().toString());
                   b.putString("password", inputPassword.getText().toString());
                   Message msg = Message.obtain(null, ServiceSignin.MSG_REQUEST_LOGIN, 0, 0);
                   msg.setData(b);
                        msg.replyTo = mMessenger;
                        messageService.send(msg);
                    } catch (RemoteException e) {
                    }
                }
            }
        }
    };
    
    /**
     * Handler of incoming messages from service.
     */
    @SuppressLint("HandlerLeak")
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
           case ServiceSignin.MSG_REQUEST_LOGIN:
            if (msg.arg1 == 1) {
            Toast.makeText(ActivitySignin.this, "Signed in", Toast.LENGTH_LONG).show();
            Intent intent = new Intent(ActivitySignin.this, ActivityLog.class);
                   startActivity(intent);
            } else {
            Toast.makeText(ActivitySignin.this, "Not signed in", Toast.LENGTH_LONG).show();
            }
               break;
           default:
               super.handleMessage(msg);
            }
        }
    };
    
    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private final Messenger mMessenger = new Messenger(new IncomingHandler());
    
    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection serviceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
        // Get a client-side representation of that from the raw service object.
            messageService = new Messenger(service);
            try {
                Message msg = Message.obtain(null, ServiceSignin.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                messageService.send(msg);
                Log.i("ActivitySignin", "Service connected");
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even do anything with it
            Log.e("ActivitySignin", "Service crashed before we could do anything with it", e);
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
            messageService = null;
            Log.i("ActivitySignin", "Service has been unexpectedly disconnected - process crashed");
        }
    };
    
    /**
     * Establish a connection with the service.  We use an explicit
     * class name because there is no reason to be able to let other
     * applications replace our component.
     */
    private void doBindService() {
        bindService(new Intent(ActivitySignin.this, ServiceSignin.class), serviceConnection, Context.BIND_AUTO_CREATE);
        stateIsBound = true;
    }
    
    /**
     * Destroy a connection with the service
     */
    private void doUnbindService() {
        if (stateIsBound) {
            // If we have received the service, and hence registered with it, then now is the time to unregister.
            if (messageService != null) {
                try {
                    Message msg = Message.obtain(null, ServiceSignin.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    messageService.send(msg);
                } catch (RemoteException e) {
                    Log.e("ActivitySignin", "Service has been unexpectedly disconnected - process crashed - doUnbindService()", e);
                }
            }
            // Detach our existing connection.
            unbindService(serviceConnection);
            stateIsBound = false;
        }
    }
    
    /**
     * Called after onCreate(Bundle) — or after onRestart() when the activity had been stopped, but is now again being displayed to the user.
     * It will be followed by onResume()
     * 
     * @see android.app.Activity#onStart()
     */
    @Override
    protected void onStart() {
    super.onStart();
        // Start our service (or bind to running service)
    if (!stateIsBound) {
    doBindService();
    Log.i("ActivitySignin", "Starting activity");
    } else {
    Log.i("ActivitySignin", "Already bound");
    }
    }
    
    /**
     * Called when the activity is starting.
     * 
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Perform the parent operations
        super.onCreate(savedInstanceState);
        
        // Set the layout to display
        setContentView(R.layout.activity_login);
        
        // Set up our inputs from the Activity
        inputUsername = (EditText)findViewById(R.id.username);
        inputPassword = (EditText)findViewById(R.id.password);
        
        // Our Submit button
        buttonLogin = (Button)findViewById(R.id.btn_login);
        
        // Put a listener on the button to capture our input
        buttonLogin.setOnClickListener(btnLoginListener);
    }
    
    /**
     * Initialise the contents of the Activity's options menu.
     * To update the menu every time it is displayed, see onPrepareOptionsMenu(Menu).
     * 
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu items for use in the action bar
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.login, menu);
        return super.onCreateOptionsMenu(menu);
    }
    
    /**
     * This hook is called whenever an item in your options menu is selected.
     * Override default implementation so we can implement activity specific functions
     * (Default implementation is called if the menu item is not handled here.
     * 
     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:;
                Intent intent = new Intent(this, ActivityPreference.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
    
    /**
     * Perform any final cleanup before an activity is destroyed.
     * @see android.app.Activity#onDestroy()
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            doUnbindService();
        } catch (Throwable t) {
            Log.e("ActivitySignin", "Failed to unbind from the service", t);
        }
    }
}

src/au/com/example/ActivityLog.java

Just a dummy screen that really does nothing.

package au.com.example;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TimePicker;
/**
 * Handles the User Interface for entering and submitting log entries
 *
 * @author nassar
 */
public class ActivityLog extends Activity {
/**
     * The time input
     */
    private TimePicker inputTime;
 
    /**
     * The log input
     */
    private EditText inputLog;
 
    /**
     * The Submit button
     */
    private Button buttonSubmit;
 
    /**
     * Messenger for communicating with service
     */
    private Messenger messageService = null;
 
    /**
     * Flag indicating whether we have called bind on the service.
     */
    private boolean stateIsBound;
 
    /**
     * Controls what happens once the submit button is clicked
     */
    private OnClickListener btnSubmitListener = new OnClickListener() {
        public void onClick(View v){
            Log.i("ActivityLog", "btnSubmit has been clicked");
            if (stateIsBound) {
                if (messageService != null) {
                    try {
                        Message msg = Message.obtain(null, ServiceSignin.MSG_REQUEST_URL, 0, 0);
                        msg.replyTo = mMessenger;
                        messageService.send(msg);
                    } catch (RemoteException e) {
                    }
                }
            }
        }
    };
 
    /**
     * Handler of incoming messages from service.
     */
    @SuppressLint("HandlerLeak")
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
           case ServiceSignin.MSG_REQUEST_URL:
               String str1 = msg.getData().getString("url");
               Log.i("ActivityLog", "URL: " + str1);
               break;
           default:
               super.handleMessage(msg);
            }
        }
    };
 
    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private final Messenger mMessenger = new Messenger(new IncomingHandler());
 
    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection serviceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
        // Get a client-side representation of that from the raw service object.
            messageService = new Messenger(service);
            try {
                Message msg = Message.obtain(null, ServiceSignin.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                messageService.send(msg);
                Log.i("ActivityLog", "Service connected");
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even do anything with it
            Log.e("ActivityLog", "Service crashed before we could do anything with it", e);
            }
        }
        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
            messageService = null;
            Log.i("ActivityLog", "Service has been unexpectedly disconnected - process crashed");
        }
    };
 
    /**
     * Establish a connection with the service.  We use an explicit
     * class name because there is no reason to be able to let other
     * applications replace our component.
     */
    private void doBindService() {
        bindService(new Intent(ActivityLog.this, ServiceSignin.class), serviceConnection, Context.BIND_AUTO_CREATE);
        stateIsBound = true;
    }
 
    /**
     * Destroy a connection with the service
     */
    private void doUnbindService() {
        if (stateIsBound) {
            // If we have received the service, and hence registered with it, then now is the time to unregister.
            if (messageService != null) {
                try {
                    Message msg = Message.obtain(null, ServiceSignin.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    messageService.send(msg);
                } catch (RemoteException e) {
                    Log.e("ActivityLog", "Service has been unexpectedly disconnected - process crashed - doUnbindService()", e);
                }
            }
            // Detach our existing connection.
            unbindService(serviceConnection);
            stateIsBound = false;
        }
    }
 
    /**
     * Called after onCreate(Bundle) — or after onRestart() when the activity had been stopped, but is now again being displayed to the user.
     * It will be followed by onResume()
     *
     * @see android.app.Activity#onStart()
     */
    @Override
    protected void onStart() {
    super.onStart();
        // Start our service (or bind to running service)
    if (!stateIsBound) {
    doBindService();
    Log.i("ActivityLog", "Starting activity");
    } else {
    Log.i("ActivityLog", "Already bound");
    }
    }
 
    /**
     * Called when the activity is starting.
     *
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Perform the parent operations
        super.onCreate(savedInstanceState);
     
        // Set the layout to display
        setContentView(R.layout.activity_log);
     
        // Set up our inputs from the Activity
        inputTime = (TimePicker)findViewById(R.id.time);
        inputLog = (EditText)findViewById(R.id.log);
     
        // Our Submit button
        buttonSubmit = (Button)findViewById(R.id.btn_login);
     
        // Put a listener on the button to capture our input
        buttonSubmit.setOnClickListener(btnSubmitListener);
    }
 
    /**
     * Initialise the contents of the Activity's options menu.
     * To update the menu every time it is displayed, see onPrepareOptionsMenu(Menu).
     *
     * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu items for use in the action bar
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.log, menu);
        return super.onCreateOptionsMenu(menu);
    }
 
    /**
     * This hook is called whenever an item in your options menu is selected.
     * Override default implementation so we can implement activity specific functions
     * (Default implementation is called if the menu item is not handled here.
     *
     * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem)
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_signout:;
                Intent intent = new Intent(this, ActivitySignin.class);
                startActivity(intent);
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
 
    /**
     * Perform any final cleanup before an activity is destroyed.
     * @see android.app.Activity#onDestroy()
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        try {
            doUnbindService();
        } catch (Throwable t) {
            Log.e("ActivityLog", "Failed to unbind from the service", t);
        }
    }
}

src/au/com/example/ActivityPreference.java

This is a simple screen to show and handle changes to our Shared Preferences.

package au.com.example;
import android.annotation.TargetApi;import android.os.Build;import android.os.Bundle;import android.preference.PreferenceActivity;import android.preference.PreferenceFragment;
public class ActivityPreference extends PreferenceActivity {    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);                // Android v11 up uses Fragments, but lower needs Activity        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {            onCreatePreferenceActivity();        } else {            onCreatePreferenceFragment();        }    }        /**     * Wraps legacy {@link #onCreate(Bundle)} code for Android < 3 (i.e. API lvl < 11).     */    @SuppressWarnings("deprecation")    private void onCreatePreferenceActivity() {        addPreferencesFromResource(R.xml.preferences);    }        /**     * Wraps {@link #onCreate(Bundle)} code for Android >= 3 (i.e. API lvl >= 11).     */    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    private void onCreatePreferenceFragment() {        getFragmentManager().beginTransaction()                .replace(android.R.id.content, new MyPreferenceFragment ())                .commit();    }        /**     * Preference Fragment for API lvl >= 11     */    @TargetApi(Build.VERSION_CODES.HONEYCOMB)    public static class MyPreferenceFragment extends PreferenceFragment    {        @Override        public void onCreate(final Bundle savedInstanceState)        {            super.onCreate(savedInstanceState);            addPreferencesFromResource(R.xml.preferences);        }    }}

Resources

No comments:

Post a Comment

Thanks for contributing!! Try to keep on topic and please avoid flame wars!!