summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2010-07-28 17:10:42 -0700
committerDianne Hackborn <hackbod@google.com>2010-07-28 17:41:08 -0700
commit508fedc4102790dcd7004e4962e2b540967b714b (patch)
treed5259213576e58e0709e25690e5b480bdcc8ecbc
parente7623f775cb37a8de049dae8c03b5b396526fdd1 (diff)
downloadpackages_apps_Settings-508fedc4102790dcd7004e4962e2b540967b714b.zip
packages_apps_Settings-508fedc4102790dcd7004e4962e2b540967b714b.tar.gz
packages_apps_Settings-508fedc4102790dcd7004e4962e2b540967b714b.tar.bz2
Improvements to manage apps / running services:
- Running services now keeps a single data structure to make switching through the UI a lot faster. - Display text when there are no apps. - Fix deadlock. - Add new preference entry to view manage apps for storage use. - Etc. Change-Id: I0f5babf407ed7e84169f59584ddcb6cd0e9d67d9
-rw-r--r--AndroidManifest.xml14
-rwxr-xr-xres/layout/manage_applications.xml (renamed from res/layout/compute_sizes.xml)24
-rw-r--r--res/values/strings.xml10
-rw-r--r--res/xml/application_settings.xml8
-rw-r--r--src/com/android/settings/applications/ApplicationsState.java2
-rw-r--r--src/com/android/settings/applications/ManageApplications.java20
-rw-r--r--src/com/android/settings/applications/RunningProcessesView.java134
-rw-r--r--src/com/android/settings/applications/RunningServiceDetails.java119
-rw-r--r--src/com/android/settings/applications/RunningState.java167
9 files changed, 310 insertions, 188 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 43374eb..7b15e3f 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -380,10 +380,10 @@
</intent-filter>
</activity>
- <!--
- <activity android:name=".applications.RunningServices"
- android:label="@string/runningservices_settings_title"
- android:clearTaskOnLaunch="true">
+ <!-- Provide direct entry into manage apps showing running services. -->
+ <activity-alias android:name=".RunningServices"
+ android:label="@string/runningservices_settings_title"
+ android:targetActivity=".applications.ManageApplications">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
@@ -391,11 +391,11 @@
<category android:name="android.intent.category.VOICE_LAUNCH" />
<category android:name="com.android.settings.SHORTCUT" />
</intent-filter>
- </activity>
- -->
+ </activity-alias>
<!-- Provide direct entry into manage apps showing running services. -->
- <activity-alias android:name=".RunningServices"
+ <activity-alias android:name=".applications.StorageUse"
+ android:label="@string/storageuse_settings_title"
android:targetActivity=".applications.ManageApplications">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/res/layout/compute_sizes.xml b/res/layout/manage_applications.xml
index 502488f..c398f5e 100755
--- a/res/layout/compute_sizes.xml
+++ b/res/layout/manage_applications.xml
@@ -4,9 +4,9 @@
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.
@@ -18,12 +18,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <ListView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/list"
- android:drawSelectorOnTop="false"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ <FrameLayout android:id="@+id/list_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ListView android:id="@android:id/list"
+ android:drawSelectorOnTop="false"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <TextView android:id="@android:id/empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:text="@string/no_applications"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+ </FrameLayout>
<view class="com.android.settings.applications.RunningProcessesView"
android:id="@+id/running_processes"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 37f9b56..720b998 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1687,7 +1687,8 @@
<string name="filter_apps_onsdcard">On SD card</string>
<!-- Manage applications, text telling using an application is disabled. -->
<string name="disabled">Disabled</string>
- <string name="loading">Loading\u2026</string>
+ <!-- Text shown when there are no applications to display. -->
+ <string name="no_applications">No applications.</string>
<!-- Manage app screen, shown when the activity is busy recomputing the size of each app -->
<string name="recompute_size">Recomputing size\u2026</string>
<!-- Manage applications, individual application screen, confirmation dialog title. Displays when user selects to "Clear data". -->
@@ -1750,12 +1751,19 @@ found in the list of installed applications.</string>
<!-- Manage applications. application installation location summary -->
<string name="app_install_location_summary">Change the preferred installation location for new applications.</string>
+ <!-- Services settings screen, setting option name for the user to go to the screen to view app storage use -->
+ <string name="storageuse_settings_title">Storage use</string>
+ <!-- Services settings screen, setting option summary for the user to go to the screen to app storage use -->
+ <string name="storageuse_settings_summary">View storage used by applications</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>
+ <!-- Text shown when there are no services running -->
+ <string name="no_running_services">Nothing running.</string>
<!-- Running services, description for a service in the started state -->
<string name="service_started_by_app">Started by application.</string>
<!-- Running services, description for a service in the started state -->
diff --git a/res/xml/application_settings.xml b/res/xml/application_settings.xml
index 6ac58d1..27a0411 100644
--- a/res/xml/application_settings.xml
+++ b/res/xml/application_settings.xml
@@ -60,6 +60,14 @@
</PreferenceScreen>
<PreferenceScreen
+ android:title="@string/storageuse_settings_title"
+ android:summary="@string/storageuse_settings_summary">
+ <intent android:action="android.intent.action.MAIN"
+ android:targetPackage="com.android.settings"
+ android:targetClass="com.android.settings.applications.StorageUse" />
+ </PreferenceScreen>
+
+ <PreferenceScreen
android:key="power_usage"
android:title="@string/power_usage_summary_title"
android:summary="@string/power_usage_summary">
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index d59aadd..f01743a 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -320,7 +320,7 @@ public class ApplicationsState {
if (entry.icon != null) {
return;
}
- synchronized (mEntriesMap) {
+ synchronized (entry) {
if (entry.icon == null) {
entry.icon = entry.info.loadIcon(mPm);
}
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index c2d8774..fd689c5 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -92,6 +92,8 @@ public class ManageApplications extends TabActivity implements
private String mCurrentPkgName;
+ private View mListContainer;
+
// ListView used to display list
private ListView mListView;
// Custom view used to display running processes
@@ -378,8 +380,9 @@ public class ManageApplications extends TabActivity implements
if (intent.getComponent().getClassName().equals(
"com.android.settings.RunningServices")) {
defaultTabTag = TAB_RUNNING;
- }
- if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
+ } else if (intent.getComponent().getClassName().equals(
+ "com.android.settings.applications.StorageUse")
+ || action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) {
mSortOrder = SORT_ORDER_SIZE;
mFilterApps = FILTER_APPS_ALL;
defaultTabTag = TAB_ALL;
@@ -401,9 +404,14 @@ public class ManageApplications extends TabActivity implements
mComputingSizeStr = getText(R.string.computing_size);
// initialize the inflater
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mRootView = mInflater.inflate(R.layout.compute_sizes, null);
+ mRootView = mInflater.inflate(R.layout.manage_applications, null);
+ mListContainer = mRootView.findViewById(R.id.list_container);
// Create adapter and list view here
- ListView lv = (ListView) mRootView.findViewById(android.R.id.list);
+ ListView lv = (ListView) mListContainer.findViewById(android.R.id.list);
+ View emptyView = mListContainer.findViewById(com.android.internal.R.id.empty);
+ if (emptyView != null) {
+ lv.setEmptyView(emptyView);
+ }
lv.setOnItemClickListener(this);
lv.setSaveEnabled(true);
lv.setItemsCanFocus(true);
@@ -543,7 +551,7 @@ public class ManageApplications extends TabActivity implements
}
if (mCurView != which) {
mRunningProcessesView.setVisibility(View.GONE);
- mListView.setVisibility(View.VISIBLE);
+ mListContainer.setVisibility(View.VISIBLE);
}
if (mActivityResumed) {
mApplicationsAdapter.resume(mFilterApps, mSortOrder);
@@ -560,7 +568,7 @@ public class ManageApplications extends TabActivity implements
mApplicationsAdapter.pause();
if (mCurView != which) {
mRunningProcessesView.setVisibility(View.VISIBLE);
- mListView.setVisibility(View.GONE);
+ mListContainer.setVisibility(View.GONE);
}
}
mCurView = which;
diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java
index 580ae05..7dbd8f8 100644
--- a/src/com/android/settings/applications/RunningProcessesView.java
+++ b/src/com/android/settings/applications/RunningProcessesView.java
@@ -26,15 +26,12 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -53,17 +50,8 @@ import java.util.HashMap;
import java.util.Iterator;
public class RunningProcessesView extends FrameLayout
- implements AdapterView.OnItemClickListener, RecyclerListener {
-
- /** 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 int MSG_REFRESH_UI = 3;
-
- static final long TIME_UPDATE_DELAY = 1000;
- static final long CONTENTS_UPDATE_DELAY = 2000;
+ implements AdapterView.OnItemClickListener, RecyclerListener,
+ RunningState.OnRefreshUiListener {
// Memory pages are 4K.
static final long PAGE_SIZE = 4*1024;
@@ -128,6 +116,8 @@ public class RunningProcessesView extends FrameLayout
if (uptimeView != null) {
if (mItem.mActiveSince >= 0) {
+ //Log.i("foo", "Time for " + mItem.mDisplayLabel
+ // + ": " + (SystemClock.uptimeMillis()-mFirstRunTime));
uptimeView.setText(DateUtils.formatElapsedTime(builder,
(SystemClock.uptimeMillis()-mFirstRunTime)/1000));
} else {
@@ -220,6 +210,11 @@ public class RunningProcessesView extends FrameLayout
return mItems.size();
}
+ @Override
+ public boolean isEmpty() {
+ return mState.hasData() && mItems.size() == 0;
+ }
+
public Object getItem(int position) {
return mItems.get(position);
}
@@ -330,55 +325,6 @@ public class RunningProcessesView extends FrameLayout
}
}
- HandlerThread mBackgroundThread;
- final class BackgroundHandler extends Handler {
- public BackgroundHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_CONTENTS:
- Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
- cmd.arg1 = mState.update(getContext(), mAm) ? 1 : 0;
- mHandler.sendMessage(cmd);
- removeMessages(MSG_UPDATE_CONTENTS);
- msg = obtainMessage(MSG_UPDATE_CONTENTS);
- sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
- break;
- }
- }
- };
-
- BackgroundHandler mBackgroundHandler;
-
- final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_TIMES:
- Iterator<ActiveItem> it = mActiveItems.values().iterator();
- while (it.hasNext()) {
- ActiveItem ai = it.next();
- if (ai.mRootView.getWindowToken() == null) {
- // Clean out any dead views, just in case.
- it.remove();
- continue;
- }
- ai.updateTime(getContext(), mBuilder);
- }
- removeMessages(MSG_UPDATE_TIMES);
- msg = obtainMessage(MSG_UPDATE_TIMES);
- sendMessageDelayed(msg, TIME_UPDATE_DELAY);
- break;
- case MSG_REFRESH_UI:
- refreshUi(msg.arg1 != 0);
- break;
- }
- }
- };
-
private boolean matchText(byte[] buffer, int index, String text) {
int N = text.length();
if ((index+N) >= buffer.length) {
@@ -506,10 +452,7 @@ public class RunningProcessesView extends FrameLayout
public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) {
mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
- mState = (RunningState)nonConfigurationInstace;
- if (mState == null) {
- mState = new RunningState();
- }
+ mState = RunningState.getInstance(getContext());
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.running_processes_view, this);
@@ -531,28 +474,49 @@ public class RunningProcessesView extends FrameLayout
}
public void doPause() {
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- if (mBackgroundThread != null) {
- mBackgroundThread.quit();
- mBackgroundThread = null;
- mBackgroundHandler = null;
- }
+ mState.pause();
}
public void doResume() {
- refreshUi(mState.update(getContext(), mAm));
- mBackgroundThread = new HandlerThread("RunningServices");
- mBackgroundThread.start();
- mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
- mHandler.sendMessageDelayed(msg, TIME_UPDATE_DELAY);
- mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
- msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
- mBackgroundHandler.sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ mState.resume(this);
+ if (mState.hasData()) {
+ // If the state already has its data, then let's populate our
+ // list right now to avoid flicker.
+ refreshUi(true);
+ }
}
public Object doRetainNonConfigurationInstance() {
- return mState;
+ return null;
+ }
+
+ void updateTimes() {
+ Iterator<ActiveItem> it = mActiveItems.values().iterator();
+ while (it.hasNext()) {
+ ActiveItem ai = it.next();
+ if (ai.mRootView.getWindowToken() == null) {
+ // Clean out any dead views, just in case.
+ it.remove();
+ continue;
+ }
+ ai.updateTime(getContext(), mBuilder);
+ }
+ }
+
+ @Override
+ public void onRefreshUi(int what) {
+ switch (what) {
+ case REFRESH_TIME:
+ updateTimes();
+ break;
+ case REFRESH_DATA:
+ refreshUi(false);
+ updateTimes();
+ break;
+ case REFRESH_STRUCTURE:
+ refreshUi(true);
+ updateTimes();
+ break;
+ }
}
}
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index a55e54f..d57cd4a 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -42,16 +42,13 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-public class RunningServiceDetails extends Activity {
+public class RunningServiceDetails extends Activity
+ implements RunningState.OnRefreshUiListener {
static final String TAG = "RunningServicesDetails";
static final String KEY_UID = "uid";
static final String KEY_PROCESS = "process";
- static final int MSG_UPDATE_TIMES = 1;
- static final int MSG_UPDATE_CONTENTS = 2;
- static final int MSG_REFRESH_UI = 3;
-
ActivityManager mAm;
LayoutInflater mInflater;
@@ -156,9 +153,7 @@ public class RunningServiceDetails extends Activity {
// so no reason for the UI to stick around.
finish();
} else {
- if (mBackgroundHandler != null) {
- mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
- }
+ mState.updateNow();
}
} else {
// Heavy-weight process. We'll do a force-stop on it.
@@ -170,52 +165,6 @@ public class RunningServiceDetails extends Activity {
StringBuilder mBuilder = new StringBuilder(128);
- HandlerThread mBackgroundThread;
- final class BackgroundHandler extends Handler {
- public BackgroundHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_CONTENTS:
- Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
- cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0;
- mHandler.sendMessage(cmd);
- removeMessages(MSG_UPDATE_CONTENTS);
- msg = obtainMessage(MSG_UPDATE_CONTENTS);
- sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
- break;
- }
- }
- };
-
- BackgroundHandler mBackgroundHandler;
-
- final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_UPDATE_TIMES:
- if (mSnippetActiveItem != null) {
- mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
- }
- for (int i=0; i<mActiveDetails.size(); i++) {
- mActiveDetails.get(i).mActiveItem.updateTime(
- RunningServiceDetails.this, mBuilder);
- }
- removeMessages(MSG_UPDATE_TIMES);
- msg = obtainMessage(MSG_UPDATE_TIMES);
- sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
- break;
- case MSG_REFRESH_UI:
- refreshUi(msg.arg1 != 0);
- break;
- }
- }
- };
-
boolean findMergedItem() {
RunningState.MergedItem item = null;
ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
@@ -447,10 +396,7 @@ public class RunningServiceDetails extends Activity {
mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mState = (RunningState)getLastNonConfigurationInstance();
- if (mState == null) {
- mState = new RunningState();
- }
+ mState = RunningState.getInstance(this);
setContentView(R.layout.running_service_details);
@@ -464,36 +410,53 @@ public class RunningServiceDetails extends Activity {
@Override
protected void onPause() {
super.onPause();
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- if (mBackgroundThread != null) {
- mBackgroundThread.quit();
- mBackgroundThread = null;
- mBackgroundHandler = null;
- }
+ mState.pause();
}
@Override
protected void onResume() {
super.onResume();
- refreshUi(mState.update(this, mAm));
- mBackgroundThread = new HandlerThread("RunningServices");
- mBackgroundThread.start();
- mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
- mHandler.removeMessages(MSG_UPDATE_TIMES);
- Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES);
- mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY);
- mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
- msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS);
- mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY);
- }
+ mState.resume(this);
- @Override
- public Object onRetainNonConfigurationInstance() {
- return mState;
+ // We want to go away if the service being shown no longer exists,
+ // so we need to ensure we have done the initial data retrieval before
+ // showing our ui.
+ mState.waitForData();
+
+ // And since we know we have the data, let's show the UI right away
+ // to avoid flicker.
+ refreshUi(true);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
+
+ void updateTimes() {
+ if (mSnippetActiveItem != null) {
+ mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder);
+ }
+ for (int i=0; i<mActiveDetails.size(); i++) {
+ mActiveDetails.get(i).mActiveItem.updateTime(
+ RunningServiceDetails.this, mBuilder);
+ }
+ }
+
+ @Override
+ public void onRefreshUi(int what) {
+ switch (what) {
+ case REFRESH_TIME:
+ updateTimes();
+ break;
+ case REFRESH_DATA:
+ refreshUi(false);
+ updateTimes();
+ break;
+ case REFRESH_STRUCTURE:
+ refreshUi(true);
+ updateTimes();
+ break;
+ }
+ }
}
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java
index b3f56e3..119742c 100644
--- a/src/com/android/settings/applications/RunningState.java
+++ b/src/com/android/settings/applications/RunningState.java
@@ -29,7 +29,12 @@ import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.text.format.Formatter;
import android.util.Log;
import android.util.SparseArray;
@@ -46,6 +51,23 @@ import java.util.List;
* applications/processes/services.
*/
public class RunningState {
+ static Object sGlobalLock = new Object();
+ static RunningState sInstance;
+
+ static final int MSG_UPDATE_CONTENTS = 1;
+ static final int MSG_REFRESH_UI = 2;
+ static final int MSG_UPDATE_TIME = 3;
+
+ static final long TIME_UPDATE_DELAY = 1000;
+ static final long CONTENTS_UPDATE_DELAY = 2000;
+
+ static final int MAX_SERVICES = 100;
+
+ final Context mApplicationContext;
+ final ActivityManager mAm;
+ final PackageManager mPm;
+
+ OnRefreshUiListener mRefreshUiListener;
// Processes that are hosting a service we are interested in, organized
// by uid and name. Note that this mapping does not change even across
@@ -85,6 +107,9 @@ public class RunningState {
// background update thread and the UI thread.
final Object mLock = new Object();
+ boolean mResumed;
+ boolean mHaveData;
+
ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
@@ -95,6 +120,78 @@ public class RunningState {
int mNumServiceProcesses;
long mServiceProcessMemory;
+ // ----- BACKGROUND MONITORING THREAD -----
+
+ final HandlerThread mBackgroundThread;
+ final class BackgroundHandler extends Handler {
+ public BackgroundHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_CONTENTS:
+ synchronized (mLock) {
+ if (!mResumed) {
+ return;
+ }
+ }
+ Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI);
+ cmd.arg1 = update(mApplicationContext, mAm) ? 1 : 0;
+ mHandler.sendMessage(cmd);
+ removeMessages(MSG_UPDATE_CONTENTS);
+ msg = obtainMessage(MSG_UPDATE_CONTENTS);
+ sendMessageDelayed(msg, CONTENTS_UPDATE_DELAY);
+ break;
+ }
+ }
+ };
+
+ final BackgroundHandler mBackgroundHandler;
+
+ final Handler mHandler = new Handler() {
+ int mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REFRESH_UI:
+ mNextUpdate = msg.arg1 != 0
+ ? OnRefreshUiListener.REFRESH_STRUCTURE
+ : OnRefreshUiListener.REFRESH_DATA;
+ break;
+ case MSG_UPDATE_TIME:
+ synchronized (mLock) {
+ if (!mResumed) {
+ return;
+ }
+ }
+ removeMessages(MSG_UPDATE_TIME);
+ Message m = obtainMessage(MSG_UPDATE_TIME);
+ sendMessageDelayed(m, TIME_UPDATE_DELAY);
+
+ if (mRefreshUiListener != null) {
+ //Log.i("foo", "Refresh UI: " + mNextUpdate
+ // + " @ " + SystemClock.uptimeMillis());
+ mRefreshUiListener.onRefreshUi(mNextUpdate);
+ mNextUpdate = OnRefreshUiListener.REFRESH_TIME;
+ }
+ break;
+ }
+ }
+ };
+
+ // ----- DATA STRUCTURES -----
+
+ static interface OnRefreshUiListener {
+ public static final int REFRESH_TIME = 0;
+ public static final int REFRESH_DATA = 1;
+ public static final int REFRESH_STRUCTURE = 2;
+
+ public void onRefreshUi(int what);
+ }
+
static class BaseItem {
final boolean mIsProcess;
@@ -428,7 +525,69 @@ public class RunningState {
return label;
}
- boolean update(Context context, ActivityManager am) {
+ static RunningState getInstance(Context context) {
+ synchronized (sGlobalLock) {
+ if (sInstance == null) {
+ sInstance = new RunningState(context);
+ }
+ return sInstance;
+ }
+ }
+
+ private RunningState(Context context) {
+ mApplicationContext = context.getApplicationContext();
+ mAm = (ActivityManager)mApplicationContext.getSystemService(Context.ACTIVITY_SERVICE);
+ mPm = mApplicationContext.getPackageManager();
+ mResumed = false;
+ mBackgroundThread = new HandlerThread("RunningState:Background");
+ mBackgroundThread.start();
+ mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper());
+ }
+
+ void resume(OnRefreshUiListener listener) {
+ synchronized (mLock) {
+ mResumed = true;
+ mRefreshUiListener = listener;
+ if (!mBackgroundHandler.hasMessages(MSG_UPDATE_CONTENTS)) {
+ mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
+ }
+ mHandler.sendEmptyMessage(MSG_UPDATE_TIME);
+ }
+ }
+
+ void updateNow() {
+ synchronized (mLock) {
+ mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS);
+ mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS);
+ }
+ }
+
+ boolean hasData() {
+ synchronized (mLock) {
+ return mHaveData;
+ }
+ }
+
+ void waitForData() {
+ synchronized (mLock) {
+ while (!mHaveData) {
+ try {
+ mLock.wait(0);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ void pause() {
+ synchronized (mLock) {
+ mResumed = false;
+ mRefreshUiListener = null;
+ mHandler.removeMessages(MSG_UPDATE_TIME);
+ }
+ }
+
+ private boolean update(Context context, ActivityManager am) {
final PackageManager pm = context.getPackageManager();
mSequence++;
@@ -436,7 +595,7 @@ public class RunningState {
boolean changed = false;
List<ActivityManager.RunningServiceInfo> services
- = am.getRunningServices(RunningProcessesView.MAX_SERVICES);
+ = am.getRunningServices(MAX_SERVICES);
final int NS = services != null ? services.size() : 0;
for (int i=0; i<NS; i++) {
ActivityManager.RunningServiceInfo si = services.get(i);
@@ -776,6 +935,10 @@ public class RunningState {
mBackgroundProcessMemory = backgroundProcessMemory;
mForegroundProcessMemory = foregroundProcessMemory;
mServiceProcessMemory = serviceProcessMemory;
+ if (!mHaveData) {
+ mHaveData = true;
+ mLock.notifyAll();
+ }
}
return changed;