diff options
author | Jeff Sharkey <jsharkey@android.com> | 2011-08-04 21:17:23 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2011-08-04 21:20:03 -0700 |
commit | d39c6e4083f1212519d5dc14f64132a10f2b7c7a (patch) | |
tree | 1d6383be1b378204301ec20d2d7b957f9d23ed9d | |
parent | ce919e40c9def53dad0dfd541060147939024827 (diff) | |
download | packages_apps_settings-d39c6e4083f1212519d5dc14f64132a10f2b7c7a.zip packages_apps_settings-d39c6e4083f1212519d5dc14f64132a10f2b7c7a.tar.gz packages_apps_settings-d39c6e4083f1212519d5dc14f64132a10f2b7c7a.tar.bz2 |
Data usage app icons and details, chart labels.
Add app icons into both summary list and details pane. Also show list
of all applications merged under a UID. Draw dates on chart axis, and
avoid flashing policy sweeps when switching networks in detail mode.
Bug: 5087283, 5038812
Change-Id: I1dcd03ca85b517f8726452af8a46b4be9b3d20f1
-rw-r--r-- | AndroidManifest.xml | 3 | ||||
-rw-r--r-- | res/layout/data_usage_app_title.xml | 22 | ||||
-rw-r--r-- | res/layout/data_usage_chart.xml | 2 | ||||
-rw-r--r-- | res/layout/data_usage_detail.xml | 21 | ||||
-rw-r--r-- | res/layout/data_usage_header.xml | 2 | ||||
-rw-r--r-- | res/layout/data_usage_item.xml | 10 | ||||
-rw-r--r-- | res/values/strings.xml | 2 | ||||
-rw-r--r-- | src/com/android/settings/DataUsageSummary.java | 120 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartGridView.java | 56 | ||||
-rw-r--r-- | src/com/android/settings/widget/ChartSweepView.java | 1 | ||||
-rw-r--r-- | src/com/android/settings/widget/DataUsageChartView.java | 1 |
11 files changed, 183 insertions, 57 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9b2391a..a99eb66 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1185,7 +1185,8 @@ </activity> <activity android:name="Settings$DataUsageSummaryActivity" - android:label="@string/data_usage_summary_title"> + android:label="@string/data_usage_summary_title" + android:uiOptions="none"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> diff --git a/res/layout/data_usage_app_title.xml b/res/layout/data_usage_app_title.xml new file mode 100644 index 0000000..baaa685 --- /dev/null +++ b/res/layout/data_usage_app_title.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + android:ellipsize="marquee" + android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/res/layout/data_usage_chart.xml b/res/layout/data_usage_chart.xml index 3ae8a03..c1f6c27 100644 --- a/res/layout/data_usage_chart.xml +++ b/res/layout/data_usage_chart.xml @@ -30,7 +30,7 @@ settings:primaryDrawable="@drawable/data_grid_primary" settings:secondaryDrawable="@drawable/data_grid_secondary" settings:borderDrawable="@drawable/data_grid_border" - settings:labelColor="#24aae1" /> + settings:labelColor="#667bb5" /> <com.android.settings.widget.ChartNetworkSeriesView android:id="@+id/series" diff --git a/res/layout/data_usage_detail.xml b/res/layout/data_usage_detail.xml index 2c8e490..8d0c0cc 100644 --- a/res/layout/data_usage_detail.xml +++ b/res/layout/data_usage_detail.xml @@ -20,17 +20,20 @@ android:layout_height="match_parent" android:orientation="vertical"> - <TextView - android:id="@+id/app_title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="16dip" /> + <ImageView + android:id="@+id/app_icon" + android:layout_width="48dip" + android:layout_height="48dip" + android:layout_marginLeft="16dip" + android:scaleType="centerInside" /> - <TextView - android:id="@+id/app_subtitle" + <LinearLayout + android:id="@+id/app_titles" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="16dip" /> + android:layout_height="match_parent" + android:layout_marginLeft="16dip" + android:layout_marginTop="8dip" + android:orientation="vertical" /> <Button android:id="@+id/app_settings" diff --git a/res/layout/data_usage_header.xml b/res/layout/data_usage_header.xml index 9602898..528fc34 100644 --- a/res/layout/data_usage_header.xml +++ b/res/layout/data_usage_header.xml @@ -43,6 +43,8 @@ android:paddingRight="16dip" android:paddingTop="8dip" android:paddingBottom="8dip" + android:singleLine="true" + android:ellipsize="marquee" android:textAppearance="?android:attr/textAppearanceSmall" /> <TextView diff --git a/res/layout/data_usage_item.xml b/res/layout/data_usage_item.xml index 36f8b4d..b41d486 100644 --- a/res/layout/data_usage_item.xml +++ b/res/layout/data_usage_item.xml @@ -21,7 +21,15 @@ android:paddingRight="16dip" android:paddingTop="8dip" android:paddingBottom="8dip" - android:columnCount="2"> + android:columnCount="3"> + + <ImageView + android:id="@android:id/icon" + android:layout_width="48dip" + android:layout_height="48dip" + android:layout_rowSpan="2" + android:layout_marginRight="8dip" + android:scaleType="centerInside" /> <TextView android:id="@android:id/title" diff --git a/res/values/strings.xml b/res/values/strings.xml index 7315863..3684dcd 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3465,7 +3465,7 @@ found in the list of installed applications.</string> <!-- Combination of total network bytes sent and received by an application. [CHAR LIMIT=NONE] --> <string name="data_usage_received_sent"><xliff:g id="received" example="128KB">%1$s</xliff:g> received, <xliff:g id="sent" example="1.3GB">%2$s</xliff:g> sent</string> <!-- Label displaying total network data transferred during a specific time period. [CHAR LIMIT=64] --> - <string name="data_usage_total_during_range">Data usage: <xliff:g id="total" example="128KB">%1$s</xliff:g> between <xliff:g id="range" example="Jul 1 - Jul 31">%2$s</xliff:g></string> + <string name="data_usage_total_during_range">Data usage: <xliff:g id="total" example="128KB">%1$s</xliff:g> on <xliff:g id="range" example="Jul 1 - Jul 31">%2$s</xliff:g></string> <!-- Button at the bottom of the CryptKeeper screen to make an emergency call. --> <string name="cryptkeeper_emergency_call">Emergency call</string> diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index 45793b1..bccc5a5 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -37,6 +37,9 @@ import static android.net.NetworkTemplate.buildTemplateMobile3gLower; import static android.net.NetworkTemplate.buildTemplateMobile4g; import static android.net.NetworkTemplate.buildTemplateMobileAll; import static android.net.NetworkTemplate.buildTemplateWifi; +import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; +import static android.text.format.DateUtils.FORMAT_SHOW_DATE; +import static android.text.format.Time.TIMEZONE_UTC; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.animation.LayoutTransition; @@ -57,6 +60,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; +import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; @@ -78,7 +82,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Formatter; -import android.text.format.Time; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -97,6 +100,7 @@ import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.NumberPicker; @@ -190,8 +194,8 @@ public class DataUsageSummary extends Fragment { private TextView mEmpty; private View mAppDetail; - private TextView mAppTitle; - private TextView mAppSubtitle; + private ImageView mAppIcon; + private ViewGroup mAppTitles; private Button mAppSettings; private LinearLayout mAppSwitches; @@ -295,8 +299,8 @@ public class DataUsageSummary extends Fragment { { // bind app detail controls mAppDetail = mHeader.findViewById(R.id.app_detail); - mAppTitle = (TextView) mAppDetail.findViewById(R.id.app_title); - mAppSubtitle = (TextView) mAppDetail.findViewById(R.id.app_subtitle); + mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon); + mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles); mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches); mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings); @@ -643,6 +647,10 @@ public class DataUsageSummary extends Fragment { * depending on {@link #isAppDetailMode()}. */ private void updateAppDetail() { + final Context context = getActivity(); + final PackageManager pm = context.getPackageManager(); + final LayoutInflater inflater = getActivity().getLayoutInflater(); + if (isAppDetailMode()) { mAppDetail.setVisibility(View.VISIBLE); mCycleAdapter.setChangeVisible(false); @@ -658,8 +666,18 @@ public class DataUsageSummary extends Fragment { // remove warning/limit sweeps while in detail mode mChart.bindNetworkPolicy(null); - final PackageManager pm = getActivity().getPackageManager(); - mAppTitle.setText(pm.getNameForUid(mUid)); + // show icon and all labels appearing under this app + final UidDetail detail = resolveDetailForUid(context, mUid); + mAppIcon.setImageDrawable(detail.icon); + + mAppTitles.removeAllViews(); + if (detail.detailLabels != null) { + for (CharSequence label : detail.detailLabels) { + mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label)); + } + } else { + mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label)); + } // enable settings button when package provides it // TODO: target torwards entire UID instead of just first package @@ -693,7 +711,6 @@ public class DataUsageSummary extends Fragment { updateDetailData(); - final Context context = getActivity(); if (NetworkPolicyManager.isUidValidForPolicy(context, mUid) && !getRestrictBackground() && isBandwidthControlEnabled()) { mAppRestrictView.setVisibility(View.VISIBLE); @@ -814,7 +831,9 @@ public class DataUsageSummary extends Fragment { if (isNetworkPolicyModifiable()) { mDisableAtLimitView.setVisibility(View.VISIBLE); mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED); - mChart.bindNetworkPolicy(policy); + if (!isAppDetailMode()) { + mChart.bindNetworkPolicy(policy); + } } else { // controls are disabled; don't bind warning/limit sweeps @@ -998,11 +1017,6 @@ public class DataUsageSummary extends Fragment { if (isAppDetailMode()) { if (mDetailHistory != null) { entry = mDetailHistory.getValues(start, end, now, null); - - mAppSubtitle.setText( - getString(R.string.data_usage_received_sent, - Formatter.formatFileSize(context, entry.rxBytes), - Formatter.formatFileSize(context, entry.txBytes))); } else { entry = null; } @@ -1015,12 +1029,11 @@ public class DataUsageSummary extends Fragment { // kick off loader for detailed stats getLoaderManager().restartLoader(LOADER_SUMMARY, SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryForAllUid); - } final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0; final String totalPhrase = Formatter.formatFileSize(context, totalBytes); - final String rangePhrase = formatDateRange(context, start, end, null); + final String rangePhrase = formatDateRange(context, start, end, false); mUsageSummary.setText( getString(R.string.data_usage_total_during_range, totalPhrase, rangePhrase)); @@ -1104,7 +1117,7 @@ public class DataUsageSummary extends Fragment { } public CycleItem(Context context, long start, long end) { - this.label = formatDateRange(context, start, end, Time.TIMEZONE_UTC); + this.label = formatDateRange(context, start, end, true); this.start = start; this.end = end; } @@ -1119,14 +1132,11 @@ public class DataUsageSummary extends Fragment { private static final java.util.Formatter sFormatter = new java.util.Formatter( sBuilder, Locale.getDefault()); - private static String formatDateRange(Context context, long start, long end, String timezone) { - synchronized (sBuilder) { - int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH; - if (Time.getJulianDay(start, 0) == Time.getJulianDay(end, 0)) { - // when times are on same day, include time detail - flags |= DateUtils.FORMAT_SHOW_TIME; - } + public static String formatDateRange(Context context, long start, long end, boolean utcTime) { + final int flags = FORMAT_SHOW_DATE | FORMAT_ABBREV_MONTH; + final String timezone = utcTime ? TIMEZONE_UTC : null; + synchronized (sBuilder) { sBuilder.setLength(0); return DateUtils .formatDateRange(context, sFormatter, start, end, flags, timezone).toString(); @@ -1250,13 +1260,17 @@ public class DataUsageSummary extends Fragment { final Context context = parent.getContext(); + final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); final TextView title = (TextView) convertView.findViewById(android.R.id.title); final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); final ProgressBar progress = (ProgressBar) convertView.findViewById( android.R.id.progress); final AppUsageItem item = mItems.get(position); - title.setText(resolveLabelForUid(context, item.uid)); + final UidDetail detail = resolveDetailForUid(context, item.uid); + + icon.setImageDrawable(detail.icon); + title.setText(detail.label); summary.setText(Formatter.formatFileSize(context, item.total)); final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0; @@ -1536,48 +1550,66 @@ public class DataUsageSummary extends Fragment { } } + public static class UidDetail { + public CharSequence label; + public CharSequence[] detailLabels; + public Drawable icon; + } + /** * Resolve best descriptive label for the given UID. */ - public static CharSequence resolveLabelForUid(Context context, int uid) { + public static UidDetail resolveDetailForUid(Context context, int uid) { final Resources res = context.getResources(); final PackageManager pm = context.getPackageManager(); + final UidDetail detail = new UidDetail(); + detail.label = pm.getNameForUid(uid); + detail.icon = pm.getDefaultActivityIcon(); + // handle special case labels switch (uid) { case android.os.Process.SYSTEM_UID: - return res.getText(R.string.process_kernel_label); + detail.label = res.getString(R.string.process_kernel_label); + detail.icon = pm.getDefaultActivityIcon(); + return detail; case TrafficStats.UID_REMOVED: - return res.getText(R.string.data_usage_uninstalled_apps); + detail.label = res.getString(R.string.data_usage_uninstalled_apps); + detail.icon = pm.getDefaultActivityIcon(); + return detail; } // otherwise fall back to using packagemanager labels final String[] packageNames = pm.getPackagesForUid(uid); final int length = packageNames != null ? packageNames.length : 0; - CharSequence label = pm.getNameForUid(uid); try { if (length == 1) { final ApplicationInfo info = pm.getApplicationInfo(packageNames[0], 0); - label = info.loadLabel(pm); + detail.label = info.loadLabel(pm).toString(); + detail.icon = info.loadIcon(pm); } else if (length > 1) { - for (String packageName : packageNames) { - final PackageInfo info = pm.getPackageInfo(packageName, 0); - if (info.sharedUserLabel != 0) { - label = pm.getText(packageName, info.sharedUserLabel, info.applicationInfo); - if (!TextUtils.isEmpty(label)) { - break; - } + detail.detailLabels = new CharSequence[length]; + for (int i = 0; i < length; i++) { + final String packageName = packageNames[i]; + final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); + final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); + + detail.detailLabels[i] = appInfo.loadLabel(pm).toString(); + if (packageInfo.sharedUserLabel != 0) { + detail.label = pm.getText(packageName, packageInfo.sharedUserLabel, + packageInfo.applicationInfo).toString(); + detail.icon = appInfo.loadIcon(pm); } } } } catch (NameNotFoundException e) { } - if (TextUtils.isEmpty(label)) { - label = Integer.toString(uid); + if (TextUtils.isEmpty(detail.label)) { + detail.label = Integer.toString(uid); } - return label; + return detail; } /** @@ -1652,6 +1684,14 @@ public class DataUsageSummary extends Fragment { return view; } + private static View inflateAppTitle( + LayoutInflater inflater, ViewGroup root, CharSequence label) { + final TextView view = (TextView) inflater.inflate( + R.layout.data_usage_app_title, root, false); + view.setText(label); + return view; + } + /** * Set {@link android.R.id#title} for a preference view inflated with * {@link #inflatePreference(LayoutInflater, ViewGroup, View)}. diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java index 7a83fbf..c2702e4 100644 --- a/src/com/android/settings/widget/ChartGridView.java +++ b/src/com/android/settings/widget/ChartGridView.java @@ -17,12 +17,20 @@ package com.android.settings.widget; import android.content.Context; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.View; +import com.android.settings.DataUsageSummary; import com.android.settings.R; import com.google.common.base.Preconditions; @@ -32,14 +40,16 @@ import com.google.common.base.Preconditions; */ public class ChartGridView extends View { - // TODO: eventually teach about drawing chart labels - private ChartAxis mHoriz; private ChartAxis mVert; private Drawable mPrimary; private Drawable mSecondary; private Drawable mBorder; + private int mLabelColor; + + private Layout mLayoutStart; + private Layout mLayoutEnd; public ChartGridView(Context context) { this(context, null, 0); @@ -60,7 +70,7 @@ public class ChartGridView extends View { mPrimary = a.getDrawable(R.styleable.ChartGridView_primaryDrawable); mSecondary = a.getDrawable(R.styleable.ChartGridView_secondaryDrawable); mBorder = a.getDrawable(R.styleable.ChartGridView_borderDrawable); - // TODO: eventually read labelColor + mLabelColor = a.getColor(R.styleable.ChartGridView_labelColor, Color.RED); a.recycle(); } @@ -70,6 +80,13 @@ public class ChartGridView extends View { mVert = Preconditions.checkNotNull(vert, "missing vert"); } + void setBounds(long start, long end) { + final Context context = getContext(); + mLayoutStart = makeLayout(DataUsageSummary.formatDateRange(context, start, start, true)); + mLayoutEnd = makeLayout(DataUsageSummary.formatDateRange(context, end, end, true)); + invalidate(); + } + @Override protected void onDraw(Canvas canvas) { final int width = getWidth(); @@ -98,5 +115,38 @@ public class ChartGridView extends View { mBorder.setBounds(0, 0, width, height); mBorder.draw(canvas); + + final int padding = mLayoutStart.getHeight() / 8; + + final Layout start = mLayoutStart; + if (start != null) { + canvas.save(); + canvas.translate(0, height + padding); + start.draw(canvas); + canvas.restore(); + } + + final Layout end = mLayoutEnd; + if (end != null) { + canvas.save(); + canvas.translate(width - end.getWidth(), height + padding); + end.draw(canvas); + canvas.restore(); + } } + + private Layout makeLayout(CharSequence text) { + final Resources res = getResources(); + final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + paint.density = res.getDisplayMetrics().density; + paint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale); + paint.setColor(mLabelColor); + paint.setTextSize( + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10, res.getDisplayMetrics())); + + return new StaticLayout(text, paint, + (int) Math.ceil(Layout.getDesiredWidth(text, paint)), + Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true); + } + } diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index b5e044f..d5e8de8 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -29,7 +29,6 @@ import android.text.Layout.Alignment; import android.text.SpannableStringBuilder; import android.text.TextPaint; import android.util.AttributeSet; -import android.util.Log; import android.util.MathUtils; import android.view.MotionEvent; import android.view.View; diff --git a/src/com/android/settings/widget/DataUsageChartView.java b/src/com/android/settings/widget/DataUsageChartView.java index b2ad844..a1c92e1 100644 --- a/src/com/android/settings/widget/DataUsageChartView.java +++ b/src/com/android/settings/widget/DataUsageChartView.java @@ -343,6 +343,7 @@ public class DataUsageChartView extends ChartView { */ public void setVisibleRange(long visibleStart, long visibleEnd) { mHoriz.setBounds(visibleStart, visibleEnd); + mGrid.setBounds(visibleStart, visibleEnd); final long validStart = Math.max(visibleStart, getStatsStart()); final long validEnd = Math.min(visibleEnd, getStatsEnd()); |