Unbanish memory screen and new UX

Give memory screen a makeover so that it looks nice enough to be
restored to its rightful home.

Bug: 20694769
Change-Id: I2f6933037b3fbbfb0d9fe5e3ca821ef59e171faa
This commit is contained in:
Jason Monk
2015-05-21 15:24:37 -04:00
committed by Jason Monk
parent 85a72a9088
commit beb171d2e5
23 changed files with 635 additions and 455 deletions

View File

@@ -15,7 +15,7 @@
limitations under the License. limitations under the License.
--> -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight" android:minHeight="?android:attr/listPreferredItemHeight"
@@ -34,16 +34,19 @@
android:duplicateParentState="true" /> android:duplicateParentState="true" />
<LinearLayout <LinearLayout
android:layout_width="0dp" android:id="@+id/text_area"
android:layout_height="match_parent" android:layout_width="match_parent"
android:layout_weight="1" android:layout_height="wrap_content"
android:orientation="vertical" android:layout_alignParentTop="true"
android:layout_toEndOf="@android:id/icon"
android:orientation="horizontal"
android:duplicateParentState="true"> android:duplicateParentState="true">
<TextView <TextView
android:id="@android:id/title" android:id="@android:id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginTop="2dip" android:layout_marginTop="2dip"
android:singleLine="true" android:singleLine="true"
android:ellipsize="marquee" android:ellipsize="marquee"
@@ -55,20 +58,22 @@
android:id="@android:id/summary" android:id="@android:id/summary"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:textAppearance="@android:style/TextAppearance.Material.Body1" android:textAppearance="@android:style/TextAppearance.Material.Body1"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textAlignment="viewStart" android:gravity="end|bottom"
android:duplicateParentState="true" /> android:duplicateParentState="true" />
</LinearLayout> </LinearLayout>
<com.android.settings.applications.LinearColorBar <com.android.settings.applications.LinearColorBar
android:id="@+id/linear_color_bar" android:id="@+id/linear_color_bar"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="10dp"
android:layout_weight="1" android:layout_marginTop="7dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp" android:layout_marginBottom="5dp"
android:layout_toEndOf="@android:id/icon"
android:layout_below="@id/text_area"
android:duplicateParentState="true" /> android:duplicateParentState="true" />
</LinearLayout> </RelativeLayout>

View File

@@ -38,6 +38,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:gravity="end" /> android:gravity="end|bottom" />
</LinearLayout> </LinearLayout>

View File

@@ -28,28 +28,18 @@
android:id="@+id/memory_state" android:id="@+id/memory_state"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp" android:layout_marginTop="5dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="5dp"
android:textAppearance="@android:style/TextAppearance.Material.Subhead" android:textColor="?android:attr/colorAccent"
android:textAppearance="@android:style/TextAppearance.Material.Display1"
/> />
<com.android.settings.applications.LinearColorBar <com.android.settings.applications.LinearColorBar
android:id="@+id/color_bar" android:id="@+id/color_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30dp" android:layout_height="28dp"
android:layout_marginBottom="15dp"
/> />
<TextView
android:id="@+id/memory_used"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
android:layout_marginBottom="10dp"
android:textAppearance="@android:style/TextAppearance.Material.Small"
android:textColor="?android:attr/textColorSecondary"
/>
<include layout="@layout/memory_key" />
</LinearLayout> </LinearLayout>

View File

@@ -25,8 +25,8 @@
android:paddingStart="?android:attr/listPreferredItemPaddingStart" android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:gravity="bottom" android:gravity="bottom"
android:paddingTop="6dip" android:paddingTop="8dip"
android:paddingBottom="6dip" android:paddingBottom="8dip"
android:orientation="horizontal"> android:orientation="horizontal">
<Button <Button
android:id="@+id/button" android:id="@+id/button"

View File

@@ -1225,13 +1225,15 @@
<!-- [CHAR LIMIT=40] Labels for memory states --> <!-- [CHAR LIMIT=40] Labels for memory states -->
<string-array name="ram_states"> <string-array name="ram_states">
<!-- Normal desired memory state. --> <!-- Normal desired memory state. -->
<item>Good performance</item> <item>Normal</item>
<!-- Moderate memory state, not as good as normal. --> <!-- Moderate memory state, not as good as normal. -->
<item>Ok performance</item> <item>Moderate</item>
<!-- Memory is running low. --> <!-- Memory is running low. -->
<item>Poor performance</item> <item>Low</item>
<!-- Memory is critical. --> <!-- Memory is critical. -->
<item>Very poor performance</item> <item>Critical</item>
<!-- Unknown memory state -->
<item>\?</item>
</string-array> </string-array>
<array name="ram_colors"> <array name="ram_colors">

View File

@@ -6556,13 +6556,13 @@
<string name="memory_details_title">Memory details</string> <string name="memory_details_title">Memory details</string>
<!-- Description of app always running [CHAR LIMIT=45] --> <!-- Description of app always running [CHAR LIMIT=45] -->
<string name="always_running">Always running</string> <string name="always_running">Always running (<xliff:g id="percentage" example="5%">%s</xliff:g>)</string>
<!-- Description of app sometimes running [CHAR LIMIT=45] --> <!-- Description of app sometimes running [CHAR LIMIT=45] -->
<string name="sometimes_running">Sometimes running</string> <string name="sometimes_running">Sometimes running (<xliff:g id="percentage" example="5%">%s</xliff:g>)</string>
<!-- Description of app rarely running [CHAR LIMIT=45] --> <!-- Description of app rarely running [CHAR LIMIT=45] -->
<string name="rarely_running">Rarely running</string> <string name="rarely_running">Rarely running (<xliff:g id="percentage" example="5%">%s</xliff:g>)</string>
<!-- Maximum memory usage key [CHAR LIMIT=25] --> <!-- Maximum memory usage key [CHAR LIMIT=25] -->
<string name="memory_max_use">Maximum</string> <string name="memory_max_use">Maximum</string>
@@ -6657,4 +6657,43 @@
<!-- Footer text in the manage assist screen. [CHAR LIMIT=NONE] --> <!-- Footer text in the manage assist screen. [CHAR LIMIT=NONE] -->
<string name="assist_footer">Assist apps help you identify and act on useful information without having to ask. Some apps support both launcher and voice input services to give you integrated assistance.</string> <string name="assist_footer">Assist apps help you identify and act on useful information without having to ask. Some apps support both launcher and voice input services to give you integrated assistance.</string>
<!-- Label for average memory use section [CHAR LIMIT=30] -->
<string name="average_memory_use">Average memory use</string>
<!-- Label for maximum memory use section [CHAR LIMIT=30] -->
<string name="maximum_memory_use">Maximum memory use</string>
<!-- Menu item for Sorting list by average memory use [CHAR LIMIT=NONE]-->
<string name="sort_avg_use">Sort by avg use</string>
<!-- Menu item for Sorting list by maximum memory use [CHAR LIMIT=NONE] -->
<string name="sort_max_use">Sort by max use</string>
<!-- Label for the current performance of the device [CHAR LIMIT=25] -->
<string name="memory_performance">Performance</string>
<!-- Label for total memory of device [CHAR LIMIT=25] -->
<string name="total_memory">Total memory</string>
<!-- Label for average memory usage of device [CHAR LIMIT=25] -->
<string name="average_used">Average used (&#x0025;)</string>
<!-- Label for free memory of device [CHAR LIMIT=25] -->
<string name="free_memory">Free</string>
<!-- Label for button that leads to list of apps and their memory usage [CHAR LIMIT=40]-->
<string name="memory_usage_apps">Memory used by apps</string>
<!-- Description of number of apps using memory [CHAR LIMIT=NONE] -->
<plurals name="memory_usage_apps_summary">
<item quantity="one">1 app used memory in the last <xliff:g id="duration" example="3 hours">%2$s</xliff:g></item>
<item quantity="other"><xliff:g id="count" example="10">%1$d</xliff:g> apps used memory in the last <xliff:g id="duration" example="3 hours">%2$s</xliff:g></item>
</plurals>
<!-- Label for frequency that the app is runnig (e.g. always, sometimes, etc.) [CHAR LIMIT=25] -->
<string name="running_frequency">Frequency</string>
<!-- Label for maximum amount of memory app has used [CHAR LIMIT=25] -->
<string name="memory_maximum_usage">Maximum usage</string>
</resources> </resources>

View File

@@ -49,8 +49,4 @@
android:value="com.android.settings.Settings$HighPowerApplicationsActivity" /> android:value="com.android.settings.Settings$HighPowerApplicationsActivity" />
</PreferenceScreen> </PreferenceScreen>
<PreferenceScreen
android:title="@string/memory_settings_title"
android:fragment="com.android.settings.applications.ProcessStatsUi" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -17,8 +17,26 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/memory_details_title"> android:title="@string/memory_details_title">
<PreferenceCategory
android:title="@string/average_memory_use" />
<com.android.settings.applications.LayoutPreference <com.android.settings.applications.LayoutPreference
android:key="details_header" android:key="status_header"
android:layout="@layout/process_stats_details" /> android:layout="@layout/proc_stats_ui" />
<Preference
android:key="frequency"
android:selectable="false"
android:layout="@layout/horizontal_preference"
android:title="@string/running_frequency" />
<Preference
android:key="max_usage"
android:selectable="false"
android:layout="@layout/horizontal_preference"
android:title="@string/memory_maximum_usage" />
<com.android.settings.applications.SpacePreference
android:layout_height="15dp" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -63,9 +63,13 @@
android:layout="@layout/single_button_panel" /> android:layout="@layout/single_button_panel" />
</PreferenceCategory> </PreferenceCategory>
<com.android.settings.applications.SpacePreference
android:layout_height="8dp" />
<Preference <Preference
android:key="cache_size" android:key="cache_size"
android:title="@string/cache_size_label" android:title="@string/cache_size_label"
android:selectable="false"
android:layout="@layout/horizontal_preference" /> android:layout="@layout/horizontal_preference" />
<com.android.settings.applications.LayoutPreference <com.android.settings.applications.LayoutPreference

View File

@@ -134,13 +134,13 @@
android:icon="@drawable/ic_settings_multiuser" android:icon="@drawable/ic_settings_multiuser"
/> />
<!-- Memory (hidden for now) <!-- Memory -->
<dashboard-tile <dashboard-tile
android:id="@+id/manage_memory" android:id="@+id/manage_memory"
android:title="@string/memory_settings_title" android:title="@string/memory_settings_title"
android:fragment="com.android.settings.applications.ProcessStatsUi" android:fragment="com.android.settings.applications.ProcessStatsSummary"
android:icon="@drawable/ic_settings_memory" android:icon="@drawable/ic_settings_memory"
/> --> />
<!-- Manage NFC payment apps --> <!-- Manage NFC payment apps -->
<dashboard-tile <dashboard-tile

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2013 The Android Open Source Project <!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,7 +18,43 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/app_memory_use" android:title="@string/app_memory_use"
android:key="app_list"> android:key="app_list">
<PreferenceCategory
android:title="@string/average_memory_use" />
<com.android.settings.applications.LayoutPreference <com.android.settings.applications.LayoutPreference
android:key="status_header" android:key="status_header"
android:selectable="false"
android:layout="@layout/proc_stats_ui" /> android:layout="@layout/proc_stats_ui" />
<Preference
android:key="performance"
android:selectable="false"
android:layout="@layout/horizontal_preference"
android:title="@string/memory_performance" />
<Preference
android:key="total_memory"
android:selectable="false"
android:layout="@layout/horizontal_preference"
android:title="@string/total_memory" />
<Preference
android:key="average_used"
android:selectable="false"
android:layout="@layout/horizontal_preference"
android:title="@string/average_used" />
<Preference
android:key="free"
android:selectable="false"
android:layout="@layout/horizontal_preference"
android:title="@string/free_memory" />
<com.android.settings.applications.SpacePreference
android:layout_height="15dp" />
<Preference
android:key="apps_list"
android:title="@string/memory_usage_apps" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/app_memory_use">
<PreferenceCategory
android:key="app_list"
android:title="@string/average_memory_use" />
</PreferenceScreen>

View File

@@ -27,6 +27,7 @@ public abstract class InstrumentedFragment extends PreferenceFragment {
public static final int UNDECLARED = 100000; public static final int UNDECLARED = 100000;
public static final int APPLICATIONS_MANAGE_ASSIST = UNDECLARED + 1; public static final int APPLICATIONS_MANAGE_ASSIST = UNDECLARED + 1;
public static final int PROCESS_STATS_SUMMARY = UNDECLARED + 2;
/** /**
* Declare the view of this category. * Declare the view of this category.

View File

@@ -147,6 +147,8 @@ public final class Utils {
private static final int SECONDS_PER_HOUR = 60 * 60; private static final int SECONDS_PER_HOUR = 60 * 60;
private static final int SECONDS_PER_DAY = 24 * 60 * 60; private static final int SECONDS_PER_DAY = 24 * 60 * 60;
public static final String OS_PKG = "os";
private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>(); private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<Bitmap>();
/** /**

View File

@@ -33,6 +33,7 @@ public class LayoutPreference extends Preference {
public LayoutPreference(Context context, AttributeSet attrs) { public LayoutPreference(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
setSelectable(false);
final TypedArray a = context.obtainStyledAttributes( final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Preference, 0, 0); attrs, com.android.internal.R.styleable.Preference, 0, 0);
int layoutResource = a.getResourceId(com.android.internal.R.styleable.Preference_layout, int layoutResource = a.getResourceId(com.android.internal.R.styleable.Preference_layout,

View File

@@ -35,6 +35,7 @@ import com.android.internal.app.ProcessStats.ProcessDataCollection;
import com.android.internal.app.ProcessStats.TotalMemoryUseCollection; import com.android.internal.app.ProcessStats.TotalMemoryUseCollection;
import com.android.internal.util.MemInfoReader; import com.android.internal.util.MemInfoReader;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -57,8 +58,6 @@ public class ProcStatsData {
private IProcessStats mProcessStats; private IProcessStats mProcessStats;
private ProcessStats mStats; private ProcessStats mStats;
private int mMemState;
private boolean mUseUss; private boolean mUseUss;
private long mDuration; private long mDuration;
@@ -187,28 +186,28 @@ public class ProcStatsData {
ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime); ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime);
ProcStatsEntry osEntry; ProcStatsEntry osEntry;
if (totalMem.sysMemNativeWeight > 0) { if (totalMem.sysMemNativeWeight > 0) {
osEntry = new ProcStatsEntry("os", 0, osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
mContext.getString(R.string.process_stats_os_native), memTotalTime, mContext.getString(R.string.process_stats_os_native), memTotalTime,
(long) (totalMem.sysMemNativeWeight / memTotalTime)); (long) (totalMem.sysMemNativeWeight / memTotalTime));
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry); osPkg.addEntry(osEntry);
} }
if (totalMem.sysMemKernelWeight > 0) { if (totalMem.sysMemKernelWeight > 0) {
osEntry = new ProcStatsEntry("os", 0, osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
mContext.getString(R.string.process_stats_os_kernel), memTotalTime, mContext.getString(R.string.process_stats_os_kernel), memTotalTime,
(long) (totalMem.sysMemKernelWeight / memTotalTime)); (long) (totalMem.sysMemKernelWeight / memTotalTime));
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry); osPkg.addEntry(osEntry);
} }
if (totalMem.sysMemZRamWeight > 0) { if (totalMem.sysMemZRamWeight > 0) {
osEntry = new ProcStatsEntry("os", 0, osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
mContext.getString(R.string.process_stats_os_zram), memTotalTime, mContext.getString(R.string.process_stats_os_zram), memTotalTime,
(long) (totalMem.sysMemZRamWeight / memTotalTime)); (long) (totalMem.sysMemZRamWeight / memTotalTime));
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry); osPkg.addEntry(osEntry);
} }
if (baseCacheRam > 0) { if (baseCacheRam > 0) {
osEntry = new ProcStatsEntry("os", 0, osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
mContext.getString(R.string.process_stats_os_cache), memTotalTime, mContext.getString(R.string.process_stats_os_cache), memTotalTime,
baseCacheRam / 1024); baseCacheRam / 1024);
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss); osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
@@ -296,7 +295,6 @@ public class ProcStatsData {
private void load() { private void load() {
try { try {
mMemState = mProcessStats.getCurrentMemoryState();
ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration); ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
mStats = new ProcessStats(false); mStats = new ProcessStats(false);
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd); InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);

View File

@@ -23,6 +23,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import java.util.ArrayList; import java.util.ArrayList;
@@ -89,10 +90,10 @@ public class ProcStatsPackageEntry implements Parcelable {
final int N = mEntries.size(); final int N = mEntries.size();
for (int i=0; i < N; i++) { for (int i=0; i < N; i++) {
ProcStatsEntry entry = mEntries.get(i); ProcStatsEntry entry = mEntries.get(i);
mBgDuration += entry.mBgDuration; mBgDuration = Math.max(entry.mBgDuration, mBgDuration);
mAvgBgMem += entry.mAvgBgMem; mAvgBgMem += entry.mAvgBgMem;
mBgWeight += entry.mBgWeight; mBgWeight += entry.mBgWeight;
mRunDuration += entry.mRunDuration; mRunDuration = Math.max(entry.mRunDuration, mRunDuration);
mAvgRunMem += entry.mAvgRunMem; mAvgRunMem += entry.mAvgRunMem;
mRunWeight += entry.mRunWeight; mRunWeight += entry.mRunWeight;
@@ -162,11 +163,14 @@ public class ProcStatsPackageEntry implements Parcelable {
// TODO: Find better place for this. // TODO: Find better place for this.
public static CharSequence getFrequency(float amount, Context context) { public static CharSequence getFrequency(float amount, Context context) {
if (amount > ALWAYS_THRESHOLD) { if (amount > ALWAYS_THRESHOLD) {
return context.getString(R.string.always_running); return context.getString(R.string.always_running,
Utils.formatPercentage((int) (amount * 100)));
} else if (amount > SOMETIMES_THRESHOLD) { } else if (amount > SOMETIMES_THRESHOLD) {
return context.getString(R.string.sometimes_running); return context.getString(R.string.sometimes_running,
Utils.formatPercentage((int) (amount * 100)));
} else { } else {
return context.getString(R.string.rarely_running); return context.getString(R.string.rarely_running,
Utils.formatPercentage((int) (amount * 100)));
} }
} }
} }

View File

@@ -0,0 +1,127 @@
/*
* Copyright (C) 2015 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 android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import com.android.internal.app.ProcessStats;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
public abstract class ProcessStatsBase extends SettingsPreferenceFragment
implements OnItemSelectedListener {
private static final String DURATION = "duration";
protected static final String ARG_TRANSFER_STATS = "transfer_stats";
protected static final String ARG_DURATION_INDEX = "duration_index";
protected static final int NUM_DURATIONS = 4;
// The actual duration value to use for each duration option. Note these
// are lower than the actual duration, since our durations are computed in
// batches of 3 hours so we want to allow the time we use to be slightly
// smaller than the actual time selected instead of bumping up to 3 hours
// beyond it.
private static final long DURATION_QUANTUM = ProcessStats.COMMIT_PERIOD;
protected static long[] sDurations = new long[] {
3 * 60 * 60 * 1000 - DURATION_QUANTUM / 2, 6 * 60 *60 * 1000 - DURATION_QUANTUM / 2,
12 * 60 * 60 * 1000 - DURATION_QUANTUM / 2, 24 * 60 * 60 * 1000 - DURATION_QUANTUM / 2
};
protected static int[] sDurationLabels = new int[] {
R.string.menu_duration_3h, R.string.menu_duration_6h,
R.string.menu_duration_12h, R.string.menu_duration_1d
};
private ViewGroup mSpinnerHeader;
private Spinner mFilterSpinner;
private ArrayAdapter<String> mFilterAdapter;
protected ProcStatsData mStatsManager;
protected int mDurationIndex;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Bundle args = getArguments();
mStatsManager = new ProcStatsData(getActivity(), icicle != null
|| (args != null && args.getBoolean(ARG_TRANSFER_STATS, false)));
mDurationIndex = icicle != null
? icicle.getInt(ARG_DURATION_INDEX)
: args != null ? args.getInt(ARG_DURATION_INDEX) : 0;
mStatsManager.setDuration(icicle != null
? icicle.getLong(DURATION, sDurations[0]) : sDurations[0]);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putLong(DURATION, mStatsManager.getDuration());
outState.putInt(ARG_DURATION_INDEX, mDurationIndex);
}
@Override
public void onResume() {
super.onResume();
mStatsManager.refreshStats(false);
refreshUi();
}
@Override
public void onDestroy() {
super.onDestroy();
if (getActivity().isChangingConfigurations()) {
mStatsManager.xferStats();
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mSpinnerHeader = (ViewGroup) setPinnedHeaderView(R.layout.apps_filter_spinner);
mFilterSpinner = (Spinner) mSpinnerHeader.findViewById(R.id.filter_spinner);
mFilterAdapter = new ArrayAdapter<String>(getActivity(), R.layout.filter_spinner_item);
mFilterAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
for (int i = 0; i < NUM_DURATIONS; i++) {
mFilterAdapter.add(getString(sDurationLabels[i]));
}
mFilterSpinner.setAdapter(mFilterAdapter);
mFilterSpinner.setSelection(mDurationIndex);
mFilterSpinner.setOnItemSelectedListener(this);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
mDurationIndex = position;
mStatsManager.setDuration(sDurations[position]);
refreshUi();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Select something.
mFilterSpinner.setSelection(0);
}
public abstract void refreshUi();
}

View File

@@ -31,13 +31,15 @@ import android.content.pm.ServiceInfo;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Process; import android.os.Process;
import android.preference.Preference;
import android.preference.PreferenceCategory; import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
@@ -46,6 +48,7 @@ import com.android.settings.CancellablePreference;
import com.android.settings.CancellablePreference.OnCancelListener; import com.android.settings.CancellablePreference.OnCancelListener;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsEntry.Service; import com.android.settings.applications.ProcStatsEntry.Service;
import java.util.ArrayList; import java.util.ArrayList;
@@ -54,36 +57,35 @@ import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
public class ProcessStatsDetail extends SettingsPreferenceFragment public class ProcessStatsDetail extends SettingsPreferenceFragment {
implements Button.OnClickListener {
private static final String TAG = "ProcessStatsDetail"; private static final String TAG = "ProcessStatsDetail";
public static final int ACTION_FORCE_STOP = 1; public static final int MENU_FORCE_STOP = 1;
public static final String EXTRA_PACKAGE_ENTRY = "package_entry"; public static final String EXTRA_PACKAGE_ENTRY = "package_entry";
public static final String EXTRA_USE_USS = "use_uss";
public static final String EXTRA_WEIGHT_TO_RAM = "weight_to_ram"; public static final String EXTRA_WEIGHT_TO_RAM = "weight_to_ram";
public static final String EXTRA_TOTAL_TIME = "total_time"; public static final String EXTRA_TOTAL_TIME = "total_time";
public static final String EXTRA_MAX_MEMORY_USAGE = "max_memory_usage"; public static final String EXTRA_MAX_MEMORY_USAGE = "max_memory_usage";
public static final String EXTRA_TOTAL_SCALE = "total_scale"; public static final String EXTRA_TOTAL_SCALE = "total_scale";
private static final String KEY_DETAILS_HEADER = "details_header"; private static final String KEY_DETAILS_HEADER = "status_header";
private static final String KEY_FREQUENCY = "frequency";
private static final String KEY_MAX_USAGE = "max_usage";
private final ArrayMap<ComponentName, CancellablePreference> mServiceMap = new ArrayMap<>(); private final ArrayMap<ComponentName, CancellablePreference> mServiceMap = new ArrayMap<>();
private PackageManager mPm; private PackageManager mPm;
private DevicePolicyManager mDpm; private DevicePolicyManager mDpm;
private MenuItem mForceStop;
private ProcStatsPackageEntry mApp; private ProcStatsPackageEntry mApp;
private boolean mUseUss;
private double mWeightToRam; private double mWeightToRam;
private long mTotalTime; private long mTotalTime;
private long mOnePercentTime; private long mOnePercentTime;
private Button mForceStopButton;
private Button mReportButton;
private LinearColorBar mColorBar; private LinearColorBar mColorBar;
private double mMaxMemoryUsage; private double mMaxMemoryUsage;
@@ -98,7 +100,6 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
final Bundle args = getArguments(); final Bundle args = getArguments();
mApp = args.getParcelable(EXTRA_PACKAGE_ENTRY); mApp = args.getParcelable(EXTRA_PACKAGE_ENTRY);
mApp.retrieveUiData(getActivity(), mPm); mApp.retrieveUiData(getActivity(), mPm);
mUseUss = args.getBoolean(EXTRA_USE_USS);
mWeightToRam = args.getDouble(EXTRA_WEIGHT_TO_RAM); mWeightToRam = args.getDouble(EXTRA_WEIGHT_TO_RAM);
mTotalTime = args.getLong(EXTRA_TOTAL_TIME); mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
mMaxMemoryUsage = args.getDouble(EXTRA_MAX_MEMORY_USAGE); mMaxMemoryUsage = args.getDouble(EXTRA_MAX_MEMORY_USAGE);
@@ -107,6 +108,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
mServiceMap.clear(); mServiceMap.clear();
createDetails(); createDetails();
setHasOptionsMenu(true);
} }
@Override @Override
@@ -115,7 +117,8 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
AppHeader.createAppHeader(this, AppHeader.createAppHeader(this,
mApp.mUiTargetApp != null ? mApp.mUiTargetApp.loadIcon(mPm) : new ColorDrawable(0), mApp.mUiTargetApp != null ? mApp.mUiTargetApp.loadIcon(mPm) : new ColorDrawable(0),
mApp.mUiLabel, AppInfoWithHeader.getInfoIntent(this, mApp.mPackage)); mApp.mUiLabel, mApp.mPackage.equals(Utils.OS_PKG) ? null
: AppInfoWithHeader.getInfoIntent(this, mApp.mPackage));
} }
@Override @Override
@@ -126,8 +129,8 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
checkForceStop();
checkForceStop();
updateRunningServices(); updateRunningServices();
} }
@@ -173,55 +176,42 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
LayoutPreference headerLayout = (LayoutPreference) findPreference(KEY_DETAILS_HEADER); LayoutPreference headerLayout = (LayoutPreference) findPreference(KEY_DETAILS_HEADER);
TextView avgUsed = (TextView) headerLayout.findViewById(R.id.memory_avg);
TextView maxUsed = (TextView) headerLayout.findViewById(R.id.memory_max);
avgUsed.setText(getString(R.string.memory_avg_desc,
Formatter.formatShortFileSize(getActivity(),
(long) (Math.max(mApp.mBgWeight, mApp.mRunWeight) * mWeightToRam))));
maxUsed.setText(getString(R.string.memory_max_desc,
Formatter.formatShortFileSize(getActivity(),
(long) (Math.max(mApp.mMaxBgMem, mApp.mMaxRunMem) * 1024 * mTotalScale))));
mForceStopButton = (Button) headerLayout.findViewById(R.id.right_button);
mReportButton = (Button) headerLayout.findViewById(R.id.left_button);
if (mApp.mEntries.get(0).mUid >= android.os.Process.FIRST_APPLICATION_UID) {
mForceStopButton.setEnabled(false);
mReportButton.setVisibility(View.INVISIBLE);
mForceStopButton.setText(R.string.force_stop);
mForceStopButton.setTag(ACTION_FORCE_STOP);
mForceStopButton.setOnClickListener(this);
} else {
mReportButton.setVisibility(View.GONE);
mForceStopButton.setVisibility(View.GONE);
}
// TODO: Find way to share this code with ProcessStatsPreference. // TODO: Find way to share this code with ProcessStatsPreference.
boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight; boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
float avgRatio = (float) ((statsForeground ? mApp.mRunWeight : mApp.mBgWeight) double avgRam = (statsForeground ? mApp.mRunWeight : mApp.mBgWeight) * mWeightToRam;
* mWeightToRam / mMaxMemoryUsage); float avgRatio = (float) (avgRam / mMaxMemoryUsage);
float maxRatio = (float) ((statsForeground ? mApp.mMaxRunMem : mApp.mMaxBgMem) float remainingRatio = 1 - avgRatio;
* mTotalScale * 1024 / mMaxMemoryUsage - avgRatio);
float remainingRatio = 1 - avgRatio - maxRatio;
mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar); mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar);
Context context = getActivity(); Context context = getActivity();
mColorBar.setColors(context.getColor(R.color.memory_avg_use), mColorBar.setColors( context.getColor(R.color.memory_max_use), 0,
context.getColor(R.color.memory_max_use),
context.getColor(R.color.memory_remaining)); context.getColor(R.color.memory_remaining));
mColorBar.setRatios(avgRatio, maxRatio, remainingRatio); mColorBar.setRatios(avgRatio, 0, remainingRatio);
((TextView) headerLayout.findViewById(R.id.memory_state)).setText(
Formatter.formatShortFileSize(getContext(), (long) avgRam));
long duration = Math.max(mApp.mRunDuration, mApp.mBgDuration);
CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration
/ (float) mTotalTime, getActivity());
findPreference(KEY_FREQUENCY).setSummary(frequency);
double max = Math.max(mApp.mMaxBgMem, mApp.mMaxRunMem) * mTotalScale * 1024;
findPreference(KEY_MAX_USAGE).setSummary(
Formatter.formatShortFileSize(getContext(), (long) max));
} }
public void onClick(View v) { @Override
doAction((Integer) v.getTag()); public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
mForceStop = menu.add(0, MENU_FORCE_STOP, 0, R.string.force_stop);
checkForceStop();
} }
private void doAction(int action) { @Override
switch (action) { public boolean onOptionsItemSelected(MenuItem item) {
case ACTION_FORCE_STOP: switch (item.getItemId()) {
case MENU_FORCE_STOP:
killProcesses(); killProcesses();
break; return true;
} }
return false;
} }
final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() { final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
@@ -250,8 +240,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
Collections.sort(entries, sEntryCompare); Collections.sort(entries, sEntryCompare);
for (int ie = 0; ie < entries.size(); ie++) { for (int ie = 0; ie < entries.size(); ie++) {
ProcStatsEntry entry = entries.get(ie); ProcStatsEntry entry = entries.get(ie);
PreferenceCategory processPref = new PreferenceCategory(getActivity()); Preference processPref = new Preference(getActivity());
processPref.setLayoutResource(R.layout.process_preference_category);
processPref.setTitle(entry.mLabel); processPref.setTitle(entry.mLabel);
long duration = Math.max(entry.mRunDuration, entry.mBgDuration); long duration = Math.max(entry.mRunDuration, entry.mBgDuration);
@@ -263,7 +252,6 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
processPref.setSummary( processPref.setSummary(
getString(R.string.memory_use_running_format, memoryString, frequency)); getString(R.string.memory_use_running_format, memoryString, frequency));
getPreferenceScreen().addPreference(processPref); getPreferenceScreen().addPreference(processPref);
fillServicesSection(entry, processPref);
} }
} }
@@ -423,12 +411,14 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
am.forceStopPackage(ent.mPackages.get(j)); am.forceStopPackage(ent.mPackages.get(j));
} }
} }
checkForceStop();
} }
private void checkForceStop() { private void checkForceStop() {
if (mForceStop == null) {
return;
}
if (mApp.mEntries.get(0).mUid < Process.FIRST_APPLICATION_UID) { if (mApp.mEntries.get(0).mUid < Process.FIRST_APPLICATION_UID) {
mForceStopButton.setEnabled(false); mForceStop.setVisible(false);
return; return;
} }
boolean isStarted = false; boolean isStarted = false;
@@ -437,7 +427,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
for (int j=0; j<ent.mPackages.size(); j++) { for (int j=0; j<ent.mPackages.size(); j++) {
String pkg = ent.mPackages.get(j); String pkg = ent.mPackages.get(j);
if (mDpm.packageHasActiveAdmins(pkg)) { if (mDpm.packageHasActiveAdmins(pkg)) {
mForceStopButton.setEnabled(false); mForceStop.setEnabled(false);
return; return;
} }
try { try {
@@ -450,7 +440,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
} }
} }
if (isStarted) { if (isStarted) {
mForceStopButton.setEnabled(true); mForceStop.setVisible(true);
} }
} }
} }

View File

@@ -21,8 +21,8 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.preference.Preference; import android.preference.Preference;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.view.View; import android.view.View;
import com.android.settings.R; import com.android.settings.R;
@@ -30,11 +30,9 @@ import com.android.settings.R;
public class ProcessStatsPreference extends Preference { public class ProcessStatsPreference extends Preference {
private ProcStatsPackageEntry mEntry; private ProcStatsPackageEntry mEntry;
private final int mAvgColor; private final int mColor;
private final int mMaxColor;
private final int mRemainingColor; private final int mRemainingColor;
private float mAvgRatio; private float mRatio;
private float mMaxRatio;
private float mRemainingRatio; private float mRemainingRatio;
public ProcessStatsPreference(Context context) { public ProcessStatsPreference(Context context) {
@@ -53,13 +51,12 @@ public class ProcessStatsPreference extends Preference {
int defStyleRes) { int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(R.layout.app_item_linear_color); setLayoutResource(R.layout.app_item_linear_color);
mAvgColor = context.getColor(R.color.memory_avg_use); mColor = context.getColor(R.color.memory_max_use);
mMaxColor = context.getColor(R.color.memory_max_use);
mRemainingColor = context.getColor(R.color.memory_remaining); mRemainingColor = context.getColor(R.color.memory_remaining);
} }
public void init(ProcStatsPackageEntry entry, PackageManager pm, double maxMemory, public void init(ProcStatsPackageEntry entry, PackageManager pm, double maxMemory,
double weightToRam, double totalScale) { double weightToRam, double totalScale, boolean avg) {
mEntry = entry; mEntry = entry;
setTitle(TextUtils.isEmpty(entry.mUiLabel) ? entry.mPackage : entry.mUiLabel); setTitle(TextUtils.isEmpty(entry.mUiLabel) ? entry.mPackage : entry.mUiLabel);
if (entry.mUiTargetApp != null) { if (entry.mUiTargetApp != null) {
@@ -68,13 +65,11 @@ public class ProcessStatsPreference extends Preference {
setIcon(new ColorDrawable(0)); setIcon(new ColorDrawable(0));
} }
boolean statsForeground = entry.mRunWeight > entry.mBgWeight; boolean statsForeground = entry.mRunWeight > entry.mBgWeight;
setSummary(entry.mRunDuration > entry.mBgDuration ? entry.getRunningFrequency(getContext()) double amount = avg ? (statsForeground ? entry.mRunWeight : entry.mBgWeight) * weightToRam
: entry.getBackgroundFrequency(getContext())); : (statsForeground ? entry.mMaxRunMem : entry.mMaxBgMem) * totalScale * 1024;
mAvgRatio = (float) ((statsForeground ? entry.mRunWeight : entry.mBgWeight) setSummary(Formatter.formatShortFileSize(getContext(), (long) amount));
* weightToRam / maxMemory); mRatio = (float) (amount / maxMemory);
mMaxRatio = (float) ((statsForeground ? entry.mMaxRunMem : entry.mMaxBgMem) mRemainingRatio = 1 - mRatio;
* totalScale * 1024 / maxMemory - mAvgRatio);
mRemainingRatio = 1 - mAvgRatio - mMaxRatio;
} }
public ProcStatsPackageEntry getEntry() { public ProcStatsPackageEntry getEntry() {
@@ -86,7 +81,7 @@ public class ProcessStatsPreference extends Preference {
super.onBindView(view); super.onBindView(view);
LinearColorBar linearColorBar = (LinearColorBar) view.findViewById(R.id.linear_color_bar); LinearColorBar linearColorBar = (LinearColorBar) view.findViewById(R.id.linear_color_bar);
linearColorBar.setColors(mAvgColor, mMaxColor, mRemainingColor); linearColorBar.setColors(mColor, mColor, mRemainingColor);
linearColorBar.setRatios(mAvgRatio, mMaxRatio, mRemainingRatio); linearColorBar.setRatios(mRatio, 0, mRemainingRatio);
} }
} }

View File

@@ -0,0 +1,121 @@
/*
* Copyright (C) 2015 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 android.content.Context;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.text.format.Formatter;
import android.widget.TextView;
import com.android.settings.InstrumentedFragment;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsData.MemInfo;
public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenceClickListener {
private static final String KEY_STATUS_HEADER = "status_header";
private static final String KEY_PERFORMANCE = "performance";
private static final String KEY_TOTAL_MEMORY = "total_memory";
private static final String KEY_AVERAGY_USED = "average_used";
private static final String KEY_FREE = "free";
private static final String KEY_APP_LIST = "apps_list";
private LinearColorBar mColors;
private LayoutPreference mHeader;
private TextView mMemStatus;
private Preference mPerformance;
private Preference mTotalMemory;
private Preference mAverageUsed;
private Preference mFree;
private Preference mAppListPreference;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.process_stats_summary);
mHeader = (LayoutPreference) findPreference(KEY_STATUS_HEADER);
mMemStatus = (TextView) mHeader.findViewById(R.id.memory_state);
mColors = (LinearColorBar) mHeader.findViewById(R.id.color_bar);
mPerformance = findPreference(KEY_PERFORMANCE);
mTotalMemory = findPreference(KEY_TOTAL_MEMORY);
mAverageUsed = findPreference(KEY_AVERAGY_USED);
mFree = findPreference(KEY_FREE);
mAppListPreference = findPreference(KEY_APP_LIST);
mAppListPreference.setOnPreferenceClickListener(this);
}
@Override
public void refreshUi() {
Context context = getContext();
int memColor = context.getColor(R.color.running_processes_apps_ram);
mColors.setColors(memColor, memColor, context.getColor(R.color.running_processes_free_ram));
MemInfo memInfo = mStatsManager.getMemInfo();
double usedRam = memInfo.realUsedRam;
double totalRam = memInfo.realTotalRam;
double freeRam = memInfo.realFreeRam;
String usedString = Formatter.formatShortFileSize(context, (long) usedRam);
String totalString = Formatter.formatShortFileSize(context, (long) totalRam);
String freeString = Formatter.formatShortFileSize(context, (long) freeRam);
CharSequence memString;
CharSequence[] memStatesStr = getResources().getTextArray(R.array.ram_states);
int memState = mStatsManager.getMemState();
if (memState >= 0 && memState < memStatesStr.length - 1) {
memString = memStatesStr[memState];
} else {
memString = memStatesStr[memStatesStr.length - 1];
}
mMemStatus.setText(usedString);
float usedRatio = (float)(usedRam / (freeRam + usedRam));
mColors.setRatios(usedRatio, 0, 1 - usedRatio);
mPerformance.setSummary(memString);
mTotalMemory.setSummary(totalString);
mAverageUsed.setSummary(Utils.formatPercentage((long) usedRam, (long) totalRam));
mFree.setSummary(freeString);
String durationString = getString(sDurationLabels[mDurationIndex]);
int numApps = mStatsManager.getEntries().size();
mAppListPreference.setSummary(getResources().getQuantityString(
R.plurals.memory_usage_apps_summary, numApps, numApps, durationString));
}
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.PROCESS_STATS_SUMMARY;
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference == mAppListPreference) {
Bundle args = new Bundle();
args.putBoolean(ARG_TRANSFER_STATS, true);
args.putInt(ARG_DURATION_INDEX, mDurationIndex);
mStatsManager.xferStats();
startFragment(this, ProcessStatsUi.class.getName(), R.string.app_memory_use, 0, args);
return true;
}
return false;
}
}

View File

@@ -16,171 +16,89 @@
package com.android.settings.applications; package com.android.settings.applications;
import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceGroup; import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.text.format.Formatter;
import android.util.Log; import android.util.Log;
import android.util.TimeUtils; import android.util.TimeUtils;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.TextView;
import com.android.internal.app.ProcessStats; import com.android.internal.app.ProcessStats;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
import com.android.settings.InstrumentedPreferenceFragment;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity; import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsData.MemInfo; import com.android.settings.applications.ProcStatsData.MemInfo;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
public class ProcessStatsUi extends InstrumentedPreferenceFragment { public class ProcessStatsUi extends ProcessStatsBase {
private static final String MEM_REGION = "mem_region";
private static final String STATS_TYPE = "stats_type";
private static final String USE_USS = "use_uss";
private static final String SHOW_SYSTEM = "show_system";
private static final String SHOW_PERCENTAGE = "show_percentage";
private static final String DURATION = "duration";
static final String TAG = "ProcessStatsUi"; static final String TAG = "ProcessStatsUi";
static final boolean DEBUG = false; static final boolean DEBUG = false;
private static final String KEY_APP_LIST = "app_list"; private static final String KEY_APP_LIST = "app_list";
private static final String KEY_STATUS_HEADER = "status_header";
private static final int NUM_DURATIONS = 4; private static final int MENU_SHOW_AVG = Menu.FIRST;
private static final int MENU_SHOW_MAX = Menu.FIRST + 1;
private static final int MENU_STATS_REFRESH = Menu.FIRST;
private static final int MENU_DURATION = Menu.FIRST + 1;
private static final int MENU_SHOW_PERCENTAGE = MENU_DURATION + NUM_DURATIONS;
private static final int MENU_SHOW_SYSTEM = MENU_SHOW_PERCENTAGE + 1;
private static final int MENU_USE_USS = MENU_SHOW_SYSTEM + 1;
private static final int MENU_TYPE_BACKGROUND = MENU_USE_USS + 1;
private static final int MENU_TYPE_FOREGROUND = MENU_TYPE_BACKGROUND + 1;
private static final int MENU_TYPE_CACHED = MENU_TYPE_FOREGROUND + 1;
static final int MAX_ITEMS_TO_LIST = 60;
final static Comparator<ProcStatsPackageEntry> sPackageEntryCompare
= new Comparator<ProcStatsPackageEntry>() {
@Override
public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) {
double rhsWeight = Math.max(rhs.mRunWeight, rhs.mBgWeight);
double lhsWeight = Math.max(lhs.mRunWeight, lhs.mBgWeight);
if (lhsWeight == rhsWeight) {
return 0;
}
return lhsWeight < rhsWeight ? 1 : -1;
}
};
private boolean mShowPercentage;
private boolean mShowSystem;
private boolean mUseUss;
private int mMemRegion;
private MenuItem[] mDurationMenus = new MenuItem[NUM_DURATIONS];
private MenuItem mShowPercentageMenu;
private MenuItem mShowSystemMenu;
private MenuItem mUseUssMenu;
private MenuItem mTypeBackgroundMenu;
private MenuItem mTypeForegroundMenu;
private MenuItem mTypeCachedMenu;
private PreferenceGroup mAppListGroup; private PreferenceGroup mAppListGroup;
private TextView mMemStatus;
private long[] mMemTimes = new long[ProcessStats.ADJ_MEM_FACTOR_COUNT];
private LinearColorBar mColors;
private TextView mMemUsed;
private LayoutPreference mHeader;
private PackageManager mPm; private PackageManager mPm;
private long memTotalTime;
private int mStatsType; private boolean mShowMax;
private MenuItem mMenuAvg;
// The actual duration value to use for each duration option. Note these private MenuItem mMenuMax;
// are lower than the actual duration, since our durations are computed in
// batches of 3 hours so we want to allow the time we use to be slightly
// smaller than the actual time selected instead of bumping up to 3 hours
// beyond it.
private static final long DURATION_QUANTUM = ProcessStats.COMMIT_PERIOD;
private static long[] sDurations = new long[] {
3*60*60*1000 - DURATION_QUANTUM/2, 6*60*60*1000 - DURATION_QUANTUM/2,
12*60*60*1000 - DURATION_QUANTUM/2, 24*60*60*1000 - DURATION_QUANTUM/2
};
private static int[] sDurationLabels = new int[] {
R.string.menu_duration_3h, R.string.menu_duration_6h,
R.string.menu_duration_12h, R.string.menu_duration_1d
};
private ProcStatsData mStatsManager;
private double mMaxMemoryUsage;
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
mStatsManager = new ProcStatsData(getActivity(), icicle != null);
mPm = getActivity().getPackageManager(); mPm = getActivity().getPackageManager();
addPreferencesFromResource(R.xml.process_stats_summary); addPreferencesFromResource(R.xml.process_stats_ui);
mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST); mAppListGroup = (PreferenceGroup) findPreference(KEY_APP_LIST);
mHeader = (LayoutPreference)mAppListGroup.findPreference(KEY_STATUS_HEADER);
mMemStatus = (TextView) mHeader.findViewById(R.id.memory_state);
mColors = (LinearColorBar) mHeader.findViewById(R.id.color_bar);
mMemUsed = (TextView) mHeader.findViewById(R.id.memory_used);
mStatsManager.setDuration(icicle != null
? icicle.getLong(DURATION, sDurations[0]) : sDurations[0]);
mShowPercentage = icicle != null ? icicle.getBoolean(SHOW_PERCENTAGE) : true;
mShowSystem = icicle != null ? icicle.getBoolean(SHOW_SYSTEM) : false;
mUseUss = icicle != null ? icicle.getBoolean(USE_USS) : false;
mStatsType = icicle != null ? icicle.getInt(STATS_TYPE, MENU_TYPE_BACKGROUND)
: MENU_TYPE_BACKGROUND;
mMemRegion = icicle != null ? icicle.getInt(MEM_REGION, LinearColorBar.REGION_GREEN)
: LinearColorBar.REGION_GREEN;
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
mMenuAvg = menu.add(0, MENU_SHOW_AVG, 0, R.string.sort_avg_use);
mMenuMax = menu.add(0, MENU_SHOW_MAX, 0, R.string.sort_max_use);
updateMenu();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_SHOW_AVG:
case MENU_SHOW_MAX:
mShowMax = !mShowMax;
refreshUi();
updateMenu();
return true;
}
return super.onOptionsItemSelected(item);
}
private void updateMenu() {
mMenuMax.setVisible(!mShowMax);
mMenuAvg.setVisible(mShowMax);
}
@Override @Override
protected int getMetricsCategory() { protected int getMetricsCategory() {
return MetricsLogger.APPLICATIONS_PROCESS_STATS_UI; return MetricsLogger.APPLICATIONS_PROCESS_STATS_UI;
} }
@Override
public void onResume() {
super.onResume();
mStatsManager.refreshStats(false);
refreshUi();
}
@Override @Override
public void onSaveInstanceState(Bundle outState) { public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
outState.putLong(DURATION, mStatsManager.getDuration());
outState.putBoolean(SHOW_PERCENTAGE, mShowPercentage);
outState.putBoolean(SHOW_SYSTEM, mShowSystem);
outState.putBoolean(USE_USS, mUseUss);
outState.putInt(STATS_TYPE, mStatsType);
outState.putInt(MEM_REGION, mMemRegion);
}
@Override
public void onDestroy() {
super.onDestroy();
if (getActivity().isChangingConfigurations()) {
mStatsManager.xferStats();
}
} }
@Override @Override
@@ -192,142 +110,19 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment {
ProcessStatsPreference pgp = (ProcessStatsPreference) preference; ProcessStatsPreference pgp = (ProcessStatsPreference) preference;
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(ProcessStatsDetail.EXTRA_PACKAGE_ENTRY, pgp.getEntry()); args.putParcelable(ProcessStatsDetail.EXTRA_PACKAGE_ENTRY, pgp.getEntry());
args.putBoolean(ProcessStatsDetail.EXTRA_USE_USS, mUseUss); MemInfo memInfo = mStatsManager.getMemInfo();
args.putDouble(ProcessStatsDetail.EXTRA_WEIGHT_TO_RAM, args.putDouble(ProcessStatsDetail.EXTRA_WEIGHT_TO_RAM,
mStatsManager.getMemInfo().weightToRam); memInfo.weightToRam);
args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, memTotalTime); args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, memInfo.memTotalTime);
args.putDouble(ProcessStatsDetail.EXTRA_MAX_MEMORY_USAGE, mMaxMemoryUsage); args.putDouble(ProcessStatsDetail.EXTRA_MAX_MEMORY_USAGE,
args.putDouble(ProcessStatsDetail.EXTRA_TOTAL_SCALE, mStatsManager.getMemInfo().totalScale); memInfo.usedWeight * memInfo.weightToRam);
args.putDouble(ProcessStatsDetail.EXTRA_TOTAL_SCALE, memInfo.totalScale);
((SettingsActivity) getActivity()).startPreferencePanel( ((SettingsActivity) getActivity()).startPreferencePanel(
ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0); ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0);
return super.onPreferenceTreeClick(preferenceScreen, preference); return super.onPreferenceTreeClick(preferenceScreen, preference);
} }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
MenuItem refresh = menu.add(0, MENU_STATS_REFRESH, 0, R.string.menu_stats_refresh)
.setIcon(R.drawable.ic_menu_refresh_holo_dark)
.setAlphabeticShortcut('r');
refresh.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
MenuItem.SHOW_AS_ACTION_WITH_TEXT);
menu.add(0, MENU_DURATION, 0, R.string.menu_proc_stats_duration);
// Hide these for now, until their need is determined.
// mShowPercentageMenu = menu.add(0, MENU_SHOW_PERCENTAGE, 0, R.string.menu_show_percentage)
// .setAlphabeticShortcut('p')
// .setCheckable(true);
// mShowSystemMenu = menu.add(0, MENU_SHOW_SYSTEM, 0, R.string.menu_show_system)
// .setAlphabeticShortcut('s')
// .setCheckable(true);
// mUseUssMenu = menu.add(0, MENU_USE_USS, 0, R.string.menu_use_uss)
// .setAlphabeticShortcut('u')
// .setCheckable(true);
// subMenu = menu.addSubMenu(R.string.menu_proc_stats_type);
// mTypeBackgroundMenu = subMenu.add(0, MENU_TYPE_BACKGROUND, 0,
// R.string.menu_proc_stats_type_background)
// .setAlphabeticShortcut('b')
// .setCheckable(true);
// mTypeForegroundMenu = subMenu.add(0, MENU_TYPE_FOREGROUND, 0,
// R.string.menu_proc_stats_type_foreground)
// .setAlphabeticShortcut('f')
// .setCheckable(true);
// mTypeCachedMenu = subMenu.add(0, MENU_TYPE_CACHED, 0,
// R.string.menu_proc_stats_type_cached)
// .setCheckable(true);
updateMenus();
}
void updateMenus() {
int closestIndex = 0;
long closestDelta = Math.abs(sDurations[0] - mStatsManager.getDuration());
for (int i = 1; i < NUM_DURATIONS; i++) {
long delta = Math.abs(sDurations[i] - mStatsManager.getDuration());
if (delta < closestDelta) {
closestDelta = delta;
closestIndex = i;
}
}
for (int i=0; i<NUM_DURATIONS; i++) {
if (mDurationMenus[i] != null) {
mDurationMenus[i].setChecked(i == closestIndex);
}
}
mStatsManager.setDuration(sDurations[closestIndex]);
if (mShowPercentageMenu != null) {
mShowPercentageMenu.setChecked(mShowPercentage);
}
if (mShowSystemMenu != null) {
mShowSystemMenu.setChecked(mShowSystem);
mShowSystemMenu.setEnabled(mStatsType == MENU_TYPE_BACKGROUND);
}
if (mUseUssMenu != null) {
mUseUssMenu.setChecked(mUseUss);
}
if (mTypeBackgroundMenu != null) {
mTypeBackgroundMenu.setChecked(mStatsType == MENU_TYPE_BACKGROUND);
}
if (mTypeForegroundMenu != null) {
mTypeForegroundMenu.setChecked(mStatsType == MENU_TYPE_FOREGROUND);
}
if (mTypeCachedMenu != null) {
mTypeCachedMenu.setChecked(mStatsType == MENU_TYPE_CACHED);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_STATS_REFRESH:
mStatsManager.refreshStats(false);
refreshUi();
return true;
case MENU_SHOW_PERCENTAGE:
mShowPercentage = !mShowPercentage;
refreshUi();
return true;
case MENU_SHOW_SYSTEM:
mShowSystem = !mShowSystem;
refreshUi();
return true;
case MENU_USE_USS:
mUseUss = !mUseUss;
refreshUi();
return true;
case MENU_TYPE_BACKGROUND:
case MENU_TYPE_FOREGROUND:
case MENU_TYPE_CACHED:
mStatsType = item.getItemId();
if (mStatsType == MENU_TYPE_FOREGROUND) {
mStatsManager.setStats(FOREGROUND_PROC_STATES);
} else if (mStatsType == MENU_TYPE_CACHED) {
mStatsManager.setStats(CACHED_PROC_STATES);
} else {
mStatsManager.setStats(mShowSystem ? BACKGROUND_AND_SYSTEM_PROC_STATES
: ProcessStats.BACKGROUND_PROC_STATES);
}
refreshUi();
return true;
case MENU_DURATION:
CharSequence[] durations = new CharSequence[sDurationLabels.length];
for (int i = 0; i < sDurationLabels.length; i++) {
durations[i] = getString(sDurationLabels[i]);
}
new AlertDialog.Builder(getContext())
.setTitle(item.getTitle())
.setItems(durations, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mStatsManager.setDuration(sDurations[which]);
refreshUi();
}
}).show();
return true;
}
return false;
}
/** /**
* All states in which we consider a process to be actively running (rather than * All states in which we consider a process to be actively running (rather than
* something that can be freely killed to reclaim RAM). Note this also includes * something that can be freely killed to reclaim RAM). Note this also includes
@@ -358,103 +153,66 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment {
return sb.toString(); return sb.toString();
} }
private void refreshUi() { public void refreshUi() {
updateMenus();
mAppListGroup.removeAll(); mAppListGroup.removeAll();
mAppListGroup.setOrderingAsAdded(false); mAppListGroup.setOrderingAsAdded(false);
mHeader.setOrder(-1); mAppListGroup.setTitle(mShowMax ? R.string.maximum_memory_use
mAppListGroup.addPreference(mHeader); : R.string.average_memory_use);
final long elapsedTime = mStatsManager.getElapsedTime();
final Context context = getActivity(); final Context context = getActivity();
// TODO: More Colors.
// For computing the ratio to show, we want to count the baseline cached RAM we
// need (at which point we start killing processes) as used RAM, so that if we
// reach the point of thrashing due to no RAM for any background processes we
// report that as RAM being full. To do this, we need to first convert the weights
// back to actual RAM... and since the RAM values we compute here won't exactly
// match the real physical RAM, scale those to the actual physical RAM. No problem!
MemInfo memInfo = mStatsManager.getMemInfo(); MemInfo memInfo = mStatsManager.getMemInfo();
memTotalTime = memInfo.memTotalTime;
double usedRam = memInfo.realUsedRam;
double totalRam = memInfo.realTotalRam;
double freeRam = memInfo.realFreeRam;
String durationString = Utils.formatElapsedTime(context, elapsedTime, false);
String usedString = Formatter.formatShortFileSize(context, (long) usedRam);
String totalString = Formatter.formatShortFileSize(context, (long) totalRam);
CharSequence memString;
CharSequence[] memStatesStr = getResources().getTextArray(R.array.ram_states);
int memState = mStatsManager.getMemState();
int memColor;
if (memState >= 0 && memState < memStatesStr.length) {
memString = memStatesStr[memState];
memColor = getResources().getIntArray(R.array.ram_colors)[memState];
} else {
memString = "?";
memColor = context.getColor(R.color.running_processes_apps_ram);
}
mColors.setColors(memColor, memColor, context.getColor(R.color.running_processes_free_ram));
if (mShowPercentage) {
mMemUsed.setText(context.getString(
R.string.process_stats_total_duration_percentage,
Utils.formatPercentage((long) usedRam, (long) totalRam),
durationString));
} else {
mMemUsed.setText(context.getString(R.string.process_stats_total_duration,
usedString, totalString, durationString));
}
mMemStatus.setText(memString);
float usedRatio = (float)(usedRam / (freeRam + usedRam));
mColors.setRatios(usedRatio, 0, 1-usedRatio);
List<ProcStatsPackageEntry> pkgEntries = mStatsManager.getEntries(); List<ProcStatsPackageEntry> pkgEntries = mStatsManager.getEntries();
// Update everything and get the absolute maximum of memory usage for scaling. // Update everything and get the absolute maximum of memory usage for scaling.
mMaxMemoryUsage = 0;
for (int i=0, N=pkgEntries.size(); i<N; i++) { for (int i=0, N=pkgEntries.size(); i<N; i++) {
ProcStatsPackageEntry pkg = pkgEntries.get(i); ProcStatsPackageEntry pkg = pkgEntries.get(i);
pkg.updateMetrics(); pkg.updateMetrics();
double maxMem = Math.max(pkg.mMaxBgMem, pkg.mMaxRunMem) * 1024 * memInfo.totalScale;
if (maxMem > mMaxMemoryUsage) {
mMaxMemoryUsage = maxMem;
}
} }
Collections.sort(pkgEntries, sPackageEntryCompare); Collections.sort(pkgEntries, mShowMax ? sMaxPackageEntryCompare : sPackageEntryCompare);
// Now collect the per-process information into applications, so that applications // Now collect the per-process information into applications, so that applications
// running as multiple processes will have only one entry representing all of them. // running as multiple processes will have only one entry representing all of them.
if (DEBUG) Log.d(TAG, "-------------------- BUILDING UI"); if (DEBUG) Log.d(TAG, "-------------------- BUILDING UI");
// Find where we should stop. Because we have two properties we are looking at, double maxMemory = mShowMax ? memInfo.realTotalRam
// we need to go from the back looking for the first place either holds. : memInfo.usedWeight * memInfo.weightToRam;
int end = pkgEntries.size()-1; for (int i = 0; i < pkgEntries.size(); i++) {
while (end >= 0) {
ProcStatsPackageEntry pkg = pkgEntries.get(end);
final double percentOfWeight = (pkg.mRunWeight
/ (memInfo.totalRam / memInfo.weightToRam)) * 100;
final double percentOfTime = (((double) pkg.mRunDuration) / memTotalTime) * 100;
if (percentOfWeight >= .01 || percentOfTime >= 25) {
break;
}
end--;
}
for (int i=0; i <= end; i++) {
ProcStatsPackageEntry pkg = pkgEntries.get(i); ProcStatsPackageEntry pkg = pkgEntries.get(i);
ProcessStatsPreference pref = new ProcessStatsPreference(context); ProcessStatsPreference pref = new ProcessStatsPreference(context);
pkg.retrieveUiData(context, mPm); pkg.retrieveUiData(context, mPm);
pref.init(pkg, mPm, mMaxMemoryUsage, memInfo.weightToRam, memInfo.totalScale); pref.init(pkg, mPm, maxMemory, memInfo.weightToRam,
memInfo.totalScale, !mShowMax);
pref.setOrder(i); pref.setOrder(i);
mAppListGroup.addPreference(pref); mAppListGroup.addPreference(pref);
if (mAppListGroup.getPreferenceCount() > (MAX_ITEMS_TO_LIST+1)) {
if (DEBUG) Log.d(TAG, "Done with UI, hit item limit!");
break;
} }
} }
final static Comparator<ProcStatsPackageEntry> sPackageEntryCompare
= new Comparator<ProcStatsPackageEntry>() {
@Override
public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) {
double rhsWeight = Math.max(rhs.mRunWeight, rhs.mBgWeight);
double lhsWeight = Math.max(lhs.mRunWeight, lhs.mBgWeight);
if (lhsWeight == rhsWeight) {
return 0;
} }
return lhsWeight < rhsWeight ? 1 : -1;
}
};
final static Comparator<ProcStatsPackageEntry> sMaxPackageEntryCompare
= new Comparator<ProcStatsPackageEntry>() {
@Override
public int compare(ProcStatsPackageEntry lhs, ProcStatsPackageEntry rhs) {
double rhsMax = Math.max(rhs.mMaxBgMem, rhs.mMaxRunMem);
double lhsMax = Math.max(lhs.mMaxBgMem, lhs.mMaxRunMem);
if (lhsMax == rhsMax) {
return 0;
}
return lhsMax < rhsMax ? 1 : -1;
}
};
} }

View File

@@ -0,0 +1,68 @@
/*
* Copyright (C) 2015 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 android.content.Context;
import android.content.res.TypedArray;
import android.preference.Preference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.Space;
/**
* A blank preference that has a specified height by android:layout_height. It can be used
* to fine tune screens that combine custom layouts and standard preferences.
*/
public class SpacePreference extends Preference {
private int mHeight;
public SpacePreference(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.preferenceStyle);
}
public SpacePreference(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public SpacePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(attrs,
new int[] { com.android.internal.R.attr.layout_height }, defStyleAttr, defStyleRes);
mHeight = a.getDimensionPixelSize(0, 0);
}
public void setHeight(int height) {
mHeight = height;
}
@Override
protected View onCreateView(ViewGroup parent) {
return new Space(getContext());
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mHeight);
view.setLayoutParams(params);
}
}