Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.applications;
     18 
     19 import com.android.internal.util.MemInfoReader;
     20 import com.android.settings.R;
     21 
     22 import android.app.ActivityManager;
     23 import android.app.Dialog;
     24 import android.app.Fragment;
     25 import android.content.Context;
     26 import android.content.pm.PackageManager;
     27 import android.os.Bundle;
     28 import android.os.SystemClock;
     29 import android.preference.PreferenceActivity;
     30 import android.text.format.DateUtils;
     31 import android.text.format.Formatter;
     32 import android.util.AttributeSet;
     33 import android.view.LayoutInflater;
     34 import android.view.View;
     35 import android.view.ViewGroup;
     36 import android.widget.AdapterView;
     37 import android.widget.BaseAdapter;
     38 import android.widget.FrameLayout;
     39 import android.widget.ImageView;
     40 import android.widget.ListView;
     41 import android.widget.TextView;
     42 import android.widget.AbsListView.RecyclerListener;
     43 
     44 import java.util.ArrayList;
     45 import java.util.HashMap;
     46 import java.util.Iterator;
     47 
     48 public class RunningProcessesView extends FrameLayout
     49         implements AdapterView.OnItemClickListener, RecyclerListener,
     50         RunningState.OnRefreshUiListener {
     51 
     52     long SECONDARY_SERVER_MEM;
     53 
     54     final HashMap<View, ActiveItem> mActiveItems = new HashMap<View, ActiveItem>();
     55 
     56     ActivityManager mAm;
     57 
     58     RunningState mState;
     59 
     60     Fragment mOwner;
     61 
     62     Runnable mDataAvail;
     63 
     64     StringBuilder mBuilder = new StringBuilder(128);
     65 
     66     RunningState.BaseItem mCurSelected;
     67 
     68     ListView mListView;
     69     ServiceListAdapter mAdapter;
     70     LinearColorBar mColorBar;
     71     TextView mBackgroundProcessText;
     72     TextView mForegroundProcessText;
     73 
     74     int mLastNumBackgroundProcesses = -1;
     75     int mLastNumForegroundProcesses = -1;
     76     int mLastNumServiceProcesses = -1;
     77     long mLastBackgroundProcessMemory = -1;
     78     long mLastForegroundProcessMemory = -1;
     79     long mLastServiceProcessMemory = -1;
     80     long mLastAvailMemory = -1;
     81 
     82     Dialog mCurDialog;
     83 
     84     MemInfoReader mMemInfoReader = new MemInfoReader();
     85 
     86     public static class ActiveItem {
     87         View mRootView;
     88         RunningState.BaseItem mItem;
     89         ActivityManager.RunningServiceInfo mService;
     90         ViewHolder mHolder;
     91         long mFirstRunTime;
     92         boolean mSetBackground;
     93 
     94         void updateTime(Context context, StringBuilder builder) {
     95             TextView uptimeView = null;
     96 
     97             if (mItem instanceof RunningState.ServiceItem) {
     98                 // If we are displaying a service, then the service
     99                 // uptime goes at the top.
    100                 uptimeView = mHolder.size;
    101 
    102             } else {
    103                 String size = mItem.mSizeStr != null ? mItem.mSizeStr : "";
    104                 if (!size.equals(mItem.mCurSizeStr)) {
    105                     mItem.mCurSizeStr = size;
    106                     mHolder.size.setText(size);
    107                 }
    108 
    109                 if (mItem.mBackground) {
    110                     // This is a background process; no uptime.
    111                     if (!mSetBackground) {
    112                         mSetBackground = true;
    113                         mHolder.uptime.setText("");
    114                     }
    115                 } else if (mItem instanceof RunningState.MergedItem) {
    116                     // This item represents both services and processes,
    117                     // so show the service uptime below.
    118                     uptimeView = mHolder.uptime;
    119                 }
    120             }
    121 
    122             if (uptimeView != null) {
    123                 mSetBackground = false;
    124                 if (mFirstRunTime >= 0) {
    125                     //Log.i("foo", "Time for " + mItem.mDisplayLabel
    126                     //        + ": " + (SystemClock.uptimeMillis()-mFirstRunTime));
    127                     uptimeView.setText(DateUtils.formatElapsedTime(builder,
    128                             (SystemClock.elapsedRealtime()-mFirstRunTime)/1000));
    129                 } else {
    130                     boolean isService = false;
    131                     if (mItem instanceof RunningState.MergedItem) {
    132                         isService = ((RunningState.MergedItem)mItem).mServices.size() > 0;
    133                     }
    134                     if (isService) {
    135                         uptimeView.setText(context.getResources().getText(
    136                                 R.string.service_restarting));
    137                     } else {
    138                         uptimeView.setText("");
    139                     }
    140                 }
    141             }
    142         }
    143     }
    144 
    145     public static class ViewHolder {
    146         public View rootView;
    147         public ImageView icon;
    148         public TextView name;
    149         public TextView description;
    150         public TextView size;
    151         public TextView uptime;
    152 
    153         public ViewHolder(View v) {
    154             rootView = v;
    155             icon = (ImageView)v.findViewById(R.id.icon);
    156             name = (TextView)v.findViewById(R.id.name);
    157             description = (TextView)v.findViewById(R.id.description);
    158             size = (TextView)v.findViewById(R.id.size);
    159             uptime = (TextView)v.findViewById(R.id.uptime);
    160             v.setTag(this);
    161         }
    162 
    163         public ActiveItem bind(RunningState state, RunningState.BaseItem item,
    164                 StringBuilder builder) {
    165             synchronized (state.mLock) {
    166                 PackageManager pm = rootView.getContext().getPackageManager();
    167                 if (item.mPackageInfo == null && item instanceof RunningState.MergedItem) {
    168                     // Items for background processes don't normally load
    169                     // their labels for performance reasons.  Do it now.
    170                     ((RunningState.MergedItem)item).mProcess.ensureLabel(pm);
    171                     item.mPackageInfo = ((RunningState.MergedItem)item).mProcess.mPackageInfo;
    172                     item.mDisplayLabel = ((RunningState.MergedItem)item).mProcess.mDisplayLabel;
    173                 }
    174                 name.setText(item.mDisplayLabel);
    175                 ActiveItem ai = new ActiveItem();
    176                 ai.mRootView = rootView;
    177                 ai.mItem = item;
    178                 ai.mHolder = this;
    179                 ai.mFirstRunTime = item.mActiveSince;
    180                 if (item.mBackground) {
    181                     description.setText(rootView.getContext().getText(R.string.cached));
    182                 } else {
    183                     description.setText(item.mDescription);
    184                 }
    185                 item.mCurSizeStr = null;
    186                 if (item.mPackageInfo != null) {
    187                     icon.setImageDrawable(item.mPackageInfo.loadIcon(pm));
    188                 }
    189                 icon.setVisibility(View.VISIBLE);
    190                 ai.updateTime(rootView.getContext(), builder);
    191                 return ai;
    192             }
    193         }
    194     }
    195 
    196     static class TimeTicker extends TextView {
    197         public TimeTicker(Context context, AttributeSet attrs) {
    198             super(context, attrs);
    199         }
    200     }
    201 
    202     class ServiceListAdapter extends BaseAdapter {
    203         final RunningState mState;
    204         final LayoutInflater mInflater;
    205         boolean mShowBackground;
    206         ArrayList<RunningState.MergedItem> mItems;
    207 
    208         ServiceListAdapter(RunningState state) {
    209             mState = state;
    210             mInflater = (LayoutInflater)getContext().getSystemService(
    211                     Context.LAYOUT_INFLATER_SERVICE);
    212             refreshItems();
    213         }
    214 
    215         void setShowBackground(boolean showBackground) {
    216             if (mShowBackground != showBackground) {
    217                 mShowBackground = showBackground;
    218                 mState.setWatchingBackgroundItems(showBackground);
    219                 refreshItems();
    220                 notifyDataSetChanged();
    221                 mColorBar.setShowingGreen(mShowBackground);
    222             }
    223         }
    224 
    225         boolean getShowBackground() {
    226             return mShowBackground;
    227         }
    228 
    229         void refreshItems() {
    230             ArrayList<RunningState.MergedItem> newItems =
    231                 mShowBackground ? mState.getCurrentBackgroundItems()
    232                         : mState.getCurrentMergedItems();
    233             if (mItems != newItems) {
    234                 mItems = newItems;
    235             }
    236             if (mItems == null) {
    237                 mItems = new ArrayList<RunningState.MergedItem>();
    238             }
    239         }
    240 
    241         public boolean hasStableIds() {
    242             return true;
    243         }
    244 
    245         public int getCount() {
    246             return mItems.size();
    247         }
    248 
    249         @Override
    250         public boolean isEmpty() {
    251             return mState.hasData() && mItems.size() == 0;
    252         }
    253 
    254         public Object getItem(int position) {
    255             return mItems.get(position);
    256         }
    257 
    258         public long getItemId(int position) {
    259             return mItems.get(position).hashCode();
    260         }
    261 
    262         public boolean areAllItemsEnabled() {
    263             return false;
    264         }
    265 
    266         public boolean isEnabled(int position) {
    267             return !mItems.get(position).mIsProcess;
    268         }
    269 
    270         public View getView(int position, View convertView, ViewGroup parent) {
    271             View v;
    272             if (convertView == null) {
    273                 v = newView(parent);
    274             } else {
    275                 v = convertView;
    276             }
    277             bindView(v, position);
    278             return v;
    279         }
    280 
    281         public View newView(ViewGroup parent) {
    282             View v = mInflater.inflate(R.layout.running_processes_item, parent, false);
    283             new ViewHolder(v);
    284             return v;
    285         }
    286 
    287         public void bindView(View view, int position) {
    288             synchronized (mState.mLock) {
    289                 if (position >= mItems.size()) {
    290                     // List must have changed since we last reported its
    291                     // size...  ignore here, we will be doing a data changed
    292                     // to refresh the entire list.
    293                     return;
    294                 }
    295                 ViewHolder vh = (ViewHolder) view.getTag();
    296                 RunningState.MergedItem item = mItems.get(position);
    297                 ActiveItem ai = vh.bind(mState, item, mBuilder);
    298                 mActiveItems.put(view, ai);
    299             }
    300         }
    301     }
    302 
    303     void refreshUi(boolean dataChanged) {
    304         if (dataChanged) {
    305             ServiceListAdapter adapter = (ServiceListAdapter)(mListView.getAdapter());
    306             adapter.refreshItems();
    307             adapter.notifyDataSetChanged();
    308         }
    309 
    310         if (mDataAvail != null) {
    311             mDataAvail.run();
    312             mDataAvail = null;
    313         }
    314 
    315         // This is the amount of available memory until we start killing
    316         // background services.
    317         mMemInfoReader.readMemInfo();
    318         long availMem = mMemInfoReader.getFreeSize() + mMemInfoReader.getCachedSize()
    319                 - SECONDARY_SERVER_MEM;
    320         if (availMem < 0) {
    321             availMem = 0;
    322         }
    323 
    324         synchronized (mState.mLock) {
    325             if (mLastNumBackgroundProcesses != mState.mNumBackgroundProcesses
    326                     || mLastBackgroundProcessMemory != mState.mBackgroundProcessMemory
    327                     || mLastAvailMemory != availMem) {
    328                 mLastNumBackgroundProcesses = mState.mNumBackgroundProcesses;
    329                 mLastBackgroundProcessMemory = mState.mBackgroundProcessMemory;
    330                 mLastAvailMemory = availMem;
    331                 long freeMem = mLastAvailMemory + mLastBackgroundProcessMemory;
    332                 String sizeStr = Formatter.formatShortFileSize(getContext(), freeMem);
    333                 mBackgroundProcessText.setText(getResources().getString(
    334                         R.string.service_background_processes, sizeStr));
    335                 sizeStr = Formatter.formatShortFileSize(getContext(),
    336                         mMemInfoReader.getTotalSize() - freeMem);
    337                 mForegroundProcessText.setText(getResources().getString(
    338                         R.string.service_foreground_processes, sizeStr));
    339             }
    340             if (mLastNumForegroundProcesses != mState.mNumForegroundProcesses
    341                     || mLastForegroundProcessMemory != mState.mForegroundProcessMemory
    342                     || mLastNumServiceProcesses != mState.mNumServiceProcesses
    343                     || mLastServiceProcessMemory != mState.mServiceProcessMemory) {
    344                 mLastNumForegroundProcesses = mState.mNumForegroundProcesses;
    345                 mLastForegroundProcessMemory = mState.mForegroundProcessMemory;
    346                 mLastNumServiceProcesses = mState.mNumServiceProcesses;
    347                 mLastServiceProcessMemory = mState.mServiceProcessMemory;
    348                 /*
    349                 String sizeStr = Formatter.formatShortFileSize(getContext(),
    350                         mLastForegroundProcessMemory + mLastServiceProcessMemory);
    351                 mForegroundProcessText.setText(getResources().getString(
    352                         R.string.service_foreground_processes, sizeStr));
    353                 */
    354             }
    355 
    356             float totalMem = mMemInfoReader.getTotalSize();
    357             float totalShownMem = availMem + mLastBackgroundProcessMemory
    358                     + mLastServiceProcessMemory;
    359             mColorBar.setRatios((totalMem-totalShownMem)/totalMem,
    360                     mLastServiceProcessMemory/totalMem,
    361                     mLastBackgroundProcessMemory/totalMem);
    362         }
    363     }
    364 
    365     public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
    366         ListView l = (ListView)parent;
    367         RunningState.MergedItem mi = (RunningState.MergedItem)l.getAdapter().getItem(position);
    368         mCurSelected = mi;
    369         startServiceDetailsActivity(mi);
    370     }
    371 
    372     // utility method used to start sub activity
    373     private void startServiceDetailsActivity(RunningState.MergedItem mi) {
    374         if (mOwner != null) {
    375             // start new fragment to display extended information
    376             Bundle args = new Bundle();
    377             args.putInt(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
    378             args.putString(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
    379             args.putBoolean(RunningServiceDetails.KEY_BACKGROUND, mAdapter.mShowBackground);
    380 
    381             PreferenceActivity pa = (PreferenceActivity)mOwner.getActivity();
    382             pa.startPreferencePanel(RunningServiceDetails.class.getName(), args,
    383                     R.string.runningservicedetails_settings_title, null, null, 0);
    384         }
    385     }
    386 
    387     public void onMovedToScrapHeap(View view) {
    388         mActiveItems.remove(view);
    389     }
    390 
    391     public RunningProcessesView(Context context, AttributeSet attrs) {
    392         super(context, attrs);
    393     }
    394 
    395     public void doCreate(Bundle savedInstanceState) {
    396         mAm = (ActivityManager)getContext().getSystemService(Context.ACTIVITY_SERVICE);
    397         mState = RunningState.getInstance(getContext());
    398         LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
    399                 Context.LAYOUT_INFLATER_SERVICE);
    400         inflater.inflate(R.layout.running_processes_view, this);
    401         mListView = (ListView)findViewById(android.R.id.list);
    402         View emptyView = findViewById(com.android.internal.R.id.empty);
    403         if (emptyView != null) {
    404             mListView.setEmptyView(emptyView);
    405         }
    406         mListView.setOnItemClickListener(this);
    407         mListView.setRecyclerListener(this);
    408         mAdapter = new ServiceListAdapter(mState);
    409         mListView.setAdapter(mAdapter);
    410         mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
    411         mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
    412         mBackgroundProcessText.setOnClickListener(new View.OnClickListener() {
    413             @Override
    414             public void onClick(View v) {
    415                 mAdapter.setShowBackground(true);
    416             }
    417         });
    418         mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
    419         mForegroundProcessText.setOnClickListener(new View.OnClickListener() {
    420             @Override
    421             public void onClick(View v) {
    422                 mAdapter.setShowBackground(false);
    423             }
    424         });
    425 
    426         ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
    427         mAm.getMemoryInfo(memInfo);
    428         SECONDARY_SERVER_MEM = memInfo.secondaryServerThreshold;
    429     }
    430 
    431     public void doPause() {
    432         mState.pause();
    433         mDataAvail = null;
    434         mOwner = null;
    435     }
    436 
    437     public boolean doResume(Fragment owner, Runnable dataAvail) {
    438         mOwner = owner;
    439         mState.resume(this);
    440         if (mState.hasData()) {
    441             // If the state already has its data, then let's populate our
    442             // list right now to avoid flicker.
    443             refreshUi(true);
    444             return true;
    445         }
    446         mDataAvail = dataAvail;
    447         return false;
    448     }
    449 
    450     void updateTimes() {
    451         Iterator<ActiveItem> it = mActiveItems.values().iterator();
    452         while (it.hasNext()) {
    453             ActiveItem ai = it.next();
    454             if (ai.mRootView.getWindowToken() == null) {
    455                 // Clean out any dead views, just in case.
    456                 it.remove();
    457                 continue;
    458             }
    459             ai.updateTime(getContext(), mBuilder);
    460         }
    461     }
    462 
    463     @Override
    464     public void onRefreshUi(int what) {
    465         switch (what) {
    466             case REFRESH_TIME:
    467                 updateTimes();
    468                 break;
    469             case REFRESH_DATA:
    470                 refreshUi(false);
    471                 updateTimes();
    472                 break;
    473             case REFRESH_STRUCTURE:
    474                 refreshUi(true);
    475                 updateTimes();
    476                 break;
    477         }
    478     }
    479 }
    480