Android Open Source - android-autostarts Toggle Service






From Project

Back to project page android-autostarts.

License

The source code is released under:

GNU General Public License

If you think the Android project android-autostarts listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.elsdoerfer.android.autostarts;
/* w ww  . j a v  a 2s. com*/
import android.app.Service;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import com.elsdoerfer.android.autostarts.db.ComponentInfo;
import com.stericson.RootTools.RootTools;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.LinkedBlockingQueue;


/**
 * Processes a queue of "change component state" requests.
 *
 * This allows the user to make multiple changes without
 * waiting for the previous one to complete.
 *
 * This is not using an IntentService, because we need both
 * information about the progress, as well as information
 * about which components are currently queued (since this
 * all needs to be reflected in the UI).
 */
public class ToggleService extends Service {

  static interface ToggleServiceListener {
    /**
     * Called whenever the component that the service is currently
     * processing changes. If the service is done processing the
     * whole queue, this will be called with null.
     */
    void onActivityChange(ComponentInfo component);

    /**
     * Called whenever this service's queue changes (new component added,
     * or finished processing the current one).
     */
    void onQueueModified(ComponentInfo component, boolean isAdded);
  }

  /**
   * The job queue this service processes. We use to separate data
   * structures for this. The queue holds the order. The target states
   * (enabled/disabled) are stored in a dictionary. So if the user
   * changes his request while an item is waiting in the queue, we
   * can change it directly, instead of processing it twice. In addition,
   * we need this so we can read the list of components that we are
   * processing, and render them in the UI accordingly.
   */
  protected HashMap<ComponentInfo, Boolean> mStates;
  protected LinkedBlockingQueue<ComponentInfo> mQueue;

  private ToggleServiceListener mListener;
  // Because Service has no runOnUIThread or equivalent.
  private Handler mHandler;

  private ComponentInfo mItemBeingProcessed = null;
  private boolean mItemBeingProcessedDesiredState;

  @Override
  public void onCreate() {
    super.onCreate();
    mStates = new LinkedHashMap<ComponentInfo, Boolean>();
    mQueue = new LinkedBlockingQueue<ComponentInfo>();
    mHandler = new Handler();
  }

  // To support API Levels <= 4 (pre 2.0).
  @Override
  public void onStart(Intent intent, int startId) {
    handleStart(intent, startId);
  }

  /**
   * Clients should start this service with an intent to submit jobs.
   */
  @Override
  synchronized public int onStartCommand(Intent intent, int flags, int startId) {
    handleStart(intent, startId);

    // TODO: While we would want the service to be sticky, this makes no sense so
    //   long we don't actually persist our change queue somewhere. So we should
    //   do that, and then change the mode here.
    return START_REDELIVER_INTENT;
  }

  void handleStart(Intent intent, int startId) {
    // Add the new job to the queue, or change the desired state
    // of the job currently in the queue.
    ComponentInfo component = intent.getParcelableExtra("component");
    boolean newState = intent.getBooleanExtra("state", false);
    if (mStates.put(component,  newState) == null);
    mQueue.offer(component);

    Log.d(Utils.TAG, "Added "+component+" to service queue, now size: "+mStates.size());
    onQueueModified(component, false);

    // Make sure the queue is running (this should do nothing if it already is).
    processNextItem();
  }

  /**
   * Process the next item in the queue.
   *
   * Shutdown the thread when done.
   */
  synchronized void processNextItem() {
    // Allow only one item to be processed at a time.
    // TODO: Might it not be sensible/beneficial to do multiple changes at a time?
    if (mItemBeingProcessed != null)
      return;

    // Use queue to determine next item to process
    final ComponentInfo component = mQueue.poll();
    if (component == null) {
      onActivityChange(null);
      Log.d(Utils.TAG, "ToggleService mQueue empty, shutting down");
      stopSelf();
      Log.d(Utils.TAG, "Closing all root shells");
      try {
        RootTools.closeAllShells();
      } catch (IOException e) {
        e.printStackTrace();
      }
      return;
    }

    // See whether we should enable or disable
    final boolean desiredState = mStates.remove(component);

    Log.d(Utils.TAG, "Processing "+component+", remaining items in queue: "+mStates.size());
    onActivityChange(component);

    new Thread(new Runnable() {
      public void run() {
        mItemBeingProcessed = component;
        mItemBeingProcessedDesiredState = desiredState;
        final boolean success =
            ToggleTool.toggleState(ToggleService.this, component, desiredState);

        mItemBeingProcessed = null;
        mHandler.post(new Runnable() {
          @Override
          public void run() {
            if (!success) {
              // Now that we do state toggling in the background in the
              // service, error handling poses new challenges. For now,
              // just show a toast. However, I never liked toasts, of course.
              // Options include notifications, as well as additional clever
              // integration with an activity that might be in the foreground.
              Resources res = getResources();
              Toast toast = Toast.makeText(
                ToggleService.this,
                String.format(res.getString(R.string.state_change_failed),
                  // TODO: Instead of the component name, it would be
                  // better to refer to the event the user wanted to
                  // to remove the component from. Unfortunately, we do
                  // not even carry this information along.
                  component.getLabel(), component.componentName),
                5000);
              toast.show();
            }

            Log.d(Utils.TAG, "Processing "+component+" done");
            onQueueModified(component, false);
            processNextItem();
          }
        });
      }
    }).start();
  }

