Add data item to app info screen

Add link from app info to directly viewing data usage for that app.

Also do some minor tweaks to get ordering and summaries right on
the app info screen.

Bug: 19511439
Change-Id: Ic50dc24bf9a5c1fe6a7aa076772915ba61168fac
This commit is contained in:
Jason Monk
2015-03-17 10:06:58 -04:00
parent f9b806e5e9
commit 4bb075bc4f
5 changed files with 166 additions and 22 deletions

View File

@@ -36,7 +36,7 @@
android:layout_alignWithParentIfMissing="true" android:layout_alignWithParentIfMissing="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:textAppearance="@style/TextAppearance.Switch" android:textAppearance="@style/TextAppearance.Switch"
android:textColor="?android:attr/textColorPrimary" android:textColor="@android:color/white"
android:textAlignment="viewStart" /> android:textAlignment="viewStart" />
<ImageView <ImageView

View File

@@ -6040,11 +6040,17 @@
<string name="launch_by_default">Launch by default</string> <string name="launch_by_default">Launch by default</string>
<!-- Summary for app storage preference [CHAR LIMIT=15] --> <!-- Summary for app storage preference [CHAR LIMIT=15] -->
<string name="storage_summary_format"><xliff:g id="size" example="30.00MB">%1$s</xliff:g> used in <xliff:g id="storage_type" example="internal memory">%2$s</xliff:g></string> <string name="storage_summary_format"><xliff:g id="size" example="30.00MB">%1$s</xliff:g> used in <xliff:g id="storage_type" example="internal storage">%2$s</xliff:g></string>
<!-- Internal storage label [CHAR LIMIT=25] -->
<string name="storage_type_internal">internal memory</string> <!-- Summary describing internal storage for applications [CHAR LIMIT=25] -->
<!-- External storage label [CHAR LIMIT=25] --> <string name="storage_type_internal">internal storage</string>
<string name="storage_type_external">external memory</string> <!-- Summary describing external storage for applications [CHAR LIMIT=25] -->
<string name="storage_type_external">external storage</string>
<!-- Title for data usage screen when entered from app info [CHAR LIMIT=30] -->
<string name="app_data_usage">App data usage</string>
<!-- Summary for data usage preference [CHAR LIMIT=15] -->
<string name="data_summary_format"><xliff:g id="size" example="30.00MB">%1$s</xliff:g> used since <xliff:g id="date" example="Jan 12">%2$s</xliff:g></string>
<!-- App notification summary with notifications enabled [CHAR LIMIT=40] --> <!-- App notification summary with notifications enabled [CHAR LIMIT=40] -->
<string name="notifications_enabled">On</string> <string name="notifications_enabled">On</string>
@@ -6068,4 +6074,7 @@
<!-- Launch defaults preference summary with none set [CHAR LIMIT=40] --> <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
<string name="launch_defaults_none">No defaults set</string> <string name="launch_defaults_none">No defaults set</string>
<!-- Warning toast shown when data usage screen can't find specified app -->
<string name="unknown_app">Unknown app</string>
</resources> </resources>

View File

@@ -20,26 +20,26 @@
android:key="header_view" android:key="header_view"
android:layout="@layout/installed_app_details" /> android:layout="@layout/installed_app_details" />
<Preference
android:key="notification_settings"
android:title="@string/notification_section_header"
android:selectable="true" />
<Preference <Preference
android:key="storage_settings" android:key="storage_settings"
android:title="@string/storage_settings" android:title="@string/storage_settings"
android:selectable="true" /> android:selectable="true" />
<Preference
android:key="permission_settings"
android:title="@string/permissions_label"
android:selectable="true" />
<Preference <Preference
android:key="data_settings" android:key="data_settings"
android:title="@string/data_size_label" android:title="@string/data_size_label"
android:selectable="true" /> android:selectable="true" />
<Preference
android:key="permission_settings"
android:title="@string/permissions_label"
android:selectable="true" />
<Preference
android:key="notification_settings"
android:title="@string/notification_section_header"
android:selectable="true" />
<Preference <Preference
android:key="preferred_settings" android:key="preferred_settings"
android:title="@string/launch_by_default" android:title="@string/launch_by_default"

