/* * Copyright (C) 2013 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 xyz.klinker.blur.launcher3.testing; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; import xyz.klinker.blur.launcher3.util.Thunk; public class WeightWatcher extends LinearLayout { private static final int RAM_GRAPH_RSS_COLOR = 0xFF990000; private static final int RAM_GRAPH_PSS_COLOR = 0xFF99CC00; private static final int TEXT_COLOR = 0xFFFFFFFF; private static final int BACKGROUND_COLOR = 0xc0000000; private static final int UPDATE_RATE = 5000; private static final int MSG_START = 1; private static final int MSG_STOP = 2; private static final int MSG_UPDATE = 3; static int indexOf(int[] a, int x) { for (int i=0; i<a.length; i++) { if (a[i] == x) return i; } return -1; } Handler mHandler = new Handler() { @Override public void handleMessage(Message m) { switch (m.what) { case MSG_START: mHandler.sendEmptyMessage(MSG_UPDATE); break; case MSG_STOP: mHandler.removeMessages(MSG_UPDATE); break; case MSG_UPDATE: int[] pids = mMemoryService.getTrackedProcesses(); final int N = getChildCount(); if (pids.length != N) initViews(); else for (int i=0; i<N; i++) { ProcessWatcher pw = ((ProcessWatcher) getChildAt(i)); if (indexOf(pids, pw.getPid()) < 0) { initViews(); break; } pw.update(); } mHandler.sendEmptyMessageDelayed(MSG_UPDATE, UPDATE_RATE); break; } } }; @Thunk MemoryTracker mMemoryService; public WeightWatcher(Context context, AttributeSet attrs) { super(context, attrs); ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { mMemoryService = ((MemoryTracker.MemoryTrackerInterface)service).getService(); initViews(); } public void onServiceDisconnected(ComponentName className) { mMemoryService = null; } }; context.bindService(new Intent(context, MemoryTracker.class), connection, Context.BIND_AUTO_CREATE); setOrientation(LinearLayout.VERTICAL); setBackgroundColor(BACKGROUND_COLOR); } public void initViews() { removeAllViews(); int[] processes = mMemoryService.getTrackedProcesses(); for (int i=0; i<processes.length; i++) { final ProcessWatcher v = new ProcessWatcher(getContext()); v.setPid(processes[i]); addView(v); } } @Override public void onAttachedToWindow() { super.onAttachedToWindow(); mHandler.sendEmptyMessage(MSG_START); } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); mHandler.sendEmptyMessage(MSG_STOP); } public class ProcessWatcher extends LinearLayout { GraphView mRamGraph; TextView mText; int mPid; @Thunk MemoryTracker.ProcessMemInfo mMemInfo; public ProcessWatcher(Context context) { this(context, null); } public ProcessWatcher(Context context, AttributeSet attrs) { super(context, attrs); final float dp = getResources().getDisplayMetrics().density; mText = new TextView(getContext()); mText.setTextColor(TEXT_COLOR); mText.setTextSize(TypedValue.COMPLEX_UNIT_PX, 10 * dp); mText.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); final int p = (int)(2*dp); setPadding(p, 0, p, 0); mRamGraph = new GraphView(getContext()); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( 0, (int)(14 * dp), 1f ); addView(mText, params); params.leftMargin = (int)(4*dp); params.weight = 0f; params.width = (int)(200 * dp); addView(mRamGraph, params); } public void setPid(int pid) { mPid = pid; mMemInfo = mMemoryService.getMemInfo(mPid); if (mMemInfo == null) { Log.v("WeightWatcher", "Missing info for pid " + mPid + ", removing view: " + this); initViews(); } } public int getPid() { return mPid; } public String getUptimeString() { long sec = mMemInfo.getUptime() / 1000; StringBuilder sb = new StringBuilder(); long days = sec / 86400; if (days > 0) { sec -= days * 86400; sb.append(days); sb.append("d"); } long hours = sec / 3600; if (hours > 0) { sec -= hours * 3600; sb.append(hours); sb.append("h"); } long mins = sec / 60; if (mins > 0) { sec -= mins * 60; sb.append(mins); sb.append("m"); } sb.append(sec); sb.append("s"); return sb.toString(); } public void update() { //Log.v("WeightWatcher.ProcessWatcher", // "MSG_UPDATE pss=" + mMemInfo.currentPss); mText.setText("(" + mPid + (mPid == android.os.Process.myPid() ? "/A" // app : "/S") // service + ") up " + getUptimeString() + " P=" + mMemInfo.currentPss + " U=" + mMemInfo.currentUss ); mRamGraph.invalidate(); } public class GraphView extends View { Paint pssPaint, ussPaint, headPaint; public GraphView(Context context, AttributeSet attrs) { super(context, attrs); pssPaint = new Paint(); pssPaint.setColor(RAM_GRAPH_PSS_COLOR); ussPaint = new Paint(); ussPaint.setColor(RAM_GRAPH_RSS_COLOR); headPaint = new Paint(); headPaint.setColor(Color.WHITE); } public GraphView(Context context) { this(context, null); } @Override public void onDraw(Canvas c) { int w = c.getWidth(); int h = c.getHeight(); if (mMemInfo == null) return; final int N = mMemInfo.pss.length; final float barStep = (float) w / N; final float barWidth = Math.max(1, barStep); final float scale = (float) h / mMemInfo.max; int i; float x; for (i=0; i<N; i++) { x = i * barStep; c.drawRect(x, h - scale * mMemInfo.pss[i], x + barWidth, h, pssPaint); c.drawRect(x, h - scale * mMemInfo.uss[i], x + barWidth, h, ussPaint); } x = mMemInfo.head * barStep; c.drawRect(x, 0, x + barWidth, h, headPaint); } } } }