From 728ac35373028b79205a788c526ef4b495518ec1 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 6 Jun 2010 22:51:42 -0700 Subject: [PATCH] New Running Services UI. This introduces a simplified (thanks, dsandler!) UI for Running Services, collapsing the groups of apps and processes into single lines. Tapping on a line moves to a new activity showing details on that group, where the stop functionality is now available. This UI is now also integrated into Manage Applications, as the Running tab. You no longer get a really confusing, misleading, scary list of every package that appears to be laying around for some reason. The code was also re-organized, to put everything related to Manage Applications and Running Services under its own package. There is still some clean-up -- some performance improvements (such as not re-computing the world when we switch to the details view), and if this looks good then eradicating the old running services UI. Change-Id: I3fc059c18060600742cab5b455d11ff74bf45ae3 --- AndroidManifest.xml | 35 +- res/layout/compute_sizes.xml | 21 +- res/layout/manage_applications_item.xml | 1 - res/layout/running_processes_item.xml | 94 ++ res/layout/running_processes_view.xml | 60 + res/layout/running_service_details.xml | 36 + .../running_service_details_process.xml | 37 + .../running_service_details_service.xml | 48 + res/layout/running_services.xml | 2 +- res/layout/separator_label.xml | 21 + res/values/strings.xml | 49 +- res/xml/application_settings.xml | 9 + src/com/android/settings/RunningServices.java | 1172 ----------------- .../InstalledAppDetails.java | 8 +- .../ManageApplications.java | 323 +++-- .../applications/RunningProcessesView.java | 548 ++++++++ .../applications/RunningProcessesViewOld.java | 538 ++++++++ .../applications/RunningServiceDetails.java | 390 ++++++ .../applications/RunningServices.java | 582 ++++++++ .../settings/applications/RunningState.java | 736 +++++++++++ .../settings/fuelgauge/PowerUsageDetail.java | 8 +- 21 files changed, 3359 insertions(+), 1359 deletions(-) create mode 100644 res/layout/running_processes_item.xml create mode 100644 res/layout/running_processes_view.xml create mode 100644 res/layout/running_service_details.xml create mode 100644 res/layout/running_service_details_process.xml create mode 100644 res/layout/running_service_details_service.xml create mode 100644 res/layout/separator_label.xml delete mode 100644 src/com/android/settings/RunningServices.java rename src/com/android/settings/{ => applications}/InstalledAppDetails.java (99%) rename src/com/android/settings/{ => applications}/ManageApplications.java (94%) create mode 100644 src/com/android/settings/applications/RunningProcessesView.java create mode 100644 src/com/android/settings/applications/RunningProcessesViewOld.java create mode 100644 src/com/android/settings/applications/RunningServiceDetails.java create mode 100644 src/com/android/settings/applications/RunningServices.java create mode 100644 src/com/android/settings/applications/RunningState.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 78c0f5b3540..c3b0236541c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -350,7 +350,7 @@ - @@ -365,15 +365,22 @@ - + + + + - + - + - @@ -384,6 +391,24 @@ + --> + + + + + + + + + + + + + + - + android:layout_height="match_parent" > + + + + + + \ No newline at end of file diff --git a/res/layout/manage_applications_item.xml b/res/layout/manage_applications_item.xml index cdf4c9de58e..48274ce0f98 100755 --- a/res/layout/manage_applications_item.xml +++ b/res/layout/manage_applications_item.xml @@ -31,7 +31,6 @@ diff --git a/res/layout/running_processes_item.xml b/res/layout/running_processes_item.xml new file mode 100644 index 00000000000..dacee9e5e51 --- /dev/null +++ b/res/layout/running_processes_item.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layout/running_processes_view.xml b/res/layout/running_processes_view.xml new file mode 100644 index 00000000000..6810ba0003f --- /dev/null +++ b/res/layout/running_processes_view.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + diff --git a/res/layout/running_service_details.xml b/res/layout/running_service_details.xml new file mode 100644 index 00000000000..98c144c1350 --- /dev/null +++ b/res/layout/running_service_details.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/res/layout/running_service_details_process.xml b/res/layout/running_service_details_process.xml new file mode 100644 index 00000000000..d12b4865493 --- /dev/null +++ b/res/layout/running_service_details_process.xml @@ -0,0 +1,37 @@ + + + + + + + + + + diff --git a/res/layout/running_service_details_service.xml b/res/layout/running_service_details_service.xml new file mode 100644 index 00000000000..b9f9bbe3dbc --- /dev/null +++ b/res/layout/running_service_details_service.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + diff --git a/res/layout/running_services.xml b/res/layout/running_services.xml index 5c0da6f1a96..faca22c39db 100644 --- a/res/layout/running_services.xml +++ b/res/layout/running_services.xml @@ -35,7 +35,7 @@ android:text="@string/no_running_services" android:textAppearance="?android:attr/textAppearanceLarge" /> - + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 6b426c8febe..ebd11f87a6d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1766,16 +1766,55 @@ found in the list of installed applications. Cancel - Started by application: touch to stop + Started by application. - %1$s: touch to manage + %1$s - Avail: %2$s+%3$s in %1$d + %1$s available - Other: %2$s in %1$d + %1$s in use - Process: %1$s + %1$s + + %1$d + process and %2$d service + + %1$d + process and %2$d services + + %1$d + processes and %2$d service + + %1$d + processes and %2$d services + + Running application + + Not active + + Services + + Processes + + Stop + + Manage + + This service was started by its + application. Stopping it may cause the application to misbehave. + + %1$s: + currently in use. You can manage your settings to stop it. + + Main process that is in use. + + Service %1$s + is in use. + + Provider %1$s + is in use. + Language & keyboard diff --git a/res/xml/application_settings.xml b/res/xml/application_settings.xml index 4df21da43ba..6ac58d1acd6 100644 --- a/res/xml/application_settings.xml +++ b/res/xml/application_settings.xml @@ -59,6 +59,15 @@ android:targetClass="com.android.settings.RunningServices" /> + + + + diff --git a/src/com/android/settings/RunningServices.java b/src/com/android/settings/RunningServices.java deleted file mode 100644 index e67adf06a03..00000000000 --- a/src/com/android/settings/RunningServices.java +++ /dev/null @@ -1,1172 +0,0 @@ -/* - * 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.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentSender; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageItemInfo; -import android.content.pm.PackageManager; -import android.content.pm.ServiceInfo; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.os.Bundle; -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.os.SystemProperties; -import android.text.format.DateUtils; -import android.text.format.Formatter; -import android.util.AttributeSet; -import android.util.Log; -import android.util.SparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.TextView; - -import java.io.FileInputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -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 int MSG_REFRESH_UI = 3; - - static final long TIME_UPDATE_DELAY = 1000; - static final long CONTENTS_UPDATE_DELAY = 2000; - - // Memory pages are 4K. - static final long PAGE_SIZE = 4*1024; - - long SECONDARY_SERVER_MEM; - - final HashMap mActiveItems = new HashMap(); - - ActivityManager mAm; - - State mState; - - StringBuilder mBuilder = new StringBuilder(128); - - BaseItem mCurSelected; - - int mProcessBgColor; - - LinearColorBar mColorBar; - TextView mBackgroundProcessText; - TextView mForegroundProcessText; - - int mLastNumBackgroundProcesses = -1; - int mLastNumForegroundProcesses = -1; - int mLastNumServiceProcesses = -1; - long mLastBackgroundProcessMemory = -1; - long mLastForegroundProcessMemory = -1; - long mLastServiceProcessMemory = -1; - long mLastAvailMemory = -1; - - Dialog mCurDialog; - - byte[] mBuffer = new byte[1024]; - - class ActiveItem { - View mRootView; - BaseItem mItem; - ActivityManager.RunningServiceInfo mService; - ViewHolder mHolder; - long mFirstRunTime; - - void updateTime(Context context) { - if (mItem.mIsProcess) { - String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; - if (!size.equals(mItem.mCurSizeStr)) { - mItem.mCurSizeStr = size; - mHolder.size.setText(size); - } - } else { - 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 { - final boolean mIsProcess; - - PackageItemInfo mPackageInfo; - CharSequence mDisplayLabel; - String mLabel; - String mDescription; - - int mCurSeq; - - long mActiveSince; - long mSize; - String mSizeStr; - String mCurSizeStr; - boolean mNeedDivider; - - public BaseItem(boolean isProcess) { - mIsProcess = isProcess; - } - } - - static class ServiceItem extends BaseItem { - ActivityManager.RunningServiceInfo mRunningService; - ServiceInfo mServiceInfo; - boolean mShownAsStarted; - - public ServiceItem() { - super(false); - } - } - - static class ProcessItem extends BaseItem { - final HashMap mServices - = new HashMap(); - final SparseArray mDependentProcesses - = new SparseArray(); - - final int mUid; - final String mProcessName; - int mPid; - - ProcessItem mClient; - int mLastNumDependentProcesses; - - int mRunningSeq; - ActivityManager.RunningAppProcessInfo mRunningProcessInfo; - - // Purely for sorting. - boolean mIsSystem; - boolean mIsStarted; - long mActiveSince; - - public ProcessItem(Context context, int uid, String processName) { - super(true); - mDescription = context.getResources().getString( - R.string.service_process_name, processName); - mUid = uid; - mProcessName = processName; - } - - void ensureLabel(PackageManager pm) { - if (mLabel != null) { - return; - } - - try { - ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0); - if (ai.uid == mUid) { - mDisplayLabel = ai.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - mPackageInfo = ai; - return; - } - } catch (PackageManager.NameNotFoundException e) { - } - - // If we couldn't get information about the overall - // process, try to find something about the uid. - String[] pkgs = pm.getPackagesForUid(mUid); - - // If there is one package with this uid, that is what we want. - if (pkgs.length == 1) { - try { - ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); - mDisplayLabel = ai.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - mPackageInfo = ai; - return; - } catch (PackageManager.NameNotFoundException e) { - } - } - - // If there are multiple, see if one gives us the official name - // for this uid. - 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) { - mDisplayLabel = nm; - mLabel = nm.toString(); - mPackageInfo = pi.applicationInfo; - return; - } - } - } catch (PackageManager.NameNotFoundException e) { - } - } - - // If still don't have anything to display, just use the - // service info. - if (mServices.size() > 0) { - mPackageInfo = mServices.values().iterator().next() - .mServiceInfo.applicationInfo; - mDisplayLabel = mPackageInfo.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - return; - } - - // Finally... whatever, just pick the first package's name. - try { - ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); - mDisplayLabel = ai.loadLabel(pm); - mLabel = mDisplayLabel.toString(); - mPackageInfo = ai; - return; - } catch (PackageManager.NameNotFoundException e) { - } - } - - boolean updateService(Context context, - ActivityManager.RunningServiceInfo service) { - final PackageManager pm = context.getPackageManager(); - - 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.applicationInfo; - 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; - } - if (service.clientPackage != null && service.clientLabel != 0) { - if (si.mShownAsStarted) { - si.mShownAsStarted = false; - changed = true; - } - try { - Resources clientr = pm.getResourcesForApplication(service.clientPackage); - String label = clientr.getString(service.clientLabel); - si.mDescription = context.getResources().getString( - R.string.service_client_name, label); - } catch (PackageManager.NameNotFoundException e) { - si.mDescription = null; - } - } else { - if (!si.mShownAsStarted) { - si.mShownAsStarted = true; - changed = true; - } - si.mDescription = context.getResources().getString( - R.string.service_started_by_app); - } - - return changed; - } - - boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) { - mSize = ((long)mem.getTotalPss()) * 1024; - if (mCurSeq == curSeq) { - String sizeStr = Formatter.formatShortFileSize( - context, mSize); - if (!sizeStr.equals(mSizeStr)){ - mSizeStr = sizeStr; - // We update this on the second tick where we update just - // the text in the current items, so no need to say we - // changed here. - return false; - } - } - return false; - } - - boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) { - final int NP = mDependentProcesses.size(); - boolean changed = false; - for (int i=0; i dest, - ArrayList destProc) { - final int NP = mDependentProcesses.size(); - for (int i=0; i 0) { - destProc.add(proc); - } - } - } - } - - static class ServiceProcessComparator implements Comparator { - public int compare(ProcessItem object1, ProcessItem object2) { - if (object1.mIsStarted != object2.mIsStarted) { - // Non-started processes go last. - return object1.mIsStarted ? -1 : 1; - } - if (object1.mIsSystem != object2.mIsSystem) { - // System processes go below non-system. - return object1.mIsSystem ? 1 : -1; - } - if (object1.mActiveSince != object2.mActiveSince) { - // Remaining ones are sorted with the longest running - // services last. - return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1; - } - return 0; - } - } - - static class State { - final SparseArray> mProcesses - = new SparseArray>(); - final SparseArray mActiveProcesses - = new SparseArray(); - final ServiceProcessComparator mServiceProcessComparator - = new ServiceProcessComparator(); - - // Temporary for finding process dependencies. - final SparseArray mRunningProcesses - = new SparseArray(); - - final ArrayList mProcessItems = new ArrayList(); - final ArrayList mAllProcessItems = new ArrayList(); - - int mSequence = 0; - - // ----- following protected by mLock ----- - - // Lock for protecting the state that will be shared between the - // background update thread and the UI thread. - final Object mLock = new Object(); - - ArrayList mItems = new ArrayList(); - - int mNumBackgroundProcesses; - long mBackgroundProcessMemory; - int mNumForegroundProcesses; - long mForegroundProcessMemory; - int mNumServiceProcesses; - long mServiceProcessMemory; - - boolean update(Context context, ActivityManager am) { - final PackageManager pm = context.getPackageManager(); - - mSequence++; - - boolean changed = false; - - List services - = am.getRunningServices(MAX_SERVICES); - final int NS = services != null ? services.size() : 0; - for (int i=0; i procs = mProcesses.get(si.uid); - if (procs == null) { - procs = new HashMap(); - mProcesses.put(si.uid, procs); - } - ProcessItem proc = procs.get(si.process); - if (proc == null) { - changed = true; - proc = new ProcessItem(context, si.uid, si.process); - procs.put(si.process, proc); - } - - if (proc.mCurSeq != mSequence) { - int pid = si.restarting == 0 ? si.pid : 0; - if (pid != proc.mPid) { - changed = true; - if (proc.mPid != pid) { - if (proc.mPid != 0) { - mActiveProcesses.remove(proc.mPid); - } - if (pid != 0) { - mActiveProcesses.put(pid, proc); - } - proc.mPid = pid; - } - } - proc.mDependentProcesses.clear(); - proc.mCurSeq = mSequence; - } - changed |= proc.updateService(context, si); - } - - // Now update the map of other processes that are running (but - // don't have services actively running inside them). - List processes - = am.getRunningAppProcesses(); - final int NP = processes != null ? processes.size() : 0; - for (int i=0; i procs = mProcesses.valueAt(i); - Iterator pit = procs.values().iterator(); - while (pit.hasNext()) { - ProcessItem pi = pit.next(); - if (pi.mCurSeq == mSequence) { - pi.ensureLabel(pm); - if (pi.mPid == 0) { - // Sanity: a non-process can't be dependent on - // anything. - pi.mDependentProcesses.clear(); - } - } else { - changed = true; - pit.remove(); - if (procs.size() == 0) { - mProcesses.remove(mProcesses.keyAt(i)); - } - if (pi.mPid != 0) { - mActiveProcesses.remove(pi.mPid); - } - continue; - } - Iterator sit = pi.mServices.values().iterator(); - while (sit.hasNext()) { - ServiceItem si = sit.next(); - if (si.mCurSeq != mSequence) { - changed = true; - sit.remove(); - } - } - } - } - - if (changed) { - // First determine an order for the services. - ArrayList sortedProcesses = new ArrayList(); - for (int i=0; i si.mRunningService.activeSince) { - pi.mActiveSince = si.mRunningService.activeSince; - } - } - } - sortedProcesses.add(pi); - } - } - - Collections.sort(sortedProcesses, mServiceProcessComparator); - - ArrayList newItems = new ArrayList(); - mProcessItems.clear(); - for (int i=0; i 0) { - mProcessItems.add(pi); - } - // And finally the services running in it. - boolean needDivider = false; - for (ServiceItem si : pi.mServices.values()) { - si.mNeedDivider = needDivider; - needDivider = true; - newItems.add(si); - } - } - synchronized (mLock) { - mItems = newItems; - } - } - - // Count number of interesting other (non-active) processes, and - // build a list of all processes we will retrieve memory for. - mAllProcessItems.clear(); - mAllProcessItems.addAll(mProcessItems); - int numBackgroundProcesses = 0; - int numForegroundProcesses = 0; - int numServiceProcesses = 0; - NRP = mRunningProcesses.size(); - for (int i=0; i= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { - numBackgroundProcesses++; - mAllProcessItems.add(proc); - } else if (proc.mRunningProcessInfo.importance <= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { - numForegroundProcesses++; - mAllProcessItems.add(proc); - } else { - Log.i(TAG, "Unknown non-service process: " - + proc.mProcessName + " #" + proc.mPid); - } - } else { - numServiceProcesses++; - } - } - - long backgroundProcessMemory = 0; - long foregroundProcessMemory = 0; - long serviceProcessMemory = 0; - try { - final int numProc = mAllProcessItems.size(); - int[] pids = new int[numProc]; - for (int i=0; i=0; i--) { - ProcessItem proc = mAllProcessItems.get(i); - changed |= proc.updateSize(context, mem[i], mSequence); - if (proc.mCurSeq == mSequence) { - serviceProcessMemory += proc.mSize; - } else if (proc.mRunningProcessInfo.importance >= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { - backgroundProcessMemory += proc.mSize; - } else if (proc.mRunningProcessInfo.importance <= - ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { - foregroundProcessMemory += proc.mSize; - } - } - } catch (RemoteException e) { - } - - synchronized (mLock) { - mNumBackgroundProcesses = numBackgroundProcesses; - mNumForegroundProcesses = numForegroundProcesses; - mNumServiceProcesses = numServiceProcesses; - mBackgroundProcessMemory = backgroundProcessMemory; - mForegroundProcessMemory = foregroundProcessMemory; - mServiceProcessMemory = serviceProcessMemory; - } - - return changed; - } - - ArrayList getCurrentItems() { - synchronized (mLock) { - return mItems; - } - } - } - - static class TimeTicker extends TextView { - public TimeTicker(Context context, AttributeSet attrs) { - super(context, attrs); - } - } - - static class ViewHolder { - ImageView separator; - ImageView icon; - TextView name; - TextView description; - TextView size; - } - - class ServiceListAdapter extends BaseAdapter { - final State mState; - final LayoutInflater mInflater; - ArrayList mItems; - - ServiceListAdapter(State state) { - mState = state; - mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); - refreshItems(); - } - - void refreshItems() { - ArrayList newItems = mState.getCurrentItems(); - if (mItems != newItems) { - mItems = newItems; - } - if (mItems == null) { - mItems = new ArrayList(); - } - } - - public boolean hasStableIds() { - return true; - } - - public int getCount() { - return mItems.size(); - } - - public Object getItem(int position) { - return mItems.get(position); - } - - public long getItemId(int position) { - return mItems.get(position).hashCode(); - } - - public boolean areAllItemsEnabled() { - return false; - } - - public boolean isEnabled(int position) { - return !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.description = (TextView)v.findViewById(R.id.description); - h.size = (TextView)v.findViewById(R.id.size); - v.setTag(h); - return v; - } - - public void bindView(View view, int position) { - synchronized (mState.mLock) { - ViewHolder vh = (ViewHolder) view.getTag(); - if (position >= mItems.size()) { - // List must have changed since we last reported its - // size... ignore here, we will be doing a data changed - // to refresh the entire list. - return; - } - BaseItem item = mItems.get(position); - vh.name.setText(item.mDisplayLabel); - vh.separator.setVisibility(item.mNeedDivider - ? View.VISIBLE : View.INVISIBLE); - ActiveItem ai = new ActiveItem(); - ai.mRootView = view; - ai.mItem = item; - ai.mHolder = vh; - ai.mFirstRunTime = item.mActiveSince; - vh.description.setText(item.mDescription); - if (item.mIsProcess) { - view.setBackgroundColor(mProcessBgColor); - vh.icon.setImageDrawable(null); - vh.icon.setVisibility(View.GONE); - vh.description.setText(item.mDescription); - item.mCurSizeStr = null; - } else { - view.setBackgroundDrawable(null); - vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager())); - vh.icon.setVisibility(View.VISIBLE); - vh.description.setText(item.mDescription); - ai.mFirstRunTime = item.mActiveSince; - } - ai.updateTime(RunningServices.this); - mActiveItems.put(view, ai); - } - } - } - - public static class LinearColorBar extends LinearLayout { - private float mRedRatio; - private float mYellowRatio; - private float mGreenRatio; - - final Rect mRect = new Rect(); - final Paint mPaint = new Paint(); - - public LinearColorBar(Context context, AttributeSet attrs) { - super(context, attrs); - setWillNotDraw(false); - mPaint.setStyle(Paint.Style.FILL); - } - - public void setRatios(float red, float yellow, float green) { - mRedRatio = red; - mYellowRatio = yellow; - mGreenRatio = green; - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - int width = getWidth(); - mRect.top = 0; - mRect.bottom = getHeight(); - - int left = 0; - - int right = left + (int)(width*mRedRatio); - if (left < right) { - mRect.left = left; - mRect.right = right; - mPaint.setColor(0xffff8080); - canvas.drawRect(mRect, mPaint); - width -= (right-left); - left = right; - } - - right = left + (int)(width*mYellowRatio); - if (left < right) { - mRect.left = left; - mRect.right = right; - mPaint.setColor(0xffffff00); - canvas.drawRect(mRect, mPaint); - width -= (right-left); - left = right; - } - - right = left + width; - if (left < right) { - mRect.left = left; - mRect.right = right; - mPaint.setColor(0xff80ff80); - canvas.drawRect(mRect, mPaint); - } - } - } - - 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(RunningServices.this, 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 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(RunningServices.this); - } - 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) { - return false; - } - for (int i=0; i= '0' && buffer[index] <= '9') { - int start = index; - index++; - while (index < buffer.length && buffer[index] >= '0' - && buffer[index] <= '9') { - index++; - } - String str = new String(buffer, 0, start, index-start); - return ((long)Integer.parseInt(str)) * 1024; - } - index++; - } - return 0; - } - - private long readAvailMem() { - try { - long memFree = 0; - long memCached = 0; - FileInputStream is = new FileInputStream("/proc/meminfo"); - int len = is.read(mBuffer); - is.close(); - final int BUFLEN = mBuffer.length; - for (int i=0; i appList =new ArrayList (); - List procList = getRunningAppProcessesList(); - if ((procList == null) || (procList.size() == 0)) { - return appList; - } - // Retrieve running processes from ActivityManager - for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) { - if ((appProcInfo != null) && (appProcInfo.pkgList != null)){ - int size = appProcInfo.pkgList.length; - for (int i = 0; i < size; i++) { - ApplicationInfo appInfo = null; - try { - appInfo = mPm.getApplicationInfo(appProcInfo.pkgList[i], - PackageManager.GET_UNINSTALLED_PACKAGES); - } catch (NameNotFoundException e) { - Log.w(TAG, "Error retrieving ApplicationInfo for pkg:"+appProcInfo.pkgList[i]); - continue; - } - if(appInfo != null) { - appList.add(appInfo); - } - } - } - } - return appList; } else { return installedAppList; } @@ -728,31 +702,6 @@ public class ManageApplications extends TabActivity implements } } return retList; - } else if (filterOption == FILTER_APPS_RUNNING) { - List procList = getRunningAppProcessesList(); - if ((procList == null) || (procList.size() == 0)) { - return retList; - } - // Retrieve running processes from ActivityManager - HashMap runningMap = - new HashMap(); - for (ActivityManager.RunningAppProcessInfo appProcInfo : procList) { - if ((appProcInfo != null) && (appProcInfo.pkgList != null)){ - int size = appProcInfo.pkgList.length; - for (int i = 0; i < size; i++) { - runningMap.put(appProcInfo.pkgList[i], appProcInfo); - } - } - } - // Query list to find running processes in current list - for (ApplicationInfo appInfo : pAppList) { - if (runningMap.get(appInfo.packageName) != null) { - if (matchFilter(filter, filterMap, appInfo.packageName)) { - retList.add(appInfo); - } - } - } - return retList; } else { for (ApplicationInfo appInfo : pAppList) { if (matchFilter(filter, filterMap, appInfo.packageName)) { @@ -763,11 +712,6 @@ public class ManageApplications extends TabActivity implements } } - private List getRunningAppProcessesList() { - ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); - return am.getRunningAppProcesses(); - } - // Some initialization code used when kicking off the size computation private void initAppList(List appList, int filterOption) { setProgressBarIndeterminateVisibility(true); @@ -1261,14 +1205,7 @@ public class ManageApplications extends TabActivity implements private boolean shouldBeInList(int filterOption, ApplicationInfo info) { // Match filter here - if (filterOption == FILTER_APPS_RUNNING) { - List runningList = getInstalledApps(FILTER_APPS_RUNNING); - for (ApplicationInfo running : runningList) { - if (running.packageName.equalsIgnoreCase(info.packageName)) { - return true; - } - } - } else if (filterOption == FILTER_APPS_THIRD_PARTY) { + if (filterOption == FILTER_APPS_THIRD_PARTY) { if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { return true; } else if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -1611,11 +1548,44 @@ public class ManageApplications extends TabActivity implements } } + static final int VIEW_NOTHING = 0; + static final int VIEW_LIST = 1; + static final int VIEW_RUNNING = 2; + + private void selectView(int which) { + if (mCurView == which) { + return; + } + + mCurView = which; + + if (which == VIEW_LIST) { + if (mResumedRunning) { + mRunningProcessesView.doPause(); + mResumedRunning = false; + } + mRunningProcessesView.setVisibility(View.GONE); + mListView.setVisibility(View.VISIBLE); + } else if (which == VIEW_RUNNING) { + if (!mCreatedRunning) { + mRunningProcessesView.doCreate(null, mNonConfigInstance); + mCreatedRunning = true; + } + if (mActivityResumed && !mResumedRunning) { + mRunningProcessesView.doResume(); + mResumedRunning = true; + } + mRunningProcessesView.setVisibility(View.VISIBLE); + mListView.setVisibility(View.GONE); + } + } + static final String TAB_DOWNLOADED = "Downloaded"; static final String TAB_RUNNING = "Running"; static final String TAB_ALL = "All"; static final String TAB_SDCARD = "OnSdCard"; private View mRootView; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -1627,18 +1597,31 @@ public class ManageApplications extends TabActivity implements Intent intent = getIntent(); String action = intent.getAction(); String defaultTabTag = TAB_DOWNLOADED; + if (intent.getComponent().getClassName().equals( + "com.android.settings.RunningServices")) { + defaultTabTag = TAB_RUNNING; + } if (action.equals(Intent.ACTION_MANAGE_PACKAGE_STORAGE)) { mSortOrder = SORT_ORDER_SIZE; mFilterApps = FILTER_APPS_ALL; defaultTabTag = TAB_ALL; mSizesFirst = true; } + + if (savedInstanceState != null) { + mSortOrder = savedInstanceState.getInt("sortOrder", mSortOrder); + mFilterApps = savedInstanceState.getInt("filterApps", mFilterApps); + String tmp = savedInstanceState.getString("defaultTabTag"); + if (tmp != null) defaultTabTag = tmp; + mSizesFirst = savedInstanceState.getBoolean("sizesFirst", mSizesFirst); + } + + mNonConfigInstance = getLastNonConfigurationInstance(); + mPm = getPackageManager(); // initialize some window features requestWindowFeature(Window.FEATURE_RIGHT_ICON); - requestWindowFeature(Window.FEATURE_PROGRESS); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - showLoadingMsg(); mDefaultAppIcon = Resources.getSystem().getDrawable( com.android.internal.R.drawable.sym_def_app_icon); mInvalidSizeStr = getText(R.string.invalid_size_value); @@ -1658,6 +1641,8 @@ public class ManageApplications extends TabActivity implements lv.setOnItemClickListener(this); lv.setTextFilterEnabled(true); mListView = lv; + mRunningProcessesView = (RunningProcessesView)mRootView.findViewById( + R.id.running_processes); if (DEBUG_TIME) { Log.i(TAG, "Total time in Activity.create:: " + (SystemClock.elapsedRealtime() - sCreate)+ " ms"); @@ -1677,10 +1662,6 @@ public class ManageApplications extends TabActivity implements .setIndicator(getString(R.string.filter_apps_third_party), getResources().getDrawable(R.drawable.ic_tab_download)) .setContent(this)); - tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING) - .setIndicator(getString(R.string.filter_apps_running), - getResources().getDrawable(R.drawable.ic_tab_running)) - .setContent(this)); tabHost.addTab(tabHost.newTabSpec(TAB_ALL) .setIndicator(getString(R.string.filter_apps_all), getResources().getDrawable(R.drawable.ic_tab_all)) @@ -1689,8 +1670,95 @@ public class ManageApplications extends TabActivity implements .setIndicator(getString(R.string.filter_apps_onsdcard), getResources().getDrawable(R.drawable.ic_tab_sdcard)) .setContent(this)); + tabHost.addTab(tabHost.newTabSpec(TAB_RUNNING) + .setIndicator(getString(R.string.filter_apps_running), + getResources().getDrawable(R.drawable.ic_tab_running)) + .setContent(this)); tabHost.setCurrentTabByTag(defaultTabTag); tabHost.setOnTabChangedListener(this); + + selectView(TAB_RUNNING.equals(defaultTabTag) ? VIEW_RUNNING : VIEW_LIST); + } + + @Override + public void onStart() { + super.onStart(); + // Register receiver + mReceiver.registerReceiver(); + sendMessageToHandler(INIT_PKG_INFO); + } + + @Override + protected void onResume() { + super.onResume(); + mActivityResumed = true; + if (mCurView == VIEW_RUNNING) { + mRunningProcessesView.doResume(); + mResumedRunning = true; + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("sortOrder", mSortOrder); + outState.putInt("filterApps", mFilterApps); + outState.putString("defautTabTag", getTabHost().getCurrentTabTag()); + outState.putBoolean("sizesFirst", mSizesFirst); + } + + @Override + public Object onRetainNonConfigurationInstance() { + return mRunningProcessesView.doRetainNonConfigurationInstance(); + } + + @Override + protected void onPause() { + super.onPause(); + mActivityResumed = false; + if (mResumedRunning) { + mRunningProcessesView.doPause(); + mResumedRunning = false; + } + } + + @Override + public void onStop() { + super.onStop(); + // Stop the background threads + if (mResourceThread != null) { + mResourceThread.setAbort(); + } + if (mSizeComputor != null) { + mSizeComputor.setAbort(); + } + // clear all messages related to application list + clearMessagesInHandler(); + // register receiver here + unregisterReceiver(mReceiver); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, + Intent data) { + if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { + // Refresh package attributes + try { + ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName, + PackageManager.GET_UNINSTALLED_PACKAGES); + } catch (NameNotFoundException e) { + Bundle rData = new Bundle(); + rData.putString(ATTR_PKG_NAME, mCurrentPkgName); + sendMessageToHandler(REMOVE_PKG, rData); + mCurrentPkgName = null; + } + } + } + + // Avoid the restart and pause when orientation changes + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); } @Override @@ -1700,34 +1768,6 @@ public class ManageApplications extends TabActivity implements super.onDestroy(); } - @Override - public Dialog onCreateDialog(int id, Bundle args) { - if (id == DLG_LOADING) { - ProgressDialog dlg = new ProgressDialog(this); - dlg.setProgressStyle(ProgressDialog.STYLE_SPINNER); - dlg.setMessage(getText(R.string.loading)); - dlg.setIndeterminate(true); - dlg.setOnCancelListener(this); - return dlg; - } - return null; - } - - private void showLoadingMsg() { - if (DEBUG_TIME) { - mLoadTimeStart = SystemClock.elapsedRealtime(); - } - showDialog(DLG_LOADING); - if(localLOGV) Log.i(TAG, "Displaying Loading message"); - } - - private void dismissLoadingMsg() { - if(localLOGV) Log.i(TAG, "Dismissing Loading message"); - dismissDialog(DLG_LOADING); - if (DEBUG_TIME) Log.i(TAG, "Displayed loading message for "+ - (SystemClock.elapsedRealtime() - mLoadTimeStart) + " ms"); - } - class AppInfoCache { final static boolean FILE_CACHE = true; private static final String mFileCacheName="ManageAppsInfo.txt"; @@ -1923,36 +1963,6 @@ public class ManageApplications extends TabActivity implements } } - @Override - public void onStart() { - super.onStart(); - // Register receiver - mReceiver.registerReceiver(); - sendMessageToHandler(INIT_PKG_INFO); - } - - @Override - public void onStop() { - super.onStop(); - // Stop the background threads - if (mResourceThread != null) { - mResourceThread.setAbort(); - } - if (mSizeComputor != null) { - mSizeComputor.setAbort(); - } - // clear all messages related to application list - clearMessagesInHandler(); - // register receiver here - unregisterReceiver(mReceiver); - } - - // Avoid the restart and pause when orientation changes - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - } - /* * comparator class used to sort AppInfo objects based on size */ @@ -1994,9 +2004,8 @@ public class ManageApplications extends TabActivity implements // utility method used to start sub activity private void startApplicationDetailsActivity() { // Create intent to start new activity - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setClass(this, InstalledAppDetails.class); - intent.putExtra(APP_PKG_NAME, mCurrentPkgName); + Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, + Uri.fromParts("package", mCurrentPkgName, null)); // start new activity to display extended information startActivityForResult(intent, INSTALLED_APP_DETAILS); } @@ -2049,33 +2058,19 @@ public class ManageApplications extends TabActivity implements int newOption; if (TAB_DOWNLOADED.equalsIgnoreCase(tabId)) { newOption = FILTER_APPS_THIRD_PARTY; - } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) { - newOption = FILTER_APPS_RUNNING; } else if (TAB_ALL.equalsIgnoreCase(tabId)) { newOption = FILTER_APPS_ALL; } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) { newOption = FILTER_APPS_SDCARD; + } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) { + selectView(VIEW_RUNNING); + return; } else { // Invalid option. Do nothing return; } + + selectView(VIEW_LIST); sendMessageToHandler(REORDER_LIST, newOption); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent data) { - if (requestCode == INSTALLED_APP_DETAILS && mCurrentPkgName != null) { - // Refresh package attributes - try { - ApplicationInfo info = mPm.getApplicationInfo(mCurrentPkgName, - PackageManager.GET_UNINSTALLED_PACKAGES); - } catch (NameNotFoundException e) { - Bundle rData = new Bundle(); - rData.putString(ATTR_PKG_NAME, mCurrentPkgName); - sendMessageToHandler(REMOVE_PKG, rData); - mCurrentPkgName = null; - } - } - } } diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java new file mode 100644 index 00000000000..9fbae6dc7f5 --- /dev/null +++ b/src/com/android/settings/applications/RunningProcessesView.java @@ -0,0 +1,548 @@ +/* + * Copyright (C) 2010 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.applications; + +import com.android.settings.R; + +import android.app.ActivityManager; +import android.app.Dialog; +import android.content.Context; +import android.content.Intent; +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.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +public class RunningProcessesView extends FrameLayout + implements AdapterView.OnItemClickListener { + + /** 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; + + // Memory pages are 4K. + static final long PAGE_SIZE = 4*1024; + + long SECONDARY_SERVER_MEM; + + final HashMap mActiveItems = new HashMap(); + + ActivityManager mAm; + + RunningState mState; + + StringBuilder mBuilder = new StringBuilder(128); + + RunningState.BaseItem mCurSelected; + + ListView mListView; + LinearColorBar mColorBar; + TextView mBackgroundProcessText; + TextView mForegroundProcessText; + + int mLastNumBackgroundProcesses = -1; + int mLastNumForegroundProcesses = -1; + int mLastNumServiceProcesses = -1; + long mLastBackgroundProcessMemory = -1; + long mLastForegroundProcessMemory = -1; + long mLastServiceProcessMemory = -1; + long mLastAvailMemory = -1; + + Dialog mCurDialog; + + byte[] mBuffer = new byte[1024]; + + public static class ActiveItem { + View mRootView; + RunningState.BaseItem mItem; + ActivityManager.RunningServiceInfo mService; + ViewHolder mHolder; + long mFirstRunTime; + + void updateTime(Context context, StringBuilder builder) { + TextView uptimeView = null; + + if (mItem instanceof RunningState.ServiceItem) { + // If we are displaying a service, then the service + // uptime goes at the top. + uptimeView = mHolder.size; + + } else { + String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; + if (!size.equals(mItem.mCurSizeStr)) { + mItem.mCurSizeStr = size; + mHolder.size.setText(size); + } + + if (mItem instanceof RunningState.MergedItem) { + // This item represents both services and proceses, + // so show the service uptime below. + uptimeView = mHolder.uptime; + } + } + + if (uptimeView != null) { + if (mItem.mActiveSince >= 0) { + uptimeView.setText(DateUtils.formatElapsedTime(builder, + (SystemClock.uptimeMillis()-mFirstRunTime)/1000)); + } else { + uptimeView.setText(context.getResources().getText( + R.string.service_restarting)); + } + } + } + } + + public static class ViewHolder { + public View rootView; + public ImageView icon; + public TextView name; + public TextView description; + public TextView size; + public TextView uptime; + + public ViewHolder(View v) { + rootView = v; + icon = (ImageView)v.findViewById(R.id.icon); + name = (TextView)v.findViewById(R.id.name); + description = (TextView)v.findViewById(R.id.description); + size = (TextView)v.findViewById(R.id.size); + uptime = (TextView)v.findViewById(R.id.uptime); + v.setTag(this); + } + + public ActiveItem bind(RunningState state, RunningState.BaseItem item, + StringBuilder builder) { + synchronized (state.mLock) { + name.setText(item.mDisplayLabel); + ActiveItem ai = new ActiveItem(); + ai.mRootView = rootView; + ai.mItem = item; + ai.mHolder = this; + ai.mFirstRunTime = item.mActiveSince; + description.setText(item.mDescription); + item.mCurSizeStr = null; + icon.setImageDrawable(item.mPackageInfo.loadIcon( + rootView.getContext().getPackageManager())); + icon.setVisibility(View.VISIBLE); + ai.updateTime(rootView.getContext(), builder); + return ai; + } + } + } + + static class TimeTicker extends TextView { + public TimeTicker(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + class ServiceListAdapter extends BaseAdapter { + final RunningState mState; + final LayoutInflater mInflater; + ArrayList mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList newItems = mState.getCurrentMergedItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList(); + } + } + + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return mItems.get(position).hashCode(); + } + + public boolean areAllItemsEnabled() { + return false; + } + + public boolean isEnabled(int position) { + return !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_processes_item, parent, false); + new ViewHolder(v); + return v; + } + + public void bindView(View view, int position) { + synchronized (mState.mLock) { + if (position >= mItems.size()) { + // List must have changed since we last reported its + // size... ignore here, we will be doing a data changed + // to refresh the entire list. + return; + } + ViewHolder vh = (ViewHolder) view.getTag(); + RunningState.MergedItem item = mItems.get(position); + ActiveItem ai = vh.bind(mState, item, mBuilder); + mActiveItems.put(view, ai); + } + } + } + + public static class LinearColorBar extends LinearLayout { + private float mRedRatio; + private float mYellowRatio; + private float mGreenRatio; + + final Rect mRect = new Rect(); + final Paint mPaint = new Paint(); + + public LinearColorBar(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + mPaint.setStyle(Paint.Style.FILL); + } + + public void setRatios(float red, float yellow, float green) { + mRedRatio = red; + mYellowRatio = yellow; + mGreenRatio = green; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + mRect.top = 0; + mRect.bottom = getHeight(); + + int left = 0; + + int right = left + (int)(width*mRedRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffff8080); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + (int)(width*mYellowRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffffff00); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + width; + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xff80ff80); + canvas.drawRect(mRect, mPaint); + } + } + } + + 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 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) { + return false; + } + for (int i=0; i= '0' && buffer[index] <= '9') { + int start = index; + index++; + while (index < buffer.length && buffer[index] >= '0' + && buffer[index] <= '9') { + index++; + } + String str = new String(buffer, 0, start, index-start); + return ((long)Integer.parseInt(str)) * 1024; + } + index++; + } + return 0; + } + + private long readAvailMem() { + try { + long memFree = 0; + long memCached = 0; + FileInputStream is = new FileInputStream("/proc/meminfo"); + int len = is.read(mBuffer); + is.close(); + final int BUFLEN = mBuffer.length; + for (int i=0; i parent, View v, int position, long id) { + ListView l = (ListView)parent; + RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position); + mCurSelected = mi; + Intent intent = new Intent(); + intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid); + intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName); + intent.setClass(getContext(), RunningServiceDetails.class); + getContext().startActivity(intent); + } + + public void onMovedToScrapHeap(View view) { + mActiveItems.remove(view); + } + + public RunningProcessesView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) { + mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); + mState = (RunningState)nonConfigurationInstace; + if (mState == null) { + mState = new RunningState(); + } + LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.running_processes_view, this); + mListView = (ListView)findViewById(android.R.id.list); + View emptyView = findViewById(com.android.internal.R.id.empty); + if (emptyView != null) { + mListView.setEmptyView(emptyView); + } + mListView.setOnItemClickListener(this); + mListView.setAdapter(new ServiceListAdapter(mState)); + mColorBar = (LinearColorBar)findViewById(R.id.color_bar); + mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText); + mForegroundProcessText = (TextView)findViewById(R.id.foregroundText); + + // Magic! Implementation detail! Don't count on this! + SECONDARY_SERVER_MEM = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + } + + public void doPause() { + mHandler.removeMessages(MSG_UPDATE_TIMES); + if (mBackgroundThread != null) { + mBackgroundThread.quit(); + mBackgroundThread = null; + mBackgroundHandler = null; + } + } + + 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); + } + + public Object doRetainNonConfigurationInstance() { + return mState; + } +} diff --git a/src/com/android/settings/applications/RunningProcessesViewOld.java b/src/com/android/settings/applications/RunningProcessesViewOld.java new file mode 100644 index 00000000000..dc7302c7a1f --- /dev/null +++ b/src/com/android/settings/applications/RunningProcessesViewOld.java @@ -0,0 +1,538 @@ +/* + * Copyright (C) 2010 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.applications; + +import com.android.settings.R; + +import android.app.ActivityManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentSender; +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; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.TextView; + +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +public class RunningProcessesViewOld extends FrameLayout + implements AdapterView.OnItemClickListener { + + 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 int MSG_REFRESH_UI = 3; + + static final long TIME_UPDATE_DELAY = 1000; + static final long CONTENTS_UPDATE_DELAY = 2000; + + // Memory pages are 4K. + static final long PAGE_SIZE = 4*1024; + + long SECONDARY_SERVER_MEM; + + final HashMap mActiveItems = new HashMap(); + + ActivityManager mAm; + + RunningState mState; + + StringBuilder mBuilder = new StringBuilder(128); + + RunningState.BaseItem mCurSelected; + + int mProcessBgColor; + + ListView mListView; + LinearColorBar mColorBar; + TextView mBackgroundProcessText; + TextView mForegroundProcessText; + + int mLastNumBackgroundProcesses = -1; + int mLastNumForegroundProcesses = -1; + int mLastNumServiceProcesses = -1; + long mLastBackgroundProcessMemory = -1; + long mLastForegroundProcessMemory = -1; + long mLastServiceProcessMemory = -1; + long mLastAvailMemory = -1; + + Dialog mCurDialog; + + byte[] mBuffer = new byte[1024]; + + class ActiveItem { + View mRootView; + RunningState.BaseItem mItem; + ActivityManager.RunningServiceInfo mService; + ViewHolder mHolder; + long mFirstRunTime; + + void updateTime(Context context) { + if (mItem.mIsProcess) { + String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; + if (!size.equals(mItem.mCurSizeStr)) { + mItem.mCurSizeStr = size; + mHolder.size.setText(size); + } + } else { + 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 ViewHolder { + ImageView separator; + ImageView icon; + TextView name; + TextView description; + TextView size; + } + + static class TimeTicker extends TextView { + public TimeTicker(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + class ServiceListAdapter extends BaseAdapter { + final RunningState mState; + final LayoutInflater mInflater; + ArrayList mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList newItems = mState.getCurrentItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList(); + } + } + + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return mItems.get(position).hashCode(); + } + + public boolean areAllItemsEnabled() { + return false; + } + + public boolean isEnabled(int position) { + return !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.description = (TextView)v.findViewById(R.id.description); + h.size = (TextView)v.findViewById(R.id.size); + v.setTag(h); + return v; + } + + public void bindView(View view, int position) { + synchronized (mState.mLock) { + ViewHolder vh = (ViewHolder) view.getTag(); + if (position >= mItems.size()) { + // List must have changed since we last reported its + // size... ignore here, we will be doing a data changed + // to refresh the entire list. + return; + } + RunningState.BaseItem item = mItems.get(position); + vh.name.setText(item.mDisplayLabel); + vh.separator.setVisibility(item.mNeedDivider + ? View.VISIBLE : View.INVISIBLE); + ActiveItem ai = new ActiveItem(); + ai.mRootView = view; + ai.mItem = item; + ai.mHolder = vh; + ai.mFirstRunTime = item.mActiveSince; + vh.description.setText(item.mDescription); + if (item.mIsProcess) { + view.setBackgroundColor(mProcessBgColor); + vh.icon.setImageDrawable(null); + vh.icon.setVisibility(View.GONE); + vh.description.setText(item.mDescription); + item.mCurSizeStr = null; + } else { + view.setBackgroundDrawable(null); + vh.icon.setImageDrawable(item.mPackageInfo.loadIcon( + getContext().getPackageManager())); + vh.icon.setVisibility(View.VISIBLE); + vh.description.setText(item.mDescription); + ai.mFirstRunTime = item.mActiveSince; + } + ai.updateTime(getContext()); + mActiveItems.put(view, ai); + } + } + } + + public static class LinearColorBar extends LinearLayout { + private float mRedRatio; + private float mYellowRatio; + private float mGreenRatio; + + final Rect mRect = new Rect(); + final Paint mPaint = new Paint(); + + public LinearColorBar(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + mPaint.setStyle(Paint.Style.FILL); + } + + public void setRatios(float red, float yellow, float green) { + mRedRatio = red; + mYellowRatio = yellow; + mGreenRatio = green; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + mRect.top = 0; + mRect.bottom = getHeight(); + + int left = 0; + + int right = left + (int)(width*mRedRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffff8080); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + (int)(width*mYellowRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffffff00); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + width; + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xff80ff80); + canvas.drawRect(mRect, mPaint); + } + } + } + + 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 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()); + } + 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) { + return false; + } + for (int i=0; i= '0' && buffer[index] <= '9') { + int start = index; + index++; + while (index < buffer.length && buffer[index] >= '0' + && buffer[index] <= '9') { + index++; + } + String str = new String(buffer, 0, start, index-start); + return ((long)Integer.parseInt(str)) * 1024; + } + index++; + } + return 0; + } + + private long readAvailMem() { + try { + long memFree = 0; + long memCached = 0; + FileInputStream is = new FileInputStream("/proc/meminfo"); + int len = is.read(mBuffer); + is.close(); + final int BUFLEN = mBuffer.length; + for (int i=0; i parent, View v, int position, long id) { + ListView l = (ListView)parent; + RunningState.BaseItem bi = (RunningState.BaseItem)l.getAdapter().getItem(position); + mCurSelected = bi; + } + + public void onMovedToScrapHeap(View view) { + mActiveItems.remove(view); + } + + public RunningProcessesViewOld(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void doCreate(Bundle savedInstanceState, Object nonConfigurationInstace) { + mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE); + mState = (RunningState)nonConfigurationInstace; + if (mState == null) { + mState = new RunningState(); + } + mProcessBgColor = 0xff505050; + LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(R.layout.running_processes_view, this); + mListView = (ListView)findViewById(android.R.id.list); + View emptyView = findViewById(com.android.internal.R.id.empty); + if (emptyView != null) { + mListView.setEmptyView(emptyView); + } + mListView.setOnItemClickListener(this); + mListView.setDivider(null); + mListView.setAdapter(new ServiceListAdapter(mState)); + mColorBar = (LinearColorBar)findViewById(R.id.color_bar); + mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText); + mForegroundProcessText = (TextView)findViewById(R.id.foregroundText); + + // Magic! Implementation detail! Don't count on this! + SECONDARY_SERVER_MEM = + Integer.valueOf(SystemProperties.get("ro.SECONDARY_SERVER_MEM"))*PAGE_SIZE; + } + + public void doPause() { + mHandler.removeMessages(MSG_UPDATE_TIMES); + if (mBackgroundThread != null) { + mBackgroundThread.quit(); + mBackgroundThread = null; + mBackgroundHandler = null; + } + } + + 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); + } + + public Object doRetainNonConfigurationInstance() { + return mState; + } +} diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java new file mode 100644 index 00000000000..f33625e7822 --- /dev/null +++ b/src/com/android/settings/applications/RunningServiceDetails.java @@ -0,0 +1,390 @@ +package com.android.settings.applications; + +import com.android.settings.R; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.PendingIntent; +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ServiceInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.List; + +public class RunningServiceDetails extends Activity { + 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; + + RunningState mState; + + int mUid; + String mProcessName; + + RunningState.MergedItem mMergedItem; + + ViewGroup mAllDetails; + ViewGroup mSnippet; + RunningProcessesView.ActiveItem mSnippetActiveItem; + RunningProcessesView.ViewHolder mSnippetViewHolder; + + TextView mServicesHeader; + TextView mProcessesHeader; + final ArrayList mActiveDetails = new ArrayList(); + + class ActiveDetail implements View.OnClickListener { + View mRootView; + RunningProcessesView.ActiveItem mActiveItem; + RunningProcessesView.ViewHolder mViewHolder; + PendingIntent mManageIntent; + + public void onClick(View v) { + if (mManageIntent != null) { + try { + startIntentSender(mManageIntent.getIntentSender(), null, + Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, + Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0); + } catch (IntentSender.SendIntentException e) { + Log.w(TAG, e); + } catch (IllegalArgumentException e) { + Log.w(TAG, e); + } catch (ActivityNotFoundException e) { + Log.w(TAG, e); + } + } else { + RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem; + stopService(new Intent().setComponent(si.mRunningService.service)); + if (mMergedItem == null || mMergedItem.mServices.size() <= 1) { + // If there was only one service, we are finishing it, + // so no reason for the UI to stick around. + finish(); + } else { + if (mBackgroundHandler != null) { + mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); + } + } + } + } + } + + 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 newItems = mState.getCurrentMergedItems(); + if (newItems != null) { + for (int i=0; i=0; i--) { + mAllDetails.removeView(mActiveDetails.get(i).mRootView); + } + mActiveDetails.clear(); + + if (mServicesHeader != null) { + mAllDetails.removeView(mServicesHeader); + mServicesHeader = null; + } + + if (mProcessesHeader != null) { + mAllDetails.removeView(mProcessesHeader); + mProcessesHeader = null; + } + + if (mMergedItem != null) { + for (int i=0; i mActiveItems = new HashMap(); + + ActivityManager mAm; + + RunningState mState; + + StringBuilder mBuilder = new StringBuilder(128); + + RunningState.BaseItem mCurSelected; + + int mProcessBgColor; + + LinearColorBar mColorBar; + TextView mBackgroundProcessText; + TextView mForegroundProcessText; + + int mLastNumBackgroundProcesses = -1; + int mLastNumForegroundProcesses = -1; + int mLastNumServiceProcesses = -1; + long mLastBackgroundProcessMemory = -1; + long mLastForegroundProcessMemory = -1; + long mLastServiceProcessMemory = -1; + long mLastAvailMemory = -1; + + Dialog mCurDialog; + + byte[] mBuffer = new byte[1024]; + + class ActiveItem { + View mRootView; + RunningState.BaseItem mItem; + ActivityManager.RunningServiceInfo mService; + ViewHolder mHolder; + long mFirstRunTime; + + void updateTime(Context context) { + if (mItem.mIsProcess) { + String size = mItem.mSizeStr != null ? mItem.mSizeStr : ""; + if (!size.equals(mItem.mCurSizeStr)) { + mItem.mCurSizeStr = size; + mHolder.size.setText(size); + } + } else { + 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 ViewHolder { + ImageView separator; + ImageView icon; + TextView name; + TextView description; + TextView size; + } + + static class TimeTicker extends TextView { + public TimeTicker(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + class ServiceListAdapter extends BaseAdapter { + final RunningState mState; + final LayoutInflater mInflater; + ArrayList mItems; + + ServiceListAdapter(RunningState state) { + mState = state; + mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + refreshItems(); + } + + void refreshItems() { + ArrayList newItems = mState.getCurrentItems(); + if (mItems != newItems) { + mItems = newItems; + } + if (mItems == null) { + mItems = new ArrayList(); + } + } + + public boolean hasStableIds() { + return true; + } + + public int getCount() { + return mItems.size(); + } + + public Object getItem(int position) { + return mItems.get(position); + } + + public long getItemId(int position) { + return mItems.get(position).hashCode(); + } + + public boolean areAllItemsEnabled() { + return false; + } + + public boolean isEnabled(int position) { + return !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.description = (TextView)v.findViewById(R.id.description); + h.size = (TextView)v.findViewById(R.id.size); + v.setTag(h); + return v; + } + + public void bindView(View view, int position) { + synchronized (mState.mLock) { + ViewHolder vh = (ViewHolder) view.getTag(); + if (position >= mItems.size()) { + // List must have changed since we last reported its + // size... ignore here, we will be doing a data changed + // to refresh the entire list. + return; + } + RunningState.BaseItem item = mItems.get(position); + vh.name.setText(item.mDisplayLabel); + vh.separator.setVisibility(item.mNeedDivider + ? View.VISIBLE : View.INVISIBLE); + ActiveItem ai = new ActiveItem(); + ai.mRootView = view; + ai.mItem = item; + ai.mHolder = vh; + ai.mFirstRunTime = item.mActiveSince; + vh.description.setText(item.mDescription); + if (item.mIsProcess) { + view.setBackgroundColor(mProcessBgColor); + vh.icon.setImageDrawable(null); + vh.icon.setVisibility(View.GONE); + vh.description.setText(item.mDescription); + item.mCurSizeStr = null; + } else { + view.setBackgroundDrawable(null); + vh.icon.setImageDrawable(item.mPackageInfo.loadIcon(getPackageManager())); + vh.icon.setVisibility(View.VISIBLE); + vh.description.setText(item.mDescription); + ai.mFirstRunTime = item.mActiveSince; + } + ai.updateTime(RunningServices.this); + mActiveItems.put(view, ai); + } + } + } + + public static class LinearColorBar extends LinearLayout { + private float mRedRatio; + private float mYellowRatio; + private float mGreenRatio; + + final Rect mRect = new Rect(); + final Paint mPaint = new Paint(); + + public LinearColorBar(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + mPaint.setStyle(Paint.Style.FILL); + } + + public void setRatios(float red, float yellow, float green) { + mRedRatio = red; + mYellowRatio = yellow; + mGreenRatio = green; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int width = getWidth(); + mRect.top = 0; + mRect.bottom = getHeight(); + + int left = 0; + + int right = left + (int)(width*mRedRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffff8080); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + (int)(width*mYellowRatio); + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xffffff00); + canvas.drawRect(mRect, mPaint); + width -= (right-left); + left = right; + } + + right = left + width; + if (left < right) { + mRect.left = left; + mRect.right = right; + mPaint.setColor(0xff80ff80); + canvas.drawRect(mRect, mPaint); + } + } + } + + 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(RunningServices.this, 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 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(RunningServices.this); + } + 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) { + return false; + } + for (int i=0; i= '0' && buffer[index] <= '9') { + int start = index; + index++; + while (index < buffer.length && buffer[index] >= '0' + && buffer[index] <= '9') { + index++; + } + String str = new String(buffer, 0, start, index-start); + return ((long)Integer.parseInt(str)) * 1024; + } + index++; + } + return 0; + } + + private long readAvailMem() { + try { + long memFree = 0; + long memCached = 0; + FileInputStream is = new FileInputStream("/proc/meminfo"); + int len = is.read(mBuffer); + is.close(); + final int BUFLEN = mBuffer.length; + for (int i=0; i> mProcesses + = new SparseArray>(); + final SparseArray mActiveProcesses + = new SparseArray(); + final ServiceProcessComparator mServiceProcessComparator + = new ServiceProcessComparator(); + + // Temporary for finding process dependencies. + final SparseArray mRunningProcesses + = new SparseArray(); + + final ArrayList mProcessItems = new ArrayList(); + final ArrayList mAllProcessItems = new ArrayList(); + + int mSequence = 0; + + // ----- following protected by mLock ----- + + // Lock for protecting the state that will be shared between the + // background update thread and the UI thread. + final Object mLock = new Object(); + + ArrayList mItems = new ArrayList(); + ArrayList mMergedItems = new ArrayList(); + + int mNumBackgroundProcesses; + long mBackgroundProcessMemory; + int mNumForegroundProcesses; + long mForegroundProcessMemory; + int mNumServiceProcesses; + long mServiceProcessMemory; + + static class BaseItem { + final boolean mIsProcess; + + PackageItemInfo mPackageInfo; + CharSequence mDisplayLabel; + String mLabel; + String mDescription; + + int mCurSeq; + + long mActiveSince; + long mSize; + String mSizeStr; + String mCurSizeStr; + boolean mNeedDivider; + + public BaseItem(boolean isProcess) { + mIsProcess = isProcess; + } + } + + static class ServiceItem extends BaseItem { + ActivityManager.RunningServiceInfo mRunningService; + ServiceInfo mServiceInfo; + boolean mShownAsStarted; + + MergedItem mMergedItem; + + public ServiceItem() { + super(false); + } + } + + static class ProcessItem extends BaseItem { + final HashMap mServices + = new HashMap(); + final SparseArray mDependentProcesses + = new SparseArray(); + + final int mUid; + final String mProcessName; + int mPid; + + ProcessItem mClient; + int mLastNumDependentProcesses; + + int mRunningSeq; + ActivityManager.RunningAppProcessInfo mRunningProcessInfo; + + // Purely for sorting. + boolean mIsSystem; + boolean mIsStarted; + long mActiveSince; + + public ProcessItem(Context context, int uid, String processName) { + super(true); + mDescription = context.getResources().getString( + R.string.service_process_name, processName); + mUid = uid; + mProcessName = processName; + } + + void ensureLabel(PackageManager pm) { + if (mLabel != null) { + return; + } + + try { + ApplicationInfo ai = pm.getApplicationInfo(mProcessName, 0); + if (ai.uid == mUid) { + mDisplayLabel = ai.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + mPackageInfo = ai; + return; + } + } catch (PackageManager.NameNotFoundException e) { + } + + // If we couldn't get information about the overall + // process, try to find something about the uid. + String[] pkgs = pm.getPackagesForUid(mUid); + + // If there is one package with this uid, that is what we want. + if (pkgs.length == 1) { + try { + ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); + mDisplayLabel = ai.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + mPackageInfo = ai; + return; + } catch (PackageManager.NameNotFoundException e) { + } + } + + // If there are multiple, see if one gives us the official name + // for this uid. + 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) { + mDisplayLabel = nm; + mLabel = nm.toString(); + mPackageInfo = pi.applicationInfo; + return; + } + } + } catch (PackageManager.NameNotFoundException e) { + } + } + + // If still don't have anything to display, just use the + // service info. + if (mServices.size() > 0) { + mPackageInfo = mServices.values().iterator().next() + .mServiceInfo.applicationInfo; + mDisplayLabel = mPackageInfo.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + return; + } + + // Finally... whatever, just pick the first package's name. + try { + ApplicationInfo ai = pm.getApplicationInfo(pkgs[0], 0); + mDisplayLabel = ai.loadLabel(pm); + mLabel = mDisplayLabel.toString(); + mPackageInfo = ai; + return; + } catch (PackageManager.NameNotFoundException e) { + } + } + + boolean updateService(Context context, + ActivityManager.RunningServiceInfo service) { + final PackageManager pm = context.getPackageManager(); + + 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) { + } + si.mDisplayLabel = makeLabel(pm, + si.mRunningService.service.getClassName(), si.mServiceInfo); + mLabel = mDisplayLabel != null ? mDisplayLabel.toString() : null; + si.mPackageInfo = si.mServiceInfo.applicationInfo; + 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; + } + if (service.clientPackage != null && service.clientLabel != 0) { + if (si.mShownAsStarted) { + si.mShownAsStarted = false; + changed = true; + } + try { + Resources clientr = pm.getResourcesForApplication(service.clientPackage); + String label = clientr.getString(service.clientLabel); + si.mDescription = context.getResources().getString( + R.string.service_client_name, label); + } catch (PackageManager.NameNotFoundException e) { + si.mDescription = null; + } + } else { + if (!si.mShownAsStarted) { + si.mShownAsStarted = true; + changed = true; + } + si.mDescription = context.getResources().getString( + R.string.service_started_by_app); + } + + return changed; + } + + boolean updateSize(Context context, Debug.MemoryInfo mem, int curSeq) { + mSize = ((long)mem.getTotalPss()) * 1024; + if (mCurSeq == curSeq) { + String sizeStr = Formatter.formatShortFileSize( + context, mSize); + if (!sizeStr.equals(mSizeStr)){ + mSizeStr = sizeStr; + // We update this on the second tick where we update just + // the text in the current items, so no need to say we + // changed here. + return false; + } + } + return false; + } + + boolean buildDependencyChain(Context context, PackageManager pm, int curSeq) { + final int NP = mDependentProcesses.size(); + boolean changed = false; + for (int i=0; i dest, + ArrayList destProc) { + final int NP = mDependentProcesses.size(); + for (int i=0; i 0) { + destProc.add(proc); + } + } + } + } + + static class MergedItem extends BaseItem { + ProcessItem mProcess; + final ArrayList mOtherProcesses = new ArrayList(); + final ArrayList mServices = new ArrayList(); + + MergedItem() { + super(false); + } + + boolean update(Context context) { + mPackageInfo = mProcess.mPackageInfo; + mDisplayLabel = mProcess.mDisplayLabel; + mLabel = mProcess.mLabel; + + int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size(); + int numServices = mServices.size(); + int resid = R.string.running_processes_item_description_s_s; + if (numProcesses != 1) { + resid = numServices != 1 + ? R.string.running_processes_item_description_p_p + : R.string.running_processes_item_description_p_s; + } else if (numServices != 1) { + resid = R.string.running_processes_item_description_s_p; + } + mDescription = context.getResources().getString(resid, numProcesses, numServices); + + mActiveSince = -1; + for (int i=0; i= 0 && mActiveSince < si.mActiveSince) { + mActiveSince = si.mActiveSince; + } + } + + return false; + } + + boolean updateSize(Context context) { + mSize = mProcess.mSize; + for (int i=0; i { + public int compare(ProcessItem object1, ProcessItem object2) { + if (object1.mIsStarted != object2.mIsStarted) { + // Non-started processes go last. + return object1.mIsStarted ? -1 : 1; + } + if (object1.mIsSystem != object2.mIsSystem) { + // System processes go below non-system. + return object1.mIsSystem ? 1 : -1; + } + if (object1.mActiveSince != object2.mActiveSince) { + // Remaining ones are sorted with the longest running + // services last. + return (object1.mActiveSince > object2.mActiveSince) ? -1 : 1; + } + return 0; + } + } + + static CharSequence makeLabel(PackageManager pm, + String className, PackageItemInfo item) { + if (item != null && (item.labelRes != 0 + || item.nonLocalizedLabel != null)) { + CharSequence label = item.loadLabel(pm); + if (label != null) { + return label; + } + } + + String label = className; + int tail = label.lastIndexOf('.'); + if (tail >= 0) { + label = label.substring(tail+1, label.length()); + } + return label; + } + + boolean update(Context context, ActivityManager am) { + final PackageManager pm = context.getPackageManager(); + + mSequence++; + + boolean changed = false; + + List services + = am.getRunningServices(RunningServices.MAX_SERVICES); + final int NS = services != null ? services.size() : 0; + for (int i=0; i procs = mProcesses.get(si.uid); + if (procs == null) { + procs = new HashMap(); + mProcesses.put(si.uid, procs); + } + ProcessItem proc = procs.get(si.process); + if (proc == null) { + changed = true; + proc = new ProcessItem(context, si.uid, si.process); + procs.put(si.process, proc); + } + + if (proc.mCurSeq != mSequence) { + int pid = si.restarting == 0 ? si.pid : 0; + if (pid != proc.mPid) { + changed = true; + if (proc.mPid != pid) { + if (proc.mPid != 0) { + mActiveProcesses.remove(proc.mPid); + } + if (pid != 0) { + mActiveProcesses.put(pid, proc); + } + proc.mPid = pid; + } + } + proc.mDependentProcesses.clear(); + proc.mCurSeq = mSequence; + } + changed |= proc.updateService(context, si); + } + + // Now update the map of other processes that are running (but + // don't have services actively running inside them). + List processes + = am.getRunningAppProcesses(); + final int NP = processes != null ? processes.size() : 0; + for (int i=0; i procs = mProcesses.valueAt(i); + Iterator pit = procs.values().iterator(); + while (pit.hasNext()) { + ProcessItem pi = pit.next(); + if (pi.mCurSeq == mSequence) { + pi.ensureLabel(pm); + if (pi.mPid == 0) { + // Sanity: a non-process can't be dependent on + // anything. + pi.mDependentProcesses.clear(); + } + } else { + changed = true; + pit.remove(); + if (procs.size() == 0) { + mProcesses.remove(mProcesses.keyAt(i)); + } + if (pi.mPid != 0) { + mActiveProcesses.remove(pi.mPid); + } + continue; + } + Iterator sit = pi.mServices.values().iterator(); + while (sit.hasNext()) { + ServiceItem si = sit.next(); + if (si.mCurSeq != mSequence) { + changed = true; + sit.remove(); + } + } + } + } + + if (changed) { + // First determine an order for the services. + ArrayList sortedProcesses = new ArrayList(); + for (int i=0; i si.mRunningService.activeSince) { + pi.mActiveSince = si.mRunningService.activeSince; + } + } + } + sortedProcesses.add(pi); + } + } + + Collections.sort(sortedProcesses, mServiceProcessComparator); + + ArrayList newItems = new ArrayList(); + ArrayList newMergedItems = new ArrayList(); + mProcessItems.clear(); + for (int i=0; i 0) { + mProcessItems.add(pi); + } + + // Now add the services running in it. + MergedItem mergedItem = null; + boolean haveAllMerged = false; + boolean needDivider = false; + for (ServiceItem si : pi.mServices.values()) { + si.mNeedDivider = needDivider; + needDivider = true; + newItems.add(si); + if (si.mMergedItem != null) { + if (mergedItem != null && mergedItem != si.mMergedItem) { + haveAllMerged = false; + } + mergedItem = si.mMergedItem; + } else { + haveAllMerged = false; + } + } + + if (!haveAllMerged || mergedItem == null + || mergedItem.mServices.size() != pi.mServices.size()) { + // Whoops, we need to build a new MergedItem! + mergedItem = new MergedItem(); + for (ServiceItem si : pi.mServices.values()) { + mergedItem.mServices.add(si); + si.mMergedItem = mergedItem; + } + mergedItem.mProcess = pi; + mergedItem.mOtherProcesses.clear(); + for (int mpi=firstProc; mpi<(mProcessItems.size()-1); mpi++) { + mergedItem.mOtherProcesses.add(mProcessItems.get(mpi)); + } + } + + mergedItem.update(context); + newMergedItems.add(mergedItem); + } + synchronized (mLock) { + mItems = newItems; + mMergedItems = newMergedItems; + } + } + + // Count number of interesting other (non-active) processes, and + // build a list of all processes we will retrieve memory for. + mAllProcessItems.clear(); + mAllProcessItems.addAll(mProcessItems); + int numBackgroundProcesses = 0; + int numForegroundProcesses = 0; + int numServiceProcesses = 0; + NRP = mRunningProcesses.size(); + for (int i=0; i= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { + numBackgroundProcesses++; + mAllProcessItems.add(proc); + } else if (proc.mRunningProcessInfo.importance <= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { + numForegroundProcesses++; + mAllProcessItems.add(proc); + } else { + Log.i(RunningServices.TAG, "Unknown non-service process: " + + proc.mProcessName + " #" + proc.mPid); + } + } else { + numServiceProcesses++; + } + } + + long backgroundProcessMemory = 0; + long foregroundProcessMemory = 0; + long serviceProcessMemory = 0; + try { + final int numProc = mAllProcessItems.size(); + int[] pids = new int[numProc]; + for (int i=0; i=0; i--) { + ProcessItem proc = mAllProcessItems.get(i); + changed |= proc.updateSize(context, mem[i], mSequence); + if (proc.mCurSeq == mSequence) { + serviceProcessMemory += proc.mSize; + } else if (proc.mRunningProcessInfo.importance >= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) { + backgroundProcessMemory += proc.mSize; + } else if (proc.mRunningProcessInfo.importance <= + ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) { + foregroundProcessMemory += proc.mSize; + } + } + } catch (RemoteException e) { + } + + for (int i=0; i getCurrentItems() { + synchronized (mLock) { + return mItems; + } + } + + ArrayList getCurrentMergedItems() { + synchronized (mLock) { + return mMergedItems; + } + } +} diff --git a/src/com/android/settings/fuelgauge/PowerUsageDetail.java b/src/com/android/settings/fuelgauge/PowerUsageDetail.java index 4db968a2ea7..cc112f81bfd 100644 --- a/src/com/android/settings/fuelgauge/PowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerUsageDetail.java @@ -40,9 +40,9 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; -import com.android.settings.InstalledAppDetails; -import com.android.settings.ManageApplications; import com.android.settings.R; +import com.android.settings.applications.InstalledAppDetails; +import com.android.settings.applications.ManageApplications; public class PowerUsageDetail extends Activity implements Button.OnClickListener { @@ -248,9 +248,9 @@ public class PowerUsageDetail extends Activity implements Button.OnClickListener startActivity(new Intent(Settings.ACTION_WIRELESS_SETTINGS)); break; case ACTION_APP_DETAILS: - Intent intent = new Intent(Intent.ACTION_VIEW); + Intent intent = new Intent(Intent.ACTION_VIEW, + Uri.fromParts("package", mPackages[0], null)); intent.setClass(this, InstalledAppDetails.class); - intent.putExtra(ManageApplications.APP_PKG_NAME, mPackages[0]); startActivity(intent); break; case ACTION_SECURITY_SETTINGS: