Merge "Unbanish memory screen and new UX" into mnc-dev

This commit is contained in:
Jason Monk
2015-05-26 16:55:42 +00:00
committed by Android (Google) Code Review
23 changed files with 635 additions and 455 deletions

View File

@@ -33,6 +33,7 @@ public class LayoutPreference extends Preference {
public LayoutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
setSelectable(false);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Preference, 0, 0);
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.util.MemInfoReader;
import com.android.settings.R;
import com.android.settings.Utils;
import java.io.IOException;
import java.io.InputStream;
@@ -57,8 +58,6 @@ public class ProcStatsData {
private IProcessStats mProcessStats;
private ProcessStats mStats;
private int mMemState;
private boolean mUseUss;
private long mDuration;
@@ -187,28 +186,28 @@ public class ProcStatsData {
ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime);
ProcStatsEntry osEntry;
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,
(long) (totalMem.sysMemNativeWeight / memTotalTime));
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
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,
(long) (totalMem.sysMemKernelWeight / memTotalTime));
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
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,
(long) (totalMem.sysMemZRamWeight / memTotalTime));
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
osPkg.addEntry(osEntry);
}
if (baseCacheRam > 0) {
osEntry = new ProcStatsEntry("os", 0,
osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
mContext.getString(R.string.process_stats_os_cache), memTotalTime,
baseCacheRam / 1024);
osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
@@ -296,7 +295,6 @@ public class ProcStatsData {
private void load() {
try {
mMemState = mProcessStats.getCurrentMemoryState();
ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
mStats = new ProcessStats(false);
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);

View File

@@ -23,6 +23,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import com.android.settings.R;
import com.android.settings.Utils;
import java.util.ArrayList;
@@ -89,10 +90,10 @@ public class ProcStatsPackageEntry implements Parcelable {
final int N = mEntries.size();
for (int i=0; i < N; i++) {
ProcStatsEntry entry = mEntries.get(i);
mBgDuration += entry.mBgDuration;
mBgDuration = Math.max(entry.mBgDuration, mBgDuration);
mAvgBgMem += entry.mAvgBgMem;
mBgWeight += entry.mBgWeight;
mRunDuration += entry.mRunDuration;
mRunDuration = Math.max(entry.mRunDuration, mRunDuration);
mAvgRunMem += entry.mAvgRunMem;
mRunWeight += entry.mRunWeight;
@@ -161,12 +162,15 @@ public class ProcStatsPackageEntry implements Parcelable {
// TODO: Find better place for this.
public static CharSequence getFrequency(float amount, Context context) {
if (amount> ALWAYS_THRESHOLD) {
return context.getString(R.string.always_running);
} else if (amount> SOMETIMES_THRESHOLD) {
return context.getString(R.string.sometimes_running);
if (amount > ALWAYS_THRESHOLD) {
return context.getString(R.string.always_running,
Utils.formatPercentage((int) (amount * 100)));
} else if (amount > SOMETIMES_THRESHOLD) {
return context.getString(R.string.sometimes_running,
Utils.formatPercentage((int) (amount * 100)));
} 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.os.Bundle;
import android.os.Process;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.ArrayMap;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
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.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsEntry.Service;
import java.util.ArrayList;
@@ -54,36 +57,35 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
public class ProcessStatsDetail extends SettingsPreferenceFragment
implements Button.OnClickListener {
public class ProcessStatsDetail extends SettingsPreferenceFragment {
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_USE_USS = "use_uss";
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_MAX_MEMORY_USAGE = "max_memory_usage";
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 PackageManager mPm;
private DevicePolicyManager mDpm;
private MenuItem mForceStop;
private ProcStatsPackageEntry mApp;
private boolean mUseUss;
private double mWeightToRam;
private long mTotalTime;
private long mOnePercentTime;
private Button mForceStopButton;
private Button mReportButton;
private LinearColorBar mColorBar;
private double mMaxMemoryUsage;
@@ -98,7 +100,6 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
final Bundle args = getArguments();
mApp = args.getParcelable(EXTRA_PACKAGE_ENTRY);
mApp.retrieveUiData(getActivity(), mPm);
mUseUss = args.getBoolean(EXTRA_USE_USS);
mWeightToRam = args.getDouble(EXTRA_WEIGHT_TO_RAM);
mTotalTime = args.getLong(EXTRA_TOTAL_TIME);
mMaxMemoryUsage = args.getDouble(EXTRA_MAX_MEMORY_USAGE);
@@ -107,6 +108,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
mServiceMap.clear();
createDetails();
setHasOptionsMenu(true);
}
@Override
@@ -115,7 +117,8 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
AppHeader.createAppHeader(this,
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
@@ -126,8 +129,8 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
@Override
public void onResume() {
super.onResume();
checkForceStop();
checkForceStop();
updateRunningServices();
}
@@ -173,55 +176,42 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
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.
boolean statsForeground = mApp.mRunWeight > mApp.mBgWeight;
float avgRatio = (float) ((statsForeground ? mApp.mRunWeight : mApp.mBgWeight)
* mWeightToRam / mMaxMemoryUsage);
float maxRatio = (float) ((statsForeground ? mApp.mMaxRunMem : mApp.mMaxBgMem)
* mTotalScale * 1024 / mMaxMemoryUsage - avgRatio);
float remainingRatio = 1 - avgRatio - maxRatio;
double avgRam = (statsForeground ? mApp.mRunWeight : mApp.mBgWeight) * mWeightToRam;
float avgRatio = (float) (avgRam / mMaxMemoryUsage);
float remainingRatio = 1 - avgRatio;
mColorBar = (LinearColorBar) headerLayout.findViewById(R.id.color_bar);
Context context = getActivity();
mColorBar.setColors(context.getColor(R.color.memory_avg_use),
context.getColor(R.color.memory_max_use),
mColorBar.setColors( context.getColor(R.color.memory_max_use), 0,
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) {
doAction((Integer) v.getTag());
@Override
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) {
switch (action) {
case ACTION_FORCE_STOP:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_FORCE_STOP:
killProcesses();
break;
return true;
}
return false;
}
final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
@@ -250,8 +240,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
Collections.sort(entries, sEntryCompare);
for (int ie = 0; ie < entries.size(); ie++) {
ProcStatsEntry entry = entries.get(ie);
PreferenceCategory processPref = new PreferenceCategory(getActivity());
processPref.setLayoutResource(R.layout.process_preference_category);
Preference processPref = new Preference(getActivity());
processPref.setTitle(entry.mLabel);
long duration = Math.max(entry.mRunDuration, entry.mBgDuration);
@@ -259,11 +248,10 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
(long) (entry.mBgWeight * mWeightToRam));
String memoryString = Formatter.formatShortFileSize(getActivity(), memoryUse);
CharSequence frequency = ProcStatsPackageEntry.getFrequency(duration
/ (float)mTotalTime, getActivity());
/ (float) mTotalTime, getActivity());
processPref.setSummary(
getString(R.string.memory_use_running_format, memoryString, frequency));
getPreferenceScreen().addPreference(processPref);
fillServicesSection(entry, processPref);
}
}
@@ -423,12 +411,14 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
am.forceStopPackage(ent.mPackages.get(j));
}
}
checkForceStop();
}
private void checkForceStop() {
if (mForceStop == null) {
return;
}
if (mApp.mEntries.get(0).mUid < Process.FIRST_APPLICATION_UID) {
mForceStopButton.setEnabled(false);
mForceStop.setVisible(false);
return;
}
boolean isStarted = false;
@@ -437,7 +427,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
for (int j=0; j<ent.mPackages.size(); j++) {
String pkg = ent.mPackages.get(j);
if (mDpm.packageHasActiveAdmins(pkg)) {
mForceStopButton.setEnabled(false);
mForceStop.setEnabled(false);
return;
}
try {
@@ -450,7 +440,7 @@ public class ProcessStatsDetail extends SettingsPreferenceFragment
}
}
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.preference.Preference;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.android.settings.R;
@@ -30,11 +30,9 @@ import com.android.settings.R;
public class ProcessStatsPreference extends Preference {
private ProcStatsPackageEntry mEntry;
private final int mAvgColor;
private final int mMaxColor;
private final int mColor;
private final int mRemainingColor;
private float mAvgRatio;
private float mMaxRatio;
private float mRatio;
private float mRemainingRatio;
public ProcessStatsPreference(Context context) {
@@ -53,13 +51,12 @@ public class ProcessStatsPreference extends Preference {
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
setLayoutResource(R.layout.app_item_linear_color);
mAvgColor = context.getColor(R.color.memory_avg_use);
mMaxColor = context.getColor(R.color.memory_max_use);
mColor = context.getColor(R.color.memory_max_use);
mRemainingColor = context.getColor(R.color.memory_remaining);
}
public void init(ProcStatsPackageEntry entry, PackageManager pm, double maxMemory,
double weightToRam, double totalScale) {
double weightToRam, double totalScale, boolean avg) {
mEntry = entry;
setTitle(TextUtils.isEmpty(entry.mUiLabel) ? entry.mPackage : entry.mUiLabel);
if (entry.mUiTargetApp != null) {
@@ -68,13 +65,11 @@ public class ProcessStatsPreference extends Preference {
setIcon(new ColorDrawable(0));
}
boolean statsForeground = entry.mRunWeight > entry.mBgWeight;
setSummary(entry.mRunDuration > entry.mBgDuration ? entry.getRunningFrequency(getContext())
: entry.getBackgroundFrequency(getContext()));
mAvgRatio = (float) ((statsForeground ? entry.mRunWeight : entry.mBgWeight)
* weightToRam / maxMemory);
mMaxRatio = (float) ((statsForeground ? entry.mMaxRunMem : entry.mMaxBgMem)
* totalScale * 1024 / maxMemory - mAvgRatio);
mRemainingRatio = 1 - mAvgRatio - mMaxRatio;
double amount = avg ? (statsForeground ? entry.mRunWeight : entry.mBgWeight) * weightToRam
: (statsForeground ? entry.mMaxRunMem : entry.mMaxBgMem) * totalScale * 1024;
setSummary(Formatter.formatShortFileSize(getContext(), (long) amount));
mRatio = (float) (amount / maxMemory);
mRemainingRatio = 1 - mRatio;
}
public ProcStatsPackageEntry getEntry() {
@@ -86,7 +81,7 @@ public class ProcessStatsPreference extends Preference {
super.onBindView(view);
LinearColorBar linearColorBar = (LinearColorBar) view.findViewById(R.id.linear_color_bar);
linearColorBar.setColors(mAvgColor, mMaxColor, mRemainingColor);
linearColorBar.setRatios(mAvgRatio, mMaxRatio, mRemainingRatio);
linearColorBar.setColors(mColor, mColor, mRemainingColor);
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;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import android.text.format.Formatter;
import android.util.Log;
import android.util.TimeUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.TextView;
import com.android.internal.app.ProcessStats;
import com.android.internal.logging.MetricsLogger;
import com.android.settings.InstrumentedPreferenceFragment;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.applications.ProcStatsData.MemInfo;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ProcessStatsUi extends InstrumentedPreferenceFragment {
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";
public class ProcessStatsUi extends ProcessStatsBase {
static final String TAG = "ProcessStatsUi";
static final boolean DEBUG = false;
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_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 static final int MENU_SHOW_AVG = Menu.FIRST;
private static final int MENU_SHOW_MAX = Menu.FIRST + 1;
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 long memTotalTime;
private int mStatsType;
// 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;
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;
private boolean mShowMax;
private MenuItem mMenuAvg;
private MenuItem mMenuMax;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mStatsManager = new ProcStatsData(getActivity(), icicle != null);
mPm = getActivity().getPackageManager();
addPreferencesFromResource(R.xml.process_stats_summary);
addPreferencesFromResource(R.xml.process_stats_ui);
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);
}
@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
protected int getMetricsCategory() {
return MetricsLogger.APPLICATIONS_PROCESS_STATS_UI;
}
@Override
public void onResume() {
super.onResume();
mStatsManager.refreshStats(false);
refreshUi();
}
@Override
public void onSaveInstanceState(Bundle 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
@@ -192,142 +110,19 @@ public class ProcessStatsUi extends InstrumentedPreferenceFragment {
ProcessStatsPreference pgp = (ProcessStatsPreference) preference;
Bundle args = new Bundle();
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,
mStatsManager.getMemInfo().weightToRam);
args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, memTotalTime);
args.putDouble(ProcessStatsDetail.EXTRA_MAX_MEMORY_USAGE, mMaxMemoryUsage);
args.putDouble(ProcessStatsDetail.EXTRA_TOTAL_SCALE, mStatsManager.getMemInfo().totalScale);
memInfo.weightToRam);
args.putLong(ProcessStatsDetail.EXTRA_TOTAL_TIME, memInfo.memTotalTime);
args.putDouble(ProcessStatsDetail.EXTRA_MAX_MEMORY_USAGE,
memInfo.usedWeight * memInfo.weightToRam);
args.putDouble(ProcessStatsDetail.EXTRA_TOTAL_SCALE, memInfo.totalScale);
((SettingsActivity) getActivity()).startPreferencePanel(
ProcessStatsDetail.class.getName(), args, R.string.details_title, null, null, 0);
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
* 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();
}
private void refreshUi() {
updateMenus();
public void refreshUi() {
mAppListGroup.removeAll();
mAppListGroup.setOrderingAsAdded(false);
mHeader.setOrder(-1);
mAppListGroup.addPreference(mHeader);
final long elapsedTime = mStatsManager.getElapsedTime();
mAppListGroup.setTitle(mShowMax ? R.string.maximum_memory_use
: R.string.average_memory_use);
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();
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();
// Update everything and get the absolute maximum of memory usage for scaling.
mMaxMemoryUsage = 0;
for (int i=0, N=pkgEntries.size(); i<N; i++) {
ProcStatsPackageEntry pkg = pkgEntries.get(i);
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
// running as multiple processes will have only one entry representing all of them.
if (DEBUG) Log.d(TAG, "-------------------- BUILDING UI");
// Find where we should stop. Because we have two properties we are looking at,
// we need to go from the back looking for the first place either holds.
int end = pkgEntries.size()-1;
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++) {
double maxMemory = mShowMax ? memInfo.realTotalRam
: memInfo.usedWeight * memInfo.weightToRam;
for (int i = 0; i < pkgEntries.size(); i++) {
ProcStatsPackageEntry pkg = pkgEntries.get(i);
ProcessStatsPreference pref = new ProcessStatsPreference(context);
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);
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);
}
}