/* * Copyright (C) 2008 Josh Guilfoyle <jasta@devtcg.org> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ package org.devtcg.five.util; import org.devtcg.five.R; import android.app.Activity; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.os.IInterface; import android.util.Log; import android.view.View; import android.view.ViewGroup; /** * Utility class to create activities which critically depend on a service * connection. */ public abstract class ServiceActivity<T extends IInterface> extends Activity { public static final String TAG = "ServiceActivity"; /** * When connected, references the service interface as a convenience to * subclasses. */ protected T mService = null; /** * Convenience feature to allow subclasses to defer UI * construction/assignment until the service is connected, and then manage * hiding/showing the UI to match the service state. * * @see onInitUI */ private boolean mShowingUI = false; @Override protected void onStart() { super.onStart(); Log.d(TAG, "onStart(): Binding service..."); if (bindService() == false) onServiceFatal(); } @Override protected void onStop() { Log.d(TAG, "onStop(): Unbinding service..."); if (mService != null) onDetached(); showUI(false); unbindService(); mService = null; super.onStop(); } protected boolean isShown() { return mShowingUI; } protected void showUI(boolean display) { if (mShowingUI == false) { if (display == true) { onInitUI(); mShowingUI = true; } } else { /* XXX: There must be a less intrusive way to figure out what * the top-level content view is? */ View v = ((ViewGroup)getWindow().getDecorView()).getChildAt(0); v.setVisibility(display ? View.VISIBLE : View.GONE); } } /** * Called when the activity needs to display the UI for the first * time. Initialization is similar to {@link Activity#onCreate}. */ protected abstract void onInitUI(); /** * Called when the activity is attached to the service, and not necessarily * the first time it connects. You should register listeners and fit * the UI to the service state here. */ protected abstract void onAttached(); /** * Called when the activity is detached from the service. You should * unregister listeners and tidy up internal state accordingly. */ protected abstract void onDetached(); protected abstract Intent getServiceIntent(); protected abstract T getServiceInterface(IBinder service); private boolean bindService() { Intent i = getServiceIntent(); /* Start the service first to ensure that it survives after our * activity unbinds (if it wants to). */ ComponentName name = startService(i); if (name == null) return false; boolean bound = bindService(new Intent().setComponent(name), mConnection, BIND_AUTO_CREATE); return bound; } private void unbindService() { unbindService(mConnection); } private final ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { mService = getServiceInterface(service); showUI(true); onAttached(); } public void onServiceDisconnected(ComponentName name) { onServiceFatal(); mService = null; } }; /** * Fatal error attempting to either start or bind to the service specified * by {@link getServiceIntent}. Will not retry. Default implementation is * to throw up an error and finish(). */ protected void onServiceFatal() { Log.e(TAG, "Unable to start service: " + getServiceIntent()); (new AlertDialog.Builder(this)) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle("Sorry!") .setMessage(R.string.app_error_msg) .create().show(); finish(); } }