View File

@@ -62,6 +62,7 @@ import android.content.Intent;
import android.content.Loader; import android.content.Loader;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
@@ -120,6 +121,7 @@ import android.widget.ProgressBar;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TabHost; import android.widget.TabHost;
import android.widget.Toast;
import android.widget.TabHost.OnTabChangeListener; import android.widget.TabHost.OnTabChangeListener;
import android.widget.TabHost.TabContentFactory; import android.widget.TabHost.TabContentFactory;
import android.widget.TabHost.TabSpec; import android.widget.TabHost.TabSpec;
@@ -142,7 +144,6 @@ import com.android.settings.sim.SimSettings;
import com.android.settings.widget.ChartDataUsageView; import com.android.settings.widget.ChartDataUsageView;
import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener; import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener;
import com.android.settings.widget.ChartNetworkSeriesView; import com.android.settings.widget.ChartNetworkSeriesView;
import com.google.android.collect.Lists; import com.google.android.collect.Lists;
import libcore.util.Objects; import libcore.util.Objects;
@@ -191,6 +192,8 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
"data_usage_disable_mobile_limit"; "data_usage_disable_mobile_limit";
private static final String DATA_USAGE_CYCLE_KEY = "data_usage_cycle"; private static final String DATA_USAGE_CYCLE_KEY = "data_usage_cycle";
public static final String EXTRA_SHOW_APP_IMMEDIATE_PKG = "showAppImmediatePkg";
private static final int LOADER_CHART_DATA = 2; private static final int LOADER_CHART_DATA = 2;
private static final int LOADER_SUMMARY = 3; private static final int LOADER_SUMMARY = 3;
@@ -281,6 +284,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
private UidDetailProvider mUidDetailProvider; private UidDetailProvider mUidDetailProvider;
// Indicates request to show app immediately rather than list.
private String mShowAppImmediatePkg;
/** /**
* Local cache of data enabled for subId, used to work around delays. * Local cache of data enabled for subId, used to work around delays.
*/ */
@@ -332,6 +338,13 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
mShowEthernet = true; mShowEthernet = true;
} }
mUidDetailProvider = new UidDetailProvider(context);
Bundle arguments = getArguments();
if (arguments != null) {
mShowAppImmediatePkg = arguments.getString(EXTRA_SHOW_APP_IMMEDIATE_PKG);
}
setHasOptionsMenu(true); setHasOptionsMenu(true);
} }
@@ -342,7 +355,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
final Context context = inflater.getContext(); final Context context = inflater.getContext();
final View view = inflater.inflate(R.layout.data_usage_summary, container, false); final View view = inflater.inflate(R.layout.data_usage_summary, container, false);
mUidDetailProvider = new UidDetailProvider(context);
mTabHost = (TabHost) view.findViewById(android.R.id.tabhost); mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container); mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container);
@@ -448,9 +460,34 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
mListView.setOnItemClickListener(mListListener); mListView.setOnItemClickListener(mListListener);
mListView.setAdapter(mAdapter); mListView.setAdapter(mAdapter);
showRequestedAppIfNeeded();
return view; return view;
} }
private void showRequestedAppIfNeeded() {
if (mShowAppImmediatePkg == null) {
return;
}
try {
int uid = getActivity().getPackageManager().getPackageUid(mShowAppImmediatePkg,
UserHandle.myUserId());
AppItem app = new AppItem(uid);
app.addUid(uid);
final UidDetail detail = mUidDetailProvider.getUidDetail(app.key, true);
// When we are going straight to an app then we are coming from App Info and want
// a header at the top.
AppHeader.createAppHeader(getActivity(), detail.icon, detail.label, null);
AppDetailsFragment.show(DataUsageSummary.this, app, detail.label, false);
} catch (NameNotFoundException e) {
Log.w(TAG, "Could not find " + mShowAppImmediatePkg, e);
Toast.makeText(getActivity(), getString(R.string.unknown_app), Toast.LENGTH_LONG)
.show();
getActivity().finish();
}
}
@Override @Override
public void onViewStateRestored(Bundle savedInstanceState) { public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState); super.onViewStateRestored(savedInstanceState);
@@ -639,6 +676,10 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
* only be assigned after initial layout is complete. * only be assigned after initial layout is complete.
*/ */
private void ensureLayoutTransitions() { private void ensureLayoutTransitions() {
if (mShowAppImmediatePkg != null) {
// If we are skipping right to showing an app, we don't care about transitions.
return;
}
// skip when already setup // skip when already setup
if (mChart.getLayoutTransition() != null) return; if (mChart.getLayoutTransition() != null) return;
@@ -1887,6 +1928,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
private static final String EXTRA_APP = "app"; private static final String EXTRA_APP = "app";
public static void show(DataUsageSummary parent, AppItem app, CharSequence label) { public static void show(DataUsageSummary parent, AppItem app, CharSequence label) {
show(parent, app, label, true);
}
public static void show(DataUsageSummary parent, AppItem app, CharSequence label,
boolean addToBack) {
if (!parent.isAdded()) return; if (!parent.isAdded()) return;
final Bundle args = new Bundle(); final Bundle args = new Bundle();
@@ -1897,7 +1943,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable
fragment.setTargetFragment(parent, 0); fragment.setTargetFragment(parent, 0);
final FragmentTransaction ft = parent.getFragmentManager().beginTransaction(); final FragmentTransaction ft = parent.getFragmentManager().beginTransaction();
ft.add(fragment, TAG_APP_DETAILS); ft.add(fragment, TAG_APP_DETAILS);
ft.addToBackStack(TAG_APP_DETAILS); if (addToBack) {
ft.addToBackStack(TAG_APP_DETAILS);
}
ft.setBreadCrumbTitle( ft.setBreadCrumbTitle(
parent.getResources().getString(R.string.data_usage_app_summary_title)); parent.getResources().getString(R.string.data_usage_app_summary_title));
ft.commitAllowingStateLoss(); ft.commitAllowingStateLoss();

View File

@@ -19,23 +19,33 @@ package com.android.settings.applications;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.Loader;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.provider.Settings; import android.provider.Settings;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
@@ -44,10 +54,14 @@ import android.widget.Button;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.android.settings.DataUsageSummary;
import com.android.settings.DataUsageSummary.AppItem;
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.Utils;
import com.android.settings.applications.ApplicationsState.AppEntry; import com.android.settings.applications.ApplicationsState.AppEntry;
import com.android.settings.net.ChartData;
import com.android.settings.net.ChartDataLoader;
import com.android.settings.notification.NotificationAppList; import com.android.settings.notification.NotificationAppList;
import com.android.settings.notification.NotificationAppList.AppRow; import com.android.settings.notification.NotificationAppList.AppRow;
import com.android.settings.notification.NotificationAppList.Backend; import com.android.settings.notification.NotificationAppList.Backend;
@@ -76,6 +90,8 @@ public class InstalledAppDetails extends AppInfoBase
public static final int REQUEST_UNINSTALL = 0; public static final int REQUEST_UNINSTALL = 0;
private static final int SUB_INFO_FRAGMENT = 1; private static final int SUB_INFO_FRAGMENT = 1;
private static final int LOADER_CHART_DATA = 2;
private static final int DLG_FORCE_STOP = DLG_BASE + 1; private static final int DLG_FORCE_STOP = DLG_BASE + 1;
private static final int DLG_DISABLE = DLG_BASE + 2; private static final int DLG_DISABLE = DLG_BASE + 2;
private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
@@ -109,6 +125,9 @@ public class InstalledAppDetails extends AppInfoBase
// Used for updating notification preference. // Used for updating notification preference.
private final Backend mBackend = new Backend(); private final Backend mBackend = new Backend();
private ChartData mChartData;
private INetworkStatsSession mStatsSession;
private boolean handleDisableable(Button button) { private boolean handleDisableable(Button button) {
boolean disableable = false; boolean disableable = false;
// Try to prevent the user from bricking their phone // Try to prevent the user from bricking their phone
@@ -207,6 +226,37 @@ public class InstalledAppDetails extends AppInfoBase
setHasOptionsMenu(true); setHasOptionsMenu(true);
addPreferencesFromResource(R.xml.installed_app_details); addPreferencesFromResource(R.xml.installed_app_details);
INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
try {
mStatsSession = statsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@Override
public void onResume() {
super.onResume();
AppItem app = new AppItem(mAppEntry.info.uid);
app.addUid(mAppEntry.info.uid);
getLoaderManager().restartLoader(LOADER_CHART_DATA,
ChartDataLoader.buildArgs(NetworkTemplate.buildTemplateMobileWildcard(), app),
mDataCallbacks);
}
@Override
public void onPause() {
getLoaderManager().destroyLoader(LOADER_CHART_DATA);
super.onPause();
}
@Override
public void onDestroy() {
TrafficStats.closeQuietly(mStatsSession);
super.onDestroy();
} }
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
@@ -223,8 +273,6 @@ public class InstalledAppDetails extends AppInfoBase
mLaunchPreference.setOnPreferenceClickListener(this); mLaunchPreference.setOnPreferenceClickListener(this);
mDataPreference = findPreference(KEY_DATA); mDataPreference = findPreference(KEY_DATA);
mDataPreference.setOnPreferenceClickListener(this); mDataPreference.setOnPreferenceClickListener(this);
// Data isn't ready, lets just pull it for now.
getPreferenceScreen().removePreference(mDataPreference);
} }
private void handleHeader() { private void handleHeader() {
@@ -384,6 +432,7 @@ public class InstalledAppDetails extends AppInfoBase
mPm, context)); mPm, context));
mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context,
mBackend)); mBackend));
mDataPreference.setSummary(getDataSummary());
if (!mInitialized) { if (!mInitialized) {
// First time init: are we displaying an uninstalled app? // First time init: are we displaying an uninstalled app?
@@ -410,6 +459,18 @@ public class InstalledAppDetails extends AppInfoBase
return true; return true;
} }
private CharSequence getDataSummary() {
if (mChartData != null) {
long totalBytes = mChartData.detail.getTotalBytes();
Context context = getActivity();
return getString(R.string.data_summary_format,
Formatter.formatFileSize(context, totalBytes),
DateUtils.formatDateTime(context, mChartData.detail.getStart(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH));
}
return getString(R.string.computing_size);
}
@Override @Override
protected AlertDialog createDialog(int id, int errorCode) { protected AlertDialog createDialog(int id, int errorCode) {
switch (id) { switch (id) {
@@ -579,7 +640,13 @@ public class InstalledAppDetails extends AppInfoBase
} else if (preference == mLaunchPreference) { } else if (preference == mLaunchPreference) {
startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle());
} else if (preference == mDataPreference) { } else if (preference == mDataPreference) {
// Not yet. Bundle args = new Bundle();
args.putString(DataUsageSummary.EXTRA_SHOW_APP_IMMEDIATE_PKG,
mAppEntry.info.packageName);
SettingsActivity sa = (SettingsActivity) getActivity();
sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1,
getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT);
} else { } else {
return false; return false;
} }
@@ -627,6 +694,26 @@ public class InstalledAppDetails extends AppInfoBase
} }
} }
private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() {
@Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
return new ChartDataLoader(getActivity(), mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
mChartData = data;
mDataPreference.setSummary(getDataSummary());
}
@Override
public void onLoaderReset(Loader<ChartData> loader) {
mChartData = null;
mDataPreference.setSummary(getDataSummary());
}
};
private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {