Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2013 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.launcher3;
     18 
     19 import android.content.ComponentName;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.ServiceConnection;
     23 import android.graphics.Canvas;
     24 import android.graphics.Color;
     25 import android.graphics.Paint;
     26 import android.os.Handler;
     27 import android.os.IBinder;
     28 import android.os.Message;
     29 import android.util.AttributeSet;
     30 import android.util.Log;
     31 import android.util.TypedValue;
     32 import android.view.Gravity;
     33 import android.view.View;
     34 import android.widget.LinearLayout;
     35 import android.widget.TextView;
     36 
     37 public class WeightWatcher extends LinearLayout {
     38     private static final int RAM_GRAPH_RSS_COLOR = 0xFF990000;
     39     private static final int RAM_GRAPH_PSS_COLOR = 0xFF99CC00;
     40     private static final int TEXT_COLOR = 0xFFFFFFFF;
     41     private static final int BACKGROUND_COLOR = 0xc0000000;
     42 
     43     private static final int UPDATE_RATE = 5000;
     44 
     45     private static final int MSG_START = 1;
     46     private static final int MSG_STOP = 2;
     47     private static final int MSG_UPDATE = 3;
     48 
     49     static int indexOf(int[] a, int x) {
     50         for (int i=0; i<a.length; i++) {
     51             if (a[i] == x) return i;
     52         }
     53         return -1;
     54     }
     55 
     56     Handler mHandler = new Handler() {
     57         @Override
     58         public void handleMessage(Message m) {
     59             switch (m.what) {
     60                 case MSG_START:
     61                     mHandler.sendEmptyMessage(MSG_UPDATE);
     62                     break;
     63                 case MSG_STOP:
     64                     mHandler.removeMessages(MSG_UPDATE);
     65                     break;
     66                 case MSG_UPDATE:
     67                     int[] pids = mMemoryService.getTrackedProcesses();
     68 
     69                     final int N = getChildCount();
     70                     if (pids.length != N) initViews();
     71                     else for (int i=0; i<N; i++) {
     72                         ProcessWatcher pw = ((ProcessWatcher) getChildAt(i));
     73                         if (indexOf(pids, pw.getPid()) < 0) {
     74                             initViews();
     75                             break;
     76                         }
     77                         pw.update();
     78                     }
     79                     mHandler.sendEmptyMessageDelayed(MSG_UPDATE, UPDATE_RATE);
     80                     break;
     81             }
     82         }
     83     };
     84     private MemoryTracker mMemoryService;
     85 
     86     public WeightWatcher(Context context, AttributeSet attrs) {
     87         super(context, attrs);
     88 
     89         ServiceConnection connection = new ServiceConnection() {
     90             public void onServiceConnected(ComponentName className, IBinder service) {
     91                 mMemoryService = ((MemoryTracker.MemoryTrackerInterface)service).getService();
     92                 initViews();
     93             }
     94 
     95             public void onServiceDisconnected(ComponentName className) {
     96                 mMemoryService = null;
     97             }
     98         };
     99         context.bindService(new Intent(context, MemoryTracker.class),
    100                 connection, Context.BIND_AUTO_CREATE);
    101 
    102         setOrientation(LinearLayout.VERTICAL);
    103 
    104         setBackgroundColor(BACKGROUND_COLOR);
    105     }
    106 
    107     public void initViews() {
    108         removeAllViews();
    109         int[] processes = mMemoryService.getTrackedProcesses();
    110         for (int i=0; i<processes.length; i++) {
    111             final ProcessWatcher v = new ProcessWatcher(getContext());
    112             v.setPid(processes[i]);
    113             addView(v);
    114         }
    115     }
    116 
    117     public WeightWatcher(Context context) {
    118         this(context, null);
    119     }
    120 
    121     @Override
    122     public void onAttachedToWindow() {
    123         super.onAttachedToWindow();
    124         mHandler.sendEmptyMessage(MSG_START);
    125     }
    126 
    127     @Override
    128     public void onDetachedFromWindow() {
    129         super.onDetachedFromWindow();
    130         mHandler.sendEmptyMessage(MSG_STOP);
    131     }
    132 
    133     public class ProcessWatcher extends LinearLayout {
    134         GraphView mRamGraph;
    135         TextView mText;
    136         int mPid;
    137         private MemoryTracker.ProcessMemInfo mMemInfo;
    138 
    139         public ProcessWatcher(Context context) {
    140             this(context, null);
    141         }
    142 
    143         public ProcessWatcher(Context context, AttributeSet attrs) {
    144             super(context, attrs);
    145 
    146             final float dp = getResources().getDisplayMetrics().density;
    147 
    148             mText = new TextView(getContext());
    149             mText.setTextColor(TEXT_COLOR);
    150             mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, 10 * dp);
    151             mText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
    152 
    153             final int p = (int)(2*dp);
    154             setPadding(p, 0, p, 0);
    155 
    156             mRamGraph = new GraphView(getContext());
    157 
    158             LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
    159                     0,
    160                     (int)(14 * dp),
    161                     1f
    162             );
    163 
    164             addView(mText, params);
    165             params.leftMargin = (int)(4*dp);
    166             params.weight = 0f;
    167             params.width = (int)(200 * dp);
    168             addView(mRamGraph, params);
    169         }
    170 
    171         public void setPid(int pid) {
    172             mPid = pid;
    173             mMemInfo = mMemoryService.getMemInfo(mPid);
    174             if (mMemInfo == null) {
    175                 Log.v("WeightWatcher", "Missing info for pid " + mPid + ", removing view: " + this);
    176                 initViews();
    177             }
    178         }
    179 
    180         public int getPid() {
    181             return mPid;
    182         }
    183 
    184         public String getUptimeString() {
    185             long sec = mMemInfo.getUptime() / 1000;
    186             StringBuilder sb = new StringBuilder();
    187             long days = sec / 86400;
    188             if (days > 0) {
    189                 sec -= days * 86400;
    190                 sb.append(days);
    191                 sb.append("d");
    192             }
    193 
    194             long hours = sec / 3600;
    195             if (hours > 0) {
    196                 sec -= hours * 3600;
    197                 sb.append(hours);
    198                 sb.append("h");
    199             }
    200 
    201             long mins = sec / 60;
    202             if (mins > 0) {
    203                 sec -= mins * 60;
    204                 sb.append(mins);
    205                 sb.append("m");
    206             }
    207 
    208             sb.append(sec);
    209             sb.append("s");
    210             return sb.toString();
    211         }
    212 
    213         public void update() {
    214             //Log.v("WeightWatcher.ProcessWatcher",
    215             //        "MSG_UPDATE pss=" + mMemInfo.currentPss);
    216             mText.setText("(" + mPid
    217                           + (mPid == android.os.Process.myPid()
    218                                 ? "/A"  // app
    219                                 : "/S") // service
    220                           + ") up " + getUptimeString()
    221                           + " P=" + mMemInfo.currentPss
    222                           + " U=" + mMemInfo.currentUss
    223                           );
    224             mRamGraph.invalidate();
    225         }
    226 
    227         public class GraphView extends View {
    228             Paint pssPaint, ussPaint, headPaint;
    229 
    230             public GraphView(Context context, AttributeSet attrs) {
    231                 super(context, attrs);
    232 
    233                 pssPaint = new Paint();
    234                 pssPaint.setColor(RAM_GRAPH_PSS_COLOR);
    235                 ussPaint = new Paint();
    236                 ussPaint.setColor(RAM_GRAPH_RSS_COLOR);
    237                 headPaint = new Paint();
    238                 headPaint.setColor(Color.WHITE);
    239             }
    240 
    241             public GraphView(Context context) {
    242                 this(context, null);
    243             }
    244 
    245             @Override
    246             public void onDraw(Canvas c) {
    247                 int w = c.getWidth();
    248                 int h = c.getHeight();
    249 
    250                 if (mMemInfo == null) return;
    251 
    252                 final int N = mMemInfo.pss.length;
    253                 final float barStep = (float) w / N;
    254                 final float barWidth = Math.max(1, barStep);
    255                 final float scale = (float) h / mMemInfo.max;
    256 
    257                 int i;
    258                 float x;
    259                 for (i=0; i<N; i++) {
    260                     x = i * barStep;
    261                     c.drawRect(x, h - scale * mMemInfo.pss[i], x + barWidth, h, pssPaint);
    262                     c.drawRect(x, h - scale * mMemInfo.uss[i], x + barWidth, h, ussPaint);
    263                 }
    264                 x = mMemInfo.head * barStep;
    265                 c.drawRect(x, 0, x + barWidth, h, headPaint);
    266             }
    267         }
    268     }
    269 }
    270