  // API for a client to use once it has bound to a service instance.

  public void setHandler(ToggleServiceListener handler) {
    mListener = handler;
  }

  /**
   * True if the component is being processed by this service, or waiting to be.
   */
  public boolean has(ComponentInfo component) {
    return (component.equals(mItemBeingProcessed) || mStates.containsKey(component));
  }

  /**
   * If the given component is in the queue, returns the state (True for enabled,
   * False for disabled) that the component should be switched to.
   *
   * Returns the default value if the component is not in the queue.
   */
  public boolean getQueuedState(ComponentInfo component, boolean defaultValue) {
    if (component.equals(mItemBeingProcessed))
      return mItemBeingProcessedDesiredState;

    Boolean desiredState = mStates.get(component);
    if (desiredState == null)
      return defaultValue;

    return desiredState;
  }

  /**
   * Once bound, the client can request that we trigger our events once to send
   * it an initial status update.
   */
  public void requestUpdate() {
    onActivityChange(mItemBeingProcessed);
  }

  // Helpers to trigger callbacks.

  private void onActivityChange(ComponentInfo component) {
    if (mListener != null)
      mListener.onActivityChange(component);
  }

  private void onQueueModified(ComponentInfo component, boolean wasAdded) {
    if (mListener != null)
      mListener.onQueueModified(component, wasAdded);
  }

  // The boilerplate to allow access to the service instance from outside

  public class LocalBinder extends Binder {
    ToggleService getService() {
      return ToggleService.this;
    }
  }

  private final IBinder mBinder = new LocalBinder();

  public IBinder onBind(Intent intent) {
    return mBinder;
  }
}




Java Source Code List

com.elsdoerfer.android.autostarts.Actions.java
com.elsdoerfer.android.autostarts.DatabaseHelper.java
com.elsdoerfer.android.autostarts.EventDetailsFragment.java
com.elsdoerfer.android.autostarts.HelpActivity.java
com.elsdoerfer.android.autostarts.ListActivity.java
com.elsdoerfer.android.autostarts.LoadTask.java
com.elsdoerfer.android.autostarts.MyExpandableListAdapter.java
com.elsdoerfer.android.autostarts.ReceiverReader.java
com.elsdoerfer.android.autostarts.ToggleService.java
com.elsdoerfer.android.autostarts.ToggleTool.java
com.elsdoerfer.android.autostarts.Utils.java
com.elsdoerfer.android.autostarts.compat.FixedExpandableListView.java
com.elsdoerfer.android.autostarts.db.ComponentInfo.java
com.elsdoerfer.android.autostarts.db.IntentFilterInfo.java
com.elsdoerfer.android.autostarts.db.PackageInfo.java
com.stericson.RootTools.Constants.java
com.stericson.RootTools.RootTools.java
com.stericson.RootTools.containers.Mount.java
com.stericson.RootTools.containers.Permissions.java
com.stericson.RootTools.containers.RootClass.java
com.stericson.RootTools.containers.Symlink.java
com.stericson.RootTools.exceptions.RootDeniedException.java
com.stericson.RootTools.execution.CommandCapture.java
com.stericson.RootTools.execution.Command.java
com.stericson.RootTools.execution.JavaCommandCapture.java
com.stericson.RootTools.execution.Shell.java
com.stericson.RootTools.internal.Installer.java
com.stericson.RootTools.internal.InternalVariables.java
com.stericson.RootTools.internal.Remounter.java
com.stericson.RootTools.internal.RootToolsInternalMethods.java
com.stericson.RootTools.internal.Runner.java
src.com.elsdoerfer.android.autostarts.opt.MarketUtils.java
src.com.elsdoerfer.android.autostarts.opt.MarketUtils.java
src.com.elsdoerfer.android.autostarts.opt.RootFeatures.java
src.com.elsdoerfer.android.autostarts.opt.RootFeatures.java