diff options
-rw-r--r-- | res/layout/app_header.xml | 2 | ||||
-rw-r--r-- | res/values/strings.xml | 19 | ||||
-rw-r--r-- | res/xml/installed_app_details.xml | 12 | ||||
-rw-r--r-- | src/com/android/settings/DataUsageSummary.java | 54 | ||||
-rwxr-xr-x | src/com/android/settings/applications/InstalledAppDetails.java | 93 |
5 files changed, 162 insertions, 18 deletions
diff --git a/res/layout/app_header.xml b/res/layout/app_header.xml index 8c3ca4b..d9453f4 100644 --- a/res/layout/app_header.xml +++ b/res/layout/app_header.xml @@ -36,7 +36,7 @@ android:layout_alignWithParentIfMissing="true" android:layout_centerVertical="true" android:textAppearance="@style/TextAppearance.Switch" - android:textColor="?android:attr/textColorPrimary" + android:textColor="@android:color/white" android:textAlignment="viewStart" /> <ImageView diff --git a/res/values/strings.xml b/res/values/strings.xml index 441034c..f2edaca 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -6040,11 +6040,17 @@ <string name="launch_by_default">Launch by default</string> <!-- 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> - <!-- Internal storage label [CHAR LIMIT=25] --> - <string name="storage_type_internal">internal memory</string> - <!-- External storage label [CHAR LIMIT=25] --> - <string name="storage_type_external">external memory</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> + + <!-- Summary describing internal storage for applications [CHAR LIMIT=25] --> + <string name="storage_type_internal">internal storage</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] --> <string name="notifications_enabled">On</string> @@ -6068,4 +6074,7 @@ <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] --> <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> diff --git a/res/xml/installed_app_details.xml b/res/xml/installed_app_details.xml index 05bea5c..2f72f5d 100644 --- a/res/xml/installed_app_details.xml +++ b/res/xml/installed_app_details.xml @@ -21,13 +21,13 @@ android:layout="@layout/installed_app_details" /> <Preference - android:key="notification_settings" - android:title="@string/notification_section_header" + android:key="storage_settings" + android:title="@string/storage_settings" android:selectable="true" /> <Preference - android:key="storage_settings" - android:title="@string/storage_settings" + android:key="data_settings" + android:title="@string/data_size_label" android:selectable="true" /> <Preference @@ -36,8 +36,8 @@ android:selectable="true" /> <Preference - android:key="data_settings" - android:title="@string/data_size_label" + android:key="notification_settings" + android:title="@string/notification_section_header" android:selectable="true" /> <Preference diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index cba0879..3ab1d23 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -62,6 +62,7 @@ import android.content.Intent; import android.content.Loader; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.UserInfo; import android.content.res.Resources; import android.content.res.TypedArray; @@ -120,6 +121,7 @@ import android.widget.ProgressBar; import android.widget.Spinner; import android.widget.Switch; import android.widget.TabHost; +import android.widget.Toast; import android.widget.TabHost.OnTabChangeListener; import android.widget.TabHost.TabContentFactory; 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.DataUsageChartListener; import com.android.settings.widget.ChartNetworkSeriesView; - import com.google.android.collect.Lists; import libcore.util.Objects; @@ -191,6 +192,8 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable "data_usage_disable_mobile_limit"; 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_SUMMARY = 3; @@ -281,6 +284,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable 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. */ @@ -332,6 +338,13 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mShowEthernet = true; } + mUidDetailProvider = new UidDetailProvider(context); + + Bundle arguments = getArguments(); + if (arguments != null) { + mShowAppImmediatePkg = arguments.getString(EXTRA_SHOW_APP_IMMEDIATE_PKG); + } + setHasOptionsMenu(true); } @@ -342,7 +355,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable final Context context = inflater.getContext(); final View view = inflater.inflate(R.layout.data_usage_summary, container, false); - mUidDetailProvider = new UidDetailProvider(context); mTabHost = (TabHost) view.findViewById(android.R.id.tabhost); mTabsContainer = (ViewGroup) view.findViewById(R.id.tabs_container); @@ -448,9 +460,34 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mListView.setOnItemClickListener(mListListener); mListView.setAdapter(mAdapter); + showRequestedAppIfNeeded(); + 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 public void onViewStateRestored(Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); @@ -639,6 +676,10 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable * only be assigned after initial layout is complete. */ 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 if (mChart.getLayoutTransition() != null) return; @@ -1887,6 +1928,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private static final String EXTRA_APP = "app"; 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; final Bundle args = new Bundle(); @@ -1897,7 +1943,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable fragment.setTargetFragment(parent, 0); final FragmentTransaction ft = parent.getFragmentManager().beginTransaction(); ft.add(fragment, TAG_APP_DETAILS); - ft.addToBackStack(TAG_APP_DETAILS); + if (addToBack) { + ft.addToBackStack(TAG_APP_DETAILS); + } ft.setBreadCrumbTitle( parent.getResources().getString(R.string.data_usage_app_summary_title)); ft.commitAllowingStateLoss(); diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java index 59dbd0e..7f20b32 100755 --- a/src/com/android/settings/applications/InstalledAppDetails.java +++ b/src/com/android/settings/applications/InstalledAppDetails.java @@ -19,23 +19,33 @@ package com.android.settings.applications; import android.app.Activity; import android.app.ActivityManager; import android.app.AlertDialog; +import android.app.LoaderManager.LoaderCallbacks; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.Loader; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; 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.os.AsyncTask; import android.os.Bundle; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.provider.Settings; +import android.text.format.DateUtils; +import android.text.format.Formatter; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -44,10 +54,14 @@ import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import com.android.settings.DataUsageSummary; +import com.android.settings.DataUsageSummary.AppItem; import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.Utils; 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.AppRow; import com.android.settings.notification.NotificationAppList.Backend; @@ -76,6 +90,8 @@ public class InstalledAppDetails extends AppInfoBase public static final int REQUEST_UNINSTALL = 0; 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_DISABLE = DLG_BASE + 2; private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; @@ -109,6 +125,9 @@ public class InstalledAppDetails extends AppInfoBase // Used for updating notification preference. private final Backend mBackend = new Backend(); + private ChartData mChartData; + private INetworkStatsSession mStatsSession; + private boolean handleDisableable(Button button) { boolean disableable = false; // Try to prevent the user from bricking their phone @@ -207,6 +226,37 @@ public class InstalledAppDetails extends AppInfoBase setHasOptionsMenu(true); 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) { @@ -223,8 +273,6 @@ public class InstalledAppDetails extends AppInfoBase mLaunchPreference.setOnPreferenceClickListener(this); mDataPreference = findPreference(KEY_DATA); mDataPreference.setOnPreferenceClickListener(this); - // Data isn't ready, lets just pull it for now. - getPreferenceScreen().removePreference(mDataPreference); } private void handleHeader() { @@ -384,6 +432,7 @@ public class InstalledAppDetails extends AppInfoBase mPm, context)); mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, mBackend)); + mDataPreference.setSummary(getDataSummary()); if (!mInitialized) { // First time init: are we displaying an uninstalled app? @@ -410,6 +459,18 @@ public class InstalledAppDetails extends AppInfoBase 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 protected AlertDialog createDialog(int id, int errorCode) { switch (id) { @@ -579,7 +640,13 @@ public class InstalledAppDetails extends AppInfoBase } else if (preference == mLaunchPreference) { startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); } 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 { 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() { @Override public void onReceive(Context context, Intent intent) { |