New UI for viewing and stopping currently running services.
Change-Id: I86012262635c911be23513aa0b027174b490374d
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9f6ac0e..d4f9381 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -279,6 +279,18 @@
</intent-filter>
</activity>
+ <activity android:name="RunningServices"
+ android:label="@string/runningservices_settings_title"
+ android:clearTaskOnLaunch="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.MONKEY" />
+ <category android:name="android.intent.category.VOICE_LAUNCH" />
+ <category android:name="com.android.settings.SHORTCUT" />
+ </intent-filter>
+ </activity>
+
<activity android:name="SecuritySettings" android:label="@string/security_settings_title"
android:configChanges="orientation|keyboardHidden"
android:clearTaskOnLaunch="true"
diff --git a/res/layout/running_services.xml b/res/layout/running_services.xml
new file mode 100644
index 0000000..8fe7f9f
--- /dev/null
+++ b/res/layout/running_services.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <ListView android:id="@android:id/list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:drawSelectorOnTop="false"
+ android:fastScrollEnabled="true" />
+ <TextView android:id="@android:id/empty"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center"
+ android:text="@string/no_running_services"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+</merge>
diff --git a/res/layout/running_services_item.xml b/res/layout/running_services_item.xml
new file mode 100644
index 0000000..6748a1a
--- /dev/null
+++ b/res/layout/running_services_item.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:orientation="vertical"
+ android:gravity="fill" >
+
+ <ImageView android:id="@+id/separator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="?android:attr/listDivider"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:paddingRight="6dip"
+ android:paddingLeft="6dip"
+ android:gravity="center_vertical" >
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@android:dimen/app_icon_size"
+ android:layout_height="@android:dimen/app_icon_size"
+ android:layout_marginLeft="5dip"
+ android:layout_marginRight="11dip"
+ android:layout_gravity="center_vertical"
+ android:scaleType="fitCenter"/>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+ <TextView android:id="@+id/name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="bold"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:layout_marginBottom="2dip" />
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+ <TextView android:id="@+id/run_time"
+ android:layout_marginTop="-4dip"
+ android:layout_gravity="center_vertical|left"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingRight="4dip"
+ android:singleLine="true"
+ android:ellipsize="marquee"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ <TextView android:id="@+id/size"
+ android:layout_marginTop="-4dip"
+ android:layout_gravity="center_vertical|right"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+ </LinearLayout>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e1a9bcd..2af9b18 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1403,6 +1403,25 @@
<!-- Manage applications, version string displayed in app snippet -->
<string name="version_text">version <xliff:g id="version_num">%1$s</xliff:g></string>
+ <!-- Services settings screen, setting option name for the user to go to the screen to view running services -->
+ <string name="runningservices_settings_title">Running services</string>
+ <!-- Services settings screen, setting option summary for the user to go to the screen to view running services -->
+ <string name="runningservices_settings_summary">View and control currently running services</string>
+ <!-- Label for a service item when it is restarting -->
+ <string name="service_restarting">Restarting</string>
+ <!-- Running services, body text when there are no services to show -->
+ <string name="no_running_services">No running services</string>
+ <!-- Running services, title of dialog to stop a service -->
+ <string name="confirm_stop_service">Stop service?</string>
+ <!-- Running services, message of dialog to stop a service -->
+ <string name="confirm_stop_service_msg">The service will no longer run until
+ started again by its application. This may have undesireable
+ consequences.</string>
+ <!-- Running services, button to stop a service -->
+ <string name="confirm_stop_stop">Stop</string>
+ <!-- Running services, button to cancel stopping of a service -->
+ <string name="confirm_stop_cancel">Cancel</string>
+
<!-- Language Settings --> <skip />
<!-- Title of setting on main settings screen. This item will take the user to the screen to tweak settings realted to locale and text -->
<string name="language_settings">Language & keyboard</string>
diff --git a/res/xml/application_settings.xml b/res/xml/application_settings.xml
index 8d0a7cb..b5418e8 100644
--- a/res/xml/application_settings.xml
+++ b/res/xml/application_settings.xml
@@ -43,6 +43,14 @@
android:targetClass="com.android.settings.ManageApplications" />
</PreferenceScreen>
+ <PreferenceScreen
+ android:title="@string/runningservices_settings_title"
+ android:summary="@string/runningservices_settings_summary">
+ <intent android:action="android.intent.action.MAIN"
+ android:targetPackage="com.android.settings"
+ android:targetClass="com.android.settings.RunningServices" />
+ </PreferenceScreen>
+
<PreferenceScreen
android:title="@string/development_settings_title"
android:summary="@string/development_settings_summary">
diff --git a/src/com/android/settings/RunningServices.java b/src/com/android/settings/RunningServices.java
new file mode 100644
index 0000000..3340a4b
--- /dev/null
+++ b/src/com/android/settings/RunningServices.java
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings;
+
+import com.android.settings.R;
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.ListActivity;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageStats;
+import android.content.pm.ServiceInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.text.format.DateUtils;
+import android.text.format.Formatter;
+import android.util.AttributeSet;
+import android.util.Config;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+public class RunningServices extends ListActivity
+ implements AbsListView.RecyclerListener,
+ DialogInterface.OnClickListener {
+ static final String TAG = "RunningServices";
+
+ /** Maximum number of services to retrieve */
+ static final int MAX_SERVICES = 100;
+
+ static final int MSG_UPDATE_TIMES = 1;
+ static final int MSG_UPDATE_CONTENTS = 2;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
+
+ ActivityManager mAm;
+
+ State mState;
+
+ StringBuilder mBuilder = new StringBuilder(128);
+
+ BaseItem mCurSelected;
+
+ int mProcessBgColor;
+
+ Dialog mCurDialog;
+
+ class ActiveItem {
+ View mRootView;
+ BaseItem mItem;
+ ActivityManager.RunningServiceInfo mService;
+ ViewHolder mHolder;
+ long mFirstRunTime;
+
+ void updateTime(Context context) {
+ if (mItem.mActiveSince >= 0) {
+ mHolder.size.setText(DateUtils.formatElapsedTime(mBuilder,
+ (SystemClock.uptimeMillis()-mFirstRunTime)/1000));
+ } else {
+ mHolder.size.setText(context.getResources().getText(
+ R.string.service_restarting));
+ }
+ }
+ }
+
+ static class BaseItem {
+ boolean mIsProcess;
+ PackageItemInfo mPackageInfo;
+ CharSequence mDisplayLabel;
+ String mLabel;
+ String mName;
+
+ int mCurSeq;
+
+ long mActiveSince;
+ long mSize;
+ String mSizeStr;
+ boolean mNeedDivider;
+ }
+
+ static class ServiceItem extends BaseItem {
+ ActivityManager.RunningServiceInfo mRunningService;
+ ServiceInfo mServiceInfo;
+ }
+
+ static class ProcessItem extends BaseItem {
+ final HashMap<ComponentName, ServiceItem> mServices
+ = new HashMap<ComponentName, ServiceItem>();
+ int mUid;
+ int mPid;
+
+ boolean updateService(PackageManager pm,
+ ActivityManager.RunningServiceInfo service) {
+ boolean changed = false;
+ ServiceItem si = mServices.get(service.service);
+ if (si == null) {
+ changed = true;
+ si = new ServiceItem();
+ si.mRunningService = service;
+ try {
+ si.mServiceInfo = pm.getServiceInfo(service.service, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ if (si.mServiceInfo != null && (si.mServiceInfo.labelRes != 0
+ || si.mServiceInfo.nonLocalizedLabel != null)) {
+ si.mDisplayLabel = si.mServiceInfo.loadLabel(pm);
+ si.mLabel = si.mDisplayLabel.toString();
+ } else {
+ si.mLabel = si.mRunningService.service.getClassName();
+ int tail = si.mLabel.lastIndexOf('.');
+ if (tail >= 0) {
+ si.mLabel = si.mLabel.substring(tail+1, si.mLabel.length());
+ }
+ si.mDisplayLabel = si.mLabel;
+ }
+ si.mPackageInfo = si.mServiceInfo;
+ mServices.put(service.service, si);
+ }
+ si.mCurSeq = mCurSeq;
+ si.mRunningService = service;
+ long activeSince = service.restarting == 0 ? service.activeSince : -1;
+ if (si.mActiveSince != activeSince) {
+ si.mActiveSince = activeSince;
+ changed = true;
+ }
+
+ return changed;
+ }
+ }
+
+ static class State {
+ final SparseArray<HashMap<String, ProcessItem>> mProcesses
+ = new SparseArray<HashMap<String, ProcessItem>>();
+
+ final ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
+
+ int mSequence = 0;
+
+ boolean update(Context context, ActivityManager am) {
+ final PackageManager pm = context.getPackageManager();
+
+ mSequence++;
+
+ boolean changed = false;
+
+ List<ActivityManager.RunningServiceInfo> services
+ = am.getRunningServices(MAX_SERVICES);
+ if (services == null) {
+ return false;
+ }
+ final int NS = services.size();
+ for (int i=0; i<NS; i++) {
+ ActivityManager.RunningServiceInfo si = services.get(i);
+ // We are not interested in non-started services, because
+ // there is nothing the user can do about them.
+ if (!si.started) {
+ continue;
+ }
+ // We likewise don't care about services running in a
+ // persistent process like the system or phone.
+ if ((si.flags&ActivityManager.RunningServiceInfo.FLAG_PERSISTENT_PROCESS)
+ != 0) {
+ continue;
+ }
+
+ HashMap<String, ProcessItem> procs = mProcesses.get(si.uid);
+ if (procs == null) {
+ procs = new HashMap<String, ProcessItem>();
+ mProcesses.put(si.uid, procs);
+ }
+ ProcessItem proc = procs.get(si.process);
+ if (proc == null) {
+ changed = true;
+ proc = new ProcessItem();
+ proc.mIsProcess = true;
+ proc.mName = si.process;
+ proc.mUid = si.uid;
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(si.process, 0);
+ if (ai.uid == si.uid) {
+ proc.mDisplayLabel = ai.loadLabel(context.getPackageManager());
+ proc.mLabel = proc.mDisplayLabel.toString();
+ proc.mPackageInfo = ai;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ procs.put(si.process, proc);
+ }
+
+ if (proc.mCurSeq != mSequence) {
+ int pid = si.restarting == 0 ? si.pid : 0;
+ if (pid != proc.mPid) {
+ changed = true;
+ proc.mPid = pid;
+ }
+ proc.mSize = 0;
+ if (proc.mPid != 0) {
+ Debug.MemoryInfo mi = new Debug.MemoryInfo();
+ // XXX This is a hack... I really don't want to be
+ // doing a synchronous call into the app, but can't
+ // figure out any other way to get the pss.
+ try {
+ ActivityManagerNative.getDefault().getProcessMemoryInfo(
+ proc.mPid, mi);
+ proc.mSize = (mi.dalvikPss + mi.nativePss
+ + mi.otherPss) * 1024;
+ String sizeStr = Formatter.formatFileSize(
+ context, proc.mSize);
+ if (!sizeStr.equals(proc.mSizeStr)){
+ changed = true;
+ proc.mSizeStr = sizeStr;
+ }
+ } catch (RemoteException e) {
+ }
+ }
+ proc.mCurSeq = mSequence;
+ }
+ changed |= proc.updateService(context.getPackageManager(), si);
+
+ if (proc.mLabel == null) {
+ // If we couldn't get information about the overall
+ // process, try to find something about the uid.
+ String[] pkgs = pm.getPackagesForUid(proc.mUid);
+ for (String name : pkgs) {
+ try {
+ PackageInfo pi = pm.getPackageInfo(name, 0);
+ if (pi.sharedUserLabel != 0) {
+ CharSequence nm = pm.getText(name,
+ pi.sharedUserLabel, pi.applicationInfo);
+ if (nm != null) {
+ proc.mDisplayLabel = nm;
+ proc.mLabel = nm.toString();
+ proc.mPackageInfo = pi.applicationInfo;
+ break;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ }
+
+ // If still don't have anything to display, just use the
+ // service info.
+ if (proc.mLabel == null) {
+ proc.mPackageInfo = proc.mServices.get(si.service)
+ .mServiceInfo.applicationInfo;
+ proc.mDisplayLabel = proc.mPackageInfo.loadLabel(pm);
+ proc.mLabel = proc.mDisplayLabel.toString();
+ }
+ }
+ }
+
+ // Look for anything that no longer exists...
+ for (int i=0; i<mProcesses.size(); i++) {
+ HashMap<String, ProcessItem> procs = mProcesses.valueAt(i);
+ Iterator<ProcessItem> pit = procs.values().iterator();
+ while (pit.hasNext()) {
+ ProcessItem pi = pit.next();
+ if (pi.mCurSeq != mSequence) {
+ changed = true;
+ pit.remove();
+ if (procs.size() == 0) {
+ mProcesses.remove(mProcesses.keyAt(i));
+ }
+ continue;
+ }
+ Iterator<ServiceItem> sit = pi.mServices.values().iterator();
+ while (sit.hasNext()) {
+ ServiceItem si = sit.next();
+ if (si.mCurSeq != mSequence) {
+ changed = true;
+ sit.remove();
+ }
+ }
+ }
+ }
+
+ if (changed) {
+ mItems.clear();
+ for (int i=0; i<mProcesses.size(); i++) {
+ for (ProcessItem pi : mProcesses.valueAt(i).values()) {
+ pi.mNeedDivider = false;
+ mItems.add(pi);
+ boolean needDivider = false;
+ for (ServiceItem si : pi.mServices.values()) {
+ si.mNeedDivider = needDivider;
+ needDivider = true;
+ mItems.add(si);
+ }
+ }
+ }
+ }
+
+ return changed;
+ }
+ }
+
+ static class TimeTicker extends TextView {
+ public TimeTicker(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+ }
+
+ static class ViewHolder {
+ ImageView separator;
+ ImageView icon;
+ TextView name;
+ TextView runTime;
+ TextView size;
+ }
+
+ class ServiceListAdapter extends BaseAdapter {
+ final State mState;
+ final LayoutInflater mInflater;
+
+ ServiceListAdapter(State state) {
+ mState = state;
+ mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ public int getCount() {
+ return mState.mItems.size();
+ }
+
+ public Object getItem(int position) {
+ return mState.mItems.get(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ public boolean isEnabled(int position) {
+ return !mState.mItems.get(position).mIsProcess;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (convertView == null) {
+ v = newView(parent);
+ } else {
+ v = convertView;
+ }
+ bindView(v, position);
+ return v;
+ }
+
+ public View newView(ViewGroup parent) {
+ View v = mInflater.inflate(R.layout.running_services_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.separator = (ImageView)v.findViewById(R.id.separator);
+ h.icon = (ImageView)v.findViewById(R.id.icon);
+ h.name = (TextView)v.findViewById(R.id.name);
+ h.runTime = (TextView)v.findViewById(R.id.run_time);
+ h.size = (TextView)v.findViewById(R.id.size);
+ v.setTag(h);
+ return v;
+ }
+
+ public void bindView(View view, int position) {
+ ViewHolder vh = (ViewHolder) view.getTag();
+ BaseItem item = mState.mItems.get(position);
+ vh.name.setText(item.mDisplayLabel);
+ vh.separator.setVisibility(item.mNeedDivider
+ ? View.VISIBLE : View.INVISIBLE);
+ if (item.mIsProcess) {
+ view.setBackgroundColor(mProcessBgColor);
+ vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager()));
+ vh.runTime.setText(item.mName);
+ vh.size.setText(item.mSizeStr);
+ mActiveItems.remove(view);
+ } else {
+ view.setBackgroundDrawable(null);
+ vh.icon.setImageDrawable(null);
+ vh.runTime.setText("");
+ ActiveItem ai = new ActiveItem();
+ ai.mRootView = view;
+ ai.mItem = item;
+ ai.mHolder = vh;
+ ai.mFirstRunTime = item.mActiveSince;
+ ai.updateTime(RunningServices.this);
+ mActiveItems.put(view, ai);
+ }
+ }
+ }
+
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_TIMES:
+ for (ActiveItem ai : mActiveItems.values()) {
+ ai.updateTime(RunningServices.this);
+ }
+ removeMessages(MSG_UPDATE_TIMES);
+ msg = obtainMessage(MSG_UPDATE_TIMES);
+ sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ break;
+ case MSG_UPDATE_CONTENTS:
+ updateList();
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
+ mState = (State)getLastNonConfigurationInstance();
+ if (mState == null) {
+ mState = new State();
+ }
+ mProcessBgColor = 0xff505050;
+ getListView().setDivider(null);
+ getListView().setAdapter(new ServiceListAdapter(mState));
+ }
+
+ void updateList() {
+ if (mState.update(this, mAm)) {
+ ((ServiceListAdapter)(getListView().getAdapter())).notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ BaseItem bi = (BaseItem)l.getAdapter().getItem(position);
+ if (!bi.mIsProcess) {
+ mCurSelected = bi;
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.confirm_stop_service);
+ builder.setMessage(R.string.confirm_stop_service_msg);
+ builder.setPositiveButton(R.string.confirm_stop_stop, this);
+ builder.setNegativeButton(R.string.confirm_stop_cancel, null);
+ builder.setCancelable(true);
+ mCurDialog = builder.show();
+ } else {
+ mCurSelected = null;
+ }
+ }
+
+ public void onClick(DialogInterface dialog, int which) {
+ if (mCurSelected != null) {
+ stopService(new Intent().setComponent(
+ ((ServiceItem)mCurSelected).mRunningService.service));
+ updateList();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ mHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ updateList();
+ mHandler.removeMessages(MSG_UPDATE_TIMES);
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
+ mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
+ mHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ msg = mHandler.obtainMessage(MSG_UPDATE_CONTENTS);
+ mHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ return mState;
+ }
+
+ public void onMovedToScrapHeap(View view) {
+ mActiveItems.remove(view);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mCurDialog != null) {
+ mCurDialog.dismiss();
+ }
+ }
+}