diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-07-21 15:38:06 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2014-07-22 16:24:37 -0700 |
commit | b654846300f79e9e4c605ce62d09f9a05d232fee (patch) | |
tree | 7680adbda0d42bc2ad3ade1a5b03b5e925de344b /src/com/android | |
parent | 47d27bd629838ded2d217f238b6e3b21d18485da (diff) | |
download | packages_apps_Settings-b654846300f79e9e4c605ce62d09f9a05d232fee.zip packages_apps_Settings-b654846300f79e9e4c605ce62d09f9a05d232fee.tar.gz packages_apps_Settings-b654846300f79e9e4c605ce62d09f9a05d232fee.tar.bz2 |
Data Usage, materialized!
Asset drop so Data Usage matches new Material spec. Removes time
dimension sweeps, so we now summarize the entire visible axis. Fix
time axis labels to not draw outside clip bounds. Remove pie chart.
No more checkboxes in menus. Telephony items like roaming are moved
back to cellular settings. Start wiring up multi-SIM support.
Bug: 15760500, 16019700, 16289924, 16303795
Change-Id: Ie8f4821962319bb82ff4bc2f13f1f66ba1bdfe65
Diffstat (limited to 'src/com/android')
8 files changed, 339 insertions, 599 deletions
diff --git a/src/com/android/settings/DataUsageSummary.java b/src/com/android/settings/DataUsageSummary.java index bdae072..f8f285f 100644 --- a/src/com/android/settings/DataUsageSummary.java +++ b/src/com/android/settings/DataUsageSummary.java @@ -55,6 +55,7 @@ import android.app.DialogFragment; import android.app.Fragment; import android.app.FragmentTransaction; import android.app.LoaderManager.LoaderCallbacks; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -63,6 +64,7 @@ import android.content.Loader; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -107,9 +109,6 @@ import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ArrayAdapter; import android.widget.BaseAdapter; 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; @@ -138,7 +137,6 @@ import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.widget.ChartDataUsageView; import com.android.settings.widget.ChartDataUsageView.DataUsageChartListener; -import com.android.settings.widget.PieChartView; import com.google.android.collect.Lists; import libcore.util.Objects; @@ -170,7 +168,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private static final String TAB_ETHERNET = "ethernet"; private static final String TAG_CONFIRM_DATA_DISABLE = "confirmDataDisable"; - private static final String TAG_CONFIRM_DATA_ROAMING = "confirmDataRoaming"; private static final String TAG_CONFIRM_LIMIT = "confirmLimit"; private static final String TAG_CYCLE_EDITOR = "cycleEditor"; private static final String TAG_WARNING_EDITOR = "warningEditor"; @@ -189,15 +186,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private static final int LOADER_CHART_DATA = 2; private static final int LOADER_SUMMARY = 3; - private static final int FOREGROUND_BYTES_COLOR = 0xff009688; - private static final int DEFAULT_BYTES_COLOR = 0xffced7db; - private INetworkManagementService mNetworkService; private INetworkStatsService mStatsService; private NetworkPolicyManager mPolicyManager; private TelephonyManager mTelephonyManager; - private INetworkStatsSession mStatsSession; private static final String PREF_FILE = "data_usage"; @@ -219,29 +212,33 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private ViewGroup mNetworkSwitchesContainer; private LinearLayout mNetworkSwitches; + private boolean mDataEnabledSupported; private Switch mDataEnabled; private View mDataEnabledView; - private CheckBox mDisableAtLimit; + private boolean mDisableAtLimitSupported; + private Switch mDisableAtLimit; private View mDisableAtLimitView; private View mCycleView; private Spinner mCycleSpinner; private CycleAdapter mCycleAdapter; + private TextView mCycleSummary; private ChartDataUsageView mChart; - private TextView mUsageSummary; + private View mDisclaimer; private TextView mEmpty; + private View mStupidPadding; private View mAppDetail; private ImageView mAppIcon; private ViewGroup mAppTitles; - private PieChartView mAppPieChart; + private TextView mAppTotal; private TextView mAppForeground; private TextView mAppBackground; private Button mAppSettings; private LinearLayout mAppSwitches; - private CheckBox mAppRestrict; + private Switch mAppRestrict; private View mAppRestrictView; private boolean mShowWifi = false; @@ -259,9 +256,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private String mCurrentTab = null; private String mIntentTab = null; - private MenuItem mMenuDataRoaming; private MenuItem mMenuRestrictBackground; - private MenuItem mMenuAutoSync; + private MenuItem mMenuShowWifi; + private MenuItem mMenuShowEthernet; + private MenuItem mMenuSimCards; + private MenuItem mMenuCellularNetworks; /** Flag used to ignore listeners during binding. */ private boolean mBinding; @@ -359,13 +358,17 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mNetworkSwitches = (LinearLayout) mHeader.findViewById(R.id.network_switches); mDataEnabled = new Switch(inflater.getContext()); + mDataEnabled.setClickable(false); + mDataEnabled.setFocusable(false); mDataEnabledView = inflatePreference(inflater, mNetworkSwitches, mDataEnabled); mDataEnabledView.setTag(R.id.preference_highlight_key, DATA_USAGE_ENABLE_MOBILE_KEY); - mDataEnabled.setOnCheckedChangeListener(mDataEnabledListener); + mDataEnabledView.setClickable(true); + mDataEnabledView.setFocusable(true); + mDataEnabledView.setOnClickListener(mDataEnabledListener); mNetworkSwitches.addView(mDataEnabledView); - mDisableAtLimit = new CheckBox(inflater.getContext()); + mDisableAtLimit = new Switch(inflater.getContext()); mDisableAtLimit.setClickable(false); mDisableAtLimit.setFocusable(false); mDisableAtLimitView = inflatePreference(inflater, mNetworkSwitches, mDisableAtLimit); @@ -375,15 +378,16 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mDisableAtLimitView.setFocusable(true); mDisableAtLimitView.setOnClickListener(mDisableAtLimitListener); mNetworkSwitches.addView(mDisableAtLimitView); - } - // bind cycle dropdown - mCycleView = mHeader.findViewById(R.id.cycles); - mCycleView.setTag(R.id.preference_highlight_key, DATA_USAGE_CYCLE_KEY); - mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner); - mCycleAdapter = new CycleAdapter(context); - mCycleSpinner.setAdapter(mCycleAdapter); - mCycleSpinner.setOnItemSelectedListener(mCycleListener); + mCycleView = inflater.inflate(R.layout.data_usage_cycles, mNetworkSwitches, false); + mCycleView.setTag(R.id.preference_highlight_key, DATA_USAGE_CYCLE_KEY); + mCycleSpinner = (Spinner) mCycleView.findViewById(R.id.cycles_spinner); + mCycleAdapter = new CycleAdapter(context); + mCycleSpinner.setAdapter(mCycleAdapter); + mCycleSpinner.setOnItemSelectedListener(mCycleListener); + mCycleSummary = (TextView) mCycleView.findViewById(R.id.cycle_summary); + mNetworkSwitches.addView(mCycleView); + } mChart = (ChartDataUsageView) mHeader.findViewById(R.id.chart); mChart.setListener(mChartListener); @@ -394,7 +398,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mAppDetail = mHeader.findViewById(R.id.app_detail); mAppIcon = (ImageView) mAppDetail.findViewById(R.id.app_icon); mAppTitles = (ViewGroup) mAppDetail.findViewById(R.id.app_titles); - mAppPieChart = (PieChartView) mAppDetail.findViewById(R.id.app_pie_chart); mAppForeground = (TextView) mAppDetail.findViewById(R.id.app_foreground); mAppBackground = (TextView) mAppDetail.findViewById(R.id.app_background); mAppSwitches = (LinearLayout) mAppDetail.findViewById(R.id.app_switches); @@ -402,7 +405,7 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mAppSettings = (Button) mAppDetail.findViewById(R.id.app_settings); mAppSettings.setOnClickListener(mAppSettingsListener); - mAppRestrict = new CheckBox(inflater.getContext()); + mAppRestrict = new Switch(inflater.getContext()); mAppRestrict.setClickable(false); mAppRestrict.setFocusable(false); mAppRestrictView = inflatePreference(inflater, mAppSwitches, mAppRestrict); @@ -412,8 +415,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mAppSwitches.addView(mAppRestrictView); } - mUsageSummary = (TextView) mHeader.findViewById(R.id.usage_summary); + mDisclaimer = mHeader.findViewById(R.id.disclaimer); mEmpty = (TextView) mHeader.findViewById(android.R.id.empty); + mStupidPadding = mHeader.findViewById(R.id.stupid_padding); mAdapter = new DataUsageAdapter(mUidDetailProvider, mInsetSide); mListView.setOnItemClickListener(mListListener); @@ -480,40 +484,25 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable final boolean appDetailMode = isAppDetailMode(); final boolean isOwner = ActivityManager.getCurrentUser() == UserHandle.USER_OWNER; - mMenuDataRoaming = menu.findItem(R.id.data_usage_menu_roaming); - mMenuDataRoaming.setVisible(hasReadyMobileRadio(context) && !appDetailMode); - mMenuDataRoaming.setChecked(getDataRoaming()); - - mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background); - mMenuRestrictBackground.setVisible( - hasReadyMobileRadio(context) && isOwner && !appDetailMode); - mMenuRestrictBackground.setChecked(mPolicyManager.getRestrictBackground()); - + mMenuShowWifi = menu.findItem(R.id.data_usage_menu_show_wifi); // TODO: Define behavior of this sync button. See: http://b/16076571 - mMenuAutoSync = menu.findItem(R.id.data_usage_menu_auto_sync); - mMenuAutoSync.setChecked(ContentResolver.getMasterSyncAutomatically()); - mMenuAutoSync.setVisible(!appDetailMode); - - final MenuItem split4g = menu.findItem(R.id.data_usage_menu_split_4g); - split4g.setVisible(hasReadyMobile4gRadio(context) && isOwner && !appDetailMode); - split4g.setChecked(isMobilePolicySplit()); - - final MenuItem showWifi = menu.findItem(R.id.data_usage_menu_show_wifi); if (hasWifiRadio(context) && hasReadyMobileRadio(context)) { - showWifi.setVisible(!appDetailMode); - showWifi.setChecked(mShowWifi); + mMenuShowWifi.setVisible(!appDetailMode); } else { - showWifi.setVisible(false); + mMenuShowWifi.setVisible(false); } - final MenuItem showEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet); + mMenuShowEthernet = menu.findItem(R.id.data_usage_menu_show_ethernet); if (hasEthernet(context) && hasReadyMobileRadio(context)) { - showEthernet.setVisible(!appDetailMode); - showEthernet.setChecked(mShowEthernet); + mMenuShowEthernet.setVisible(!appDetailMode); } else { - showEthernet.setVisible(false); + mMenuShowEthernet.setVisible(false); } + mMenuRestrictBackground = menu.findItem(R.id.data_usage_menu_restrict_background); + mMenuRestrictBackground.setVisible( + hasReadyMobileRadio(context) && isOwner && !appDetailMode); + final MenuItem metered = menu.findItem(R.id.data_usage_menu_metered); if (hasReadyMobileRadio(context) || hasWifiRadio(context)) { metered.setVisible(!appDetailMode); @@ -521,6 +510,17 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable metered.setVisible(false); } + // TODO: show when multiple sims available + mMenuSimCards = menu.findItem(R.id.data_usage_menu_sim_cards); + mMenuSimCards.setVisible(false); + + mMenuCellularNetworks = menu.findItem(R.id.data_usage_menu_cellular_networks); + if (hasReadyMobileRadio(context)) { + mMenuCellularNetworks.setVisible(!appDetailMode); + } else { + mMenuCellularNetworks.setVisible(false); + } + final MenuItem help = menu.findItem(R.id.data_usage_menu_help); String helpUrl; if (!TextUtils.isEmpty(helpUrl = getResources().getString(R.string.help_url_data_usage))) { @@ -528,23 +528,35 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } else { help.setVisible(false); } + + updateMenuTitles(); + } + + private void updateMenuTitles() { + if (mPolicyManager.getRestrictBackground()) { + mMenuRestrictBackground.setTitle(R.string.data_usage_menu_allow_background); + } else { + mMenuRestrictBackground.setTitle(R.string.data_usage_menu_restrict_background); + } + + if (mShowWifi) { + mMenuShowWifi.setTitle(R.string.data_usage_menu_hide_wifi); + } else { + mMenuShowWifi.setTitle(R.string.data_usage_menu_show_wifi); + } + + if (mShowEthernet) { + mMenuShowEthernet.setTitle(R.string.data_usage_menu_hide_ethernet); + } else { + mMenuShowEthernet.setTitle(R.string.data_usage_menu_show_ethernet); + } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.data_usage_menu_roaming: { - final boolean dataRoaming = !item.isChecked(); - if (dataRoaming) { - ConfirmDataRoamingFragment.show(this); - } else { - // no confirmation to disable roaming - setDataRoaming(false); - } - return true; - } case R.id.data_usage_menu_restrict_background: { - final boolean restrictBackground = !item.isChecked(); + final boolean restrictBackground = !mPolicyManager.getRestrictBackground(); if (restrictBackground) { ConfirmRestrictFragment.show(this); } else { @@ -553,41 +565,37 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } return true; } - case R.id.data_usage_menu_split_4g: { - final boolean mobileSplit = !item.isChecked(); - setMobilePolicySplit(mobileSplit); - item.setChecked(isMobilePolicySplit()); - updateTabs(); - return true; - } case R.id.data_usage_menu_show_wifi: { - mShowWifi = !item.isChecked(); + mShowWifi = !mShowWifi; mPrefs.edit().putBoolean(PREF_SHOW_WIFI, mShowWifi).apply(); - item.setChecked(mShowWifi); + updateMenuTitles(); updateTabs(); return true; } case R.id.data_usage_menu_show_ethernet: { - mShowEthernet = !item.isChecked(); + mShowEthernet = !mShowEthernet; mPrefs.edit().putBoolean(PREF_SHOW_ETHERNET, mShowEthernet).apply(); - item.setChecked(mShowEthernet); + updateMenuTitles(); updateTabs(); return true; } + case R.id.data_usage_menu_sim_cards: { + // TODO: hook up to sim cards + return true; + } + case R.id.data_usage_menu_cellular_networks: { + final Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setComponent(new ComponentName("com.android.phone", + "com.android.phone.MobileNetworkSettings")); + startActivity(intent); + return true; + } case R.id.data_usage_menu_metered: { final SettingsActivity sa = (SettingsActivity) getActivity(); sa.startPreferencePanel(DataUsageMeteredSettings.class.getCanonicalName(), null, R.string.data_usage_metered_title, null, this, 0); return true; } - case R.id.data_usage_menu_auto_sync: { - if (ActivityManager.isUserAMonkey()) { - Log.d("SyncState", "ignoring monkey's attempt to flip global sync state"); - } else { - ConfirmAutoSyncChangeFragment.show(this, !item.isChecked()); - } - return true; - } } return false; } @@ -725,7 +733,8 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable if (LOGD) Log.d(TAG, "updateBody() with currentTab=" + currentTab); - mDataEnabledView.setVisibility(isOwner ? View.VISIBLE : View.GONE); + mDataEnabledSupported = isOwner; + mDisableAtLimitSupported = true; // TODO: remove mobile tabs when SIM isn't ready @@ -748,14 +757,14 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } else if (TAB_WIFI.equals(currentTab)) { // wifi doesn't have any controls - mDataEnabledView.setVisibility(View.GONE); - mDisableAtLimitView.setVisibility(View.GONE); + mDataEnabledSupported = false; + mDisableAtLimitSupported = false; mTemplate = buildTemplateWifiWildcard(); } else if (TAB_ETHERNET.equals(currentTab)) { // ethernet doesn't have any controls - mDataEnabledView.setVisibility(View.GONE); - mDisableAtLimitView.setVisibility(View.GONE); + mDataEnabledSupported = false; + mDisableAtLimitSupported = false; mTemplate = buildTemplateEthernet(); } else { @@ -808,12 +817,25 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable mAppIcon.setImageDrawable(detail.icon); mAppTitles.removeAllViews(); + + View title = null; if (detail.detailLabels != null) { for (CharSequence label : detail.detailLabels) { - mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, label)); + title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false); + ((TextView) title.findViewById(R.id.app_title)).setText(label); + mAppTitles.addView(title); } } else { - mAppTitles.addView(inflateAppTitle(inflater, mAppTitles, detail.label)); + title = inflater.inflate(R.layout.data_usage_app_title, mAppTitles, false); + ((TextView) title.findViewById(R.id.app_title)).setText(detail.label); + mAppTitles.addView(title); + } + + // Remember last slot for summary + if (title != null) { + mAppTotal = (TextView) title.findViewById(R.id.app_summary); + } else { + mAppTotal = null; } // enable settings button when package provides it @@ -904,24 +926,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } } - private boolean getDataRoaming() { - final ContentResolver resolver = getActivity().getContentResolver(); - return android.provider.Settings.Global.getInt(resolver, - android.provider.Settings.Global.DATA_ROAMING, 0) != 0; - } - - private void setDataRoaming(boolean enabled) { - // TODO: teach telephony DataConnectionTracker to watch and apply - // updates when changed. - final ContentResolver resolver = getActivity().getContentResolver(); - android.provider.Settings.Global.putInt(resolver, - android.provider.Settings.Global.DATA_ROAMING, enabled ? 1 : 0); - mMenuDataRoaming.setChecked(enabled); - } - public void setRestrictBackground(boolean restrictBackground) { mPolicyManager.setRestrictBackground(restrictBackground); - mMenuRestrictBackground.setChecked(restrictBackground); + updateMenuTitles(); } private boolean getAppRestrictBackground() { @@ -943,10 +950,12 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable * current {@link #mTemplate}. */ private void updatePolicy(boolean refreshCycle) { + boolean dataEnabledVisible = mDataEnabledSupported; + boolean disableAtLimitVisible = mDisableAtLimitSupported; + if (isAppDetailMode()) { - mNetworkSwitches.setVisibility(View.GONE); - } else { - mNetworkSwitches.setVisibility(View.VISIBLE); + dataEnabledVisible = false; + disableAtLimitVisible = false; } // TODO: move enabled state directly into policy @@ -958,7 +967,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable final NetworkPolicy policy = mPolicyEditor.getPolicy(mTemplate); if (isNetworkPolicyModifiable(policy)) { - mDisableAtLimitView.setVisibility(View.VISIBLE); mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED); if (!isAppDetailMode()) { mChart.bindNetworkPolicy(policy); @@ -966,10 +974,13 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } else { // controls are disabled; don't bind warning/limit sweeps - mDisableAtLimitView.setVisibility(View.GONE); + disableAtLimitVisible = false; mChart.bindNetworkPolicy(null); } + mDataEnabledView.setVisibility(dataEnabledVisible ? View.VISIBLE : View.GONE); + mDisableAtLimitView.setVisibility(disableAtLimitVisible ? View.VISIBLE : View.GONE); + if (refreshCycle) { // generate cycle list based on policy and available history updateCycleList(policy); @@ -1049,12 +1060,12 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } } - private OnCheckedChangeListener mDataEnabledListener = new OnCheckedChangeListener() { + private View.OnClickListener mDataEnabledListener = new View.OnClickListener() { @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + public void onClick(View v) { if (mBinding) return; - final boolean dataEnabled = isChecked; + final boolean dataEnabled = !mDataEnabled.isChecked(); final String currentTab = mCurrentTab; if (TAB_MOBILE.equals(currentTab)) { if (dataEnabled) { @@ -1178,15 +1189,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable final long defaultBytes = entry.rxBytes + entry.txBytes; entry = mChartData.detailForeground.getValues(start, end, now, entry); final long foregroundBytes = entry.rxBytes + entry.txBytes; + final long totalBytes = defaultBytes + foregroundBytes; - mAppPieChart.setOriginAngle(175); - - mAppPieChart.removeAllSlices(); - mAppPieChart.addSlice(foregroundBytes, FOREGROUND_BYTES_COLOR); - mAppPieChart.addSlice(defaultBytes, DEFAULT_BYTES_COLOR); - - mAppPieChart.generatePath(); - + if (mAppTotal != null) { + mAppTotal.setText(Formatter.formatFileSize(context, totalBytes)); + } mAppBackground.setText(Formatter.formatFileSize(context, defaultBytes)); mAppForeground.setText(Formatter.formatFileSize(context, foregroundBytes)); @@ -1195,11 +1202,15 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable getLoaderManager().destroyLoader(LOADER_SUMMARY); + mCycleSummary.setVisibility(View.GONE); + } else { if (mChartData != null) { entry = mChartData.network.getValues(start, end, now, null); } + mCycleSummary.setVisibility(View.VISIBLE); + // kick off loader for detailed stats getLoaderManager().restartLoader(LOADER_SUMMARY, SummaryForAllUidLoader.buildArgs(mTemplate, start, end), mSummaryCallbacks); @@ -1207,18 +1218,19 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable final long totalBytes = entry != null ? entry.rxBytes + entry.txBytes : 0; final String totalPhrase = Formatter.formatFileSize(context, totalBytes); - final String rangePhrase = formatDateRange(context, start, end); + mCycleSummary.setText(totalPhrase); - final int summaryRes; if (TAB_MOBILE.equals(mCurrentTab) || TAB_3G.equals(mCurrentTab) || TAB_4G.equals(mCurrentTab)) { - summaryRes = R.string.data_usage_total_during_range_mobile; + if (isAppDetailMode()) { + mDisclaimer.setVisibility(View.GONE); + } else { + mDisclaimer.setVisibility(View.VISIBLE); + } } else { - summaryRes = R.string.data_usage_total_during_range; + mDisclaimer.setVisibility(View.GONE); } - mUsageSummary.setText(getString(summaryRes, totalPhrase, rangePhrase)); - // initial layout is finished above, ensure we have transitions ensureLayoutTransitions(); } @@ -1278,6 +1290,7 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private void updateEmptyVisible() { final boolean isEmpty = mAdapter.isEmpty() && !isAppDetailMode(); mEmpty.setVisibility(isEmpty ? View.VISIBLE : View.GONE); + mStupidPadding.setVisibility(isEmpty ? View.VISIBLE : View.GONE); } }; @@ -1309,12 +1322,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private DataUsageChartListener mChartListener = new DataUsageChartListener() { @Override - public void onInspectRangeChanged() { - if (LOGD) Log.d(TAG, "onInspectRangeChanged()"); - updateDetailData(); - } - - @Override public void onWarningChanged() { setPolicyWarningBytes(mChart.getWarningBytes()); } @@ -1404,8 +1411,8 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable private final CycleChangeItem mChangeItem; public CycleAdapter(Context context) { - super(context, android.R.layout.simple_spinner_item); - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + super(context, R.layout.data_usage_cycle_item); + setDropDownViewResource(R.layout.data_usage_cycle_item_dropdown); mChangeItem = new CycleChangeItem(context); } @@ -1447,11 +1454,21 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } public static class AppItem implements Comparable<AppItem>, Parcelable { + public static final int CATEGORY_USER = 0; + public static final int CATEGORY_APP_TITLE = 1; + public static final int CATEGORY_APP = 2; + public final int key; public boolean restricted; + public int category; + public SparseBooleanArray uids = new SparseBooleanArray(); public long total; + public AppItem() { + this.key = 0; + } + public AppItem(int key) { this.key = key; } @@ -1480,7 +1497,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable @Override public int compareTo(AppItem another) { - return Long.compare(another.total, total); + int comparison = Integer.compare(another.category, category); + if (comparison == 0) { + comparison = Long.compare(another.total, total); + } + return comparison; } public static final Creator<AppItem> CREATOR = new Creator<AppItem>() { @@ -1516,9 +1537,11 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable */ public void bindStats(NetworkStats stats, int[] restrictedUids) { mItems.clear(); + mLargest = 0; final int currentUserId = ActivityManager.getCurrentUser(); final SparseArray<AppItem> knownItems = new SparseArray<AppItem>(); + boolean hasApps = false; NetworkStats.Entry entry = null; final int size = stats != null ? stats.size() : 0; @@ -1548,6 +1571,9 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } item.addUid(uid); item.total += entry.rxBytes + entry.txBytes; + if (item.total > mLargest) { + mLargest = item.total; + } } for (int uid : restrictedUids) { @@ -1564,8 +1590,14 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable item.restricted = true; } + hasApps = !mItems.isEmpty(); + if (hasApps) { + final AppItem title = new AppItem(); + title.category = AppItem.CATEGORY_APP_TITLE; + mItems.add(title); + } + Collections.sort(mItems); - mLargest = (mItems.size() > 0) ? mItems.get(0).total : 0; notifyDataSetChanged(); } @@ -1585,36 +1617,72 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable } @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = LayoutInflater.from(parent.getContext()).inflate( - R.layout.data_usage_item, parent, false); + public int getViewTypeCount() { + return 2; + } - if (mInsetSide > 0) { - convertView.setPaddingRelative(mInsetSide, 0, mInsetSide, 0); - } + @Override + public int getItemViewType(int position) { + final AppItem item = mItems.get(position); + if (item.category == AppItem.CATEGORY_APP_TITLE) { + return 1; + } else { + return 0; } + } - final Context context = parent.getContext(); + @Override + public boolean areAllItemsEnabled() { + return false; + } - final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1); - final ProgressBar progress = (ProgressBar) convertView.findViewById( - android.R.id.progress); + @Override + public boolean isEnabled(int position) { + return getItemViewType(position) == 0; + } - // kick off async load of app details + @Override + public View getView(int position, View convertView, ViewGroup parent) { final AppItem item = mItems.get(position); - UidDetailTask.bindView(mProvider, item, convertView); + if (getItemViewType(position) == 1) { + if (convertView == null) { + convertView = inflateCategoryHeader(LayoutInflater.from(parent.getContext()), + parent); + } + + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + title.setText(R.string.data_usage_app); - if (item.restricted && item.total <= 0) { - text1.setText(R.string.data_usage_app_restricted); - progress.setVisibility(View.GONE); } else { - text1.setText(Formatter.formatFileSize(context, item.total)); - progress.setVisibility(View.VISIBLE); - } + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()).inflate( + R.layout.data_usage_item, parent, false); - final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0; - progress.setProgress(percentTotal); + if (mInsetSide > 0) { + convertView.setPaddingRelative(mInsetSide, 0, mInsetSide, 0); + } + } + + final Context context = parent.getContext(); + + final TextView text1 = (TextView) convertView.findViewById(android.R.id.text1); + final ProgressBar progress = (ProgressBar) convertView.findViewById( + android.R.id.progress); + + // kick off async load of app details + UidDetailTask.bindView(mProvider, item, convertView); + + if (item.restricted && item.total <= 0) { + text1.setText(R.string.data_usage_app_restricted); + progress.setVisibility(View.GONE); + } else { + text1.setText(Formatter.formatFileSize(context, item.total)); + progress.setVisibility(View.VISIBLE); + } + + final int percentTotal = mLargest != 0 ? (int) (item.total * 100 / mLargest) : 0; + progress.setProgress(percentTotal); + } return convertView; } @@ -1639,7 +1707,8 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable final FragmentTransaction ft = parent.getFragmentManager().beginTransaction(); ft.add(fragment, TAG_APP_DETAILS); ft.addToBackStack(TAG_APP_DETAILS); - ft.setBreadCrumbTitle(label); + ft.setBreadCrumbTitle( + parent.getResources().getString(R.string.data_usage_app_summary_title)); ft.commitAllowingStateLoss(); } @@ -1948,46 +2017,6 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable /** * Dialog to request user confirmation before setting - * {@link android.provider.Settings.Global#DATA_ROAMING}. - */ - public static class ConfirmDataRoamingFragment extends DialogFragment { - public static void show(DataUsageSummary parent) { - if (!parent.isAdded()) return; - - final ConfirmDataRoamingFragment dialog = new ConfirmDataRoamingFragment(); - dialog.setTargetFragment(parent, 0); - dialog.show(parent.getFragmentManager(), TAG_CONFIRM_DATA_ROAMING); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Context context = getActivity(); - - final AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.roaming_reenable_title); - if (Utils.hasMultipleUsers(context)) { - builder.setMessage(R.string.roaming_warning_multiuser); - } else { - builder.setMessage(R.string.roaming_warning); - } - - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - final DataUsageSummary target = (DataUsageSummary) getTargetFragment(); - if (target != null) { - target.setDataRoaming(true); - } - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - - return builder.create(); - } - } - - /** - * Dialog to request user confirmation before setting * {@link INetworkPolicyManager#setRestrictBackground(boolean)}. */ public static class ConfirmRestrictFragment extends DialogFragment { @@ -2308,12 +2337,12 @@ public class DataUsageSummary extends HighlightingFragment implements Indexable 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; + private static View inflateCategoryHeader(LayoutInflater inflater, ViewGroup root) { + final TypedArray a = inflater.getContext().obtainStyledAttributes(null, + com.android.internal.R.styleable.Preference, + com.android.internal.R.attr.preferenceCategoryStyle, 0); + final int resId = a.getResourceId(com.android.internal.R.styleable.Preference_layout, 0); + return inflater.inflate(resId, root, false); } /** diff --git a/src/com/android/settings/net/DataUsageMeteredSettings.java b/src/com/android/settings/net/DataUsageMeteredSettings.java index 87358f6..d567c7e 100644 --- a/src/com/android/settings/net/DataUsageMeteredSettings.java +++ b/src/com/android/settings/net/DataUsageMeteredSettings.java @@ -29,10 +29,9 @@ import android.net.NetworkTemplate; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; -import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceCategory; -import android.provider.SearchIndexableResource; +import android.preference.SwitchPreference; import android.telephony.TelephonyManager; import com.android.settings.R; @@ -42,7 +41,6 @@ import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; /** @@ -117,7 +115,7 @@ public class DataUsageMeteredSettings extends SettingsPreferenceFragment impleme return pref; } - private class MeteredPreference extends CheckBoxPreference { + private class MeteredPreference extends SwitchPreference { private final NetworkTemplate mTemplate; private boolean mBinding; diff --git a/src/com/android/settings/widget/ChartDataUsageView.java b/src/com/android/settings/widget/ChartDataUsageView.java index 4e16bfc..c20a8db 100644 --- a/src/com/android/settings/widget/ChartDataUsageView.java +++ b/src/com/android/settings/widget/ChartDataUsageView.java @@ -50,26 +50,24 @@ public class ChartDataUsageView extends ChartView { private static final int MSG_UPDATE_AXIS = 100; private static final long DELAY_MILLIS = 250; - private static final boolean LIMIT_SWEEPS_TO_VALID_DATA = false; - private ChartGridView mGrid; private ChartNetworkSeriesView mSeries; private ChartNetworkSeriesView mDetailSeries; private NetworkStatsHistory mHistory; - private ChartSweepView mSweepLeft; - private ChartSweepView mSweepRight; private ChartSweepView mSweepWarning; private ChartSweepView mSweepLimit; + private long mInspectStart; + private long mInspectEnd; + private Handler mHandler; /** Current maximum value of {@link #mVert}. */ private long mVertMax; public interface DataUsageChartListener { - public void onInspectRangeChanged(); public void onWarningChanged(); public void onLimitChanged(); public void requestWarningEdit(); @@ -112,43 +110,27 @@ public class ChartDataUsageView extends ChartView { mDetailSeries = (ChartNetworkSeriesView) findViewById(R.id.detail_series); mDetailSeries.setVisibility(View.GONE); - mSweepLeft = (ChartSweepView) findViewById(R.id.sweep_left); - mSweepRight = (ChartSweepView) findViewById(R.id.sweep_right); mSweepLimit = (ChartSweepView) findViewById(R.id.sweep_limit); mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning); // prevent sweeps from crossing each other - mSweepLeft.setValidRangeDynamic(null, mSweepRight); - mSweepRight.setValidRangeDynamic(mSweepLeft, null); mSweepWarning.setValidRangeDynamic(null, mSweepLimit); mSweepLimit.setValidRangeDynamic(mSweepWarning, null); // mark neighbors for checking touch events against - mSweepLeft.setNeighbors(mSweepRight); - mSweepRight.setNeighbors(mSweepLeft); - mSweepLimit.setNeighbors(mSweepWarning, mSweepLeft, mSweepRight); - mSweepWarning.setNeighbors(mSweepLimit, mSweepLeft, mSweepRight); + mSweepLimit.setNeighbors(mSweepWarning); + mSweepWarning.setNeighbors(mSweepLimit); - mSweepLeft.addOnSweepListener(mHorizListener); - mSweepRight.addOnSweepListener(mHorizListener); mSweepWarning.addOnSweepListener(mVertListener); mSweepLimit.addOnSweepListener(mVertListener); mSweepWarning.setDragInterval(5 * MB_IN_BYTES); mSweepLimit.setDragInterval(5 * MB_IN_BYTES); - // TODO: make time sweeps adjustable through dpad - mSweepLeft.setClickable(false); - mSweepLeft.setFocusable(false); - mSweepRight.setClickable(false); - mSweepRight.setFocusable(false); - // tell everyone about our axis mGrid.init(mHoriz, mVert); mSeries.init(mHoriz, mVert); mDetailSeries.init(mHoriz, mVert); - mSweepLeft.init(mHoriz); - mSweepRight.init(mHoriz); mSweepWarning.init(mVert); mSweepLimit.init(mVert); @@ -194,7 +176,7 @@ public class ChartDataUsageView extends ChartView { mSweepLimit.setEnabled(true); mSweepLimit.setValue(policy.limitBytes); } else { - mSweepLimit.setVisibility(View.VISIBLE); + mSweepLimit.setVisibility(View.INVISIBLE); mSweepLimit.setEnabled(false); mSweepLimit.setValue(-1); } @@ -295,23 +277,6 @@ public class ChartDataUsageView extends ChartView { mSeries.setEstimateVisible(estimateVisible); } - private OnSweepListener mHorizListener = new OnSweepListener() { - @Override - public void onSweep(ChartSweepView sweep, boolean sweepDone) { - updatePrimaryRange(); - - // update detail list only when done sweeping - if (sweepDone && mListener != null) { - mListener.onInspectRangeChanged(); - } - } - - @Override - public void requestEdit(ChartSweepView sweep) { - // ignored - } - }; - private void sendUpdateAxisDelayed(ChartSweepView sweep, boolean force) { if (force || !mHandler.hasMessages(MSG_UPDATE_AXIS, sweep)) { mHandler.sendMessageDelayed( @@ -369,11 +334,11 @@ public class ChartDataUsageView extends ChartView { } public long getInspectStart() { - return mSweepLeft.getValue(); + return mInspectStart; } public long getInspectEnd() { - return mSweepRight.getValue(); + return mInspectEnd; } public long getWarningBytes() { @@ -384,14 +349,6 @@ public class ChartDataUsageView extends ChartView { return mSweepLimit.getLabelValue(); } - private long getHistoryStart() { - return mHistory != null ? mHistory.getStart() : Long.MAX_VALUE; - } - - private long getHistoryEnd() { - return mHistory != null ? mHistory.getEnd() : Long.MIN_VALUE; - } - /** * Set the exact time range that should be displayed, updating how * {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the @@ -403,30 +360,8 @@ public class ChartDataUsageView extends ChartView { mSeries.setBounds(visibleStart, visibleEnd); mDetailSeries.setBounds(visibleStart, visibleEnd); - final long historyStart = getHistoryStart(); - final long historyEnd = getHistoryEnd(); - - final long validStart = historyStart == Long.MAX_VALUE ? visibleStart - : Math.max(visibleStart, historyStart); - final long validEnd = historyEnd == Long.MIN_VALUE ? visibleEnd - : Math.min(visibleEnd, historyEnd); - - if (LIMIT_SWEEPS_TO_VALID_DATA) { - // prevent time sweeps from leaving valid data - mSweepLeft.setValidRange(validStart, validEnd); - mSweepRight.setValidRange(validStart, validEnd); - } else { - mSweepLeft.setValidRange(visibleStart, visibleEnd); - mSweepRight.setValidRange(visibleStart, visibleEnd); - } - - // default sweeps to last week of data - final long halfRange = (visibleEnd + visibleStart) / 2; - final long sweepMax = validEnd; - final long sweepMin = Math.max(visibleStart, (sweepMax - DateUtils.WEEK_IN_MILLIS)); - - mSweepLeft.setValue(sweepMin); - mSweepRight.setValue(sweepMax); + mInspectStart = visibleStart; + mInspectEnd = visibleEnd; requestLayout(); if (changed) { @@ -440,15 +375,11 @@ public class ChartDataUsageView extends ChartView { } private void updatePrimaryRange() { - final long left = mSweepLeft.getValue(); - final long right = mSweepRight.getValue(); - // prefer showing primary range on detail series, when available if (mDetailSeries.getVisibility() == View.VISIBLE) { - mDetailSeries.setPrimaryRange(left, right); - mSeries.setPrimaryRange(0, 0); + mSeries.setSecondary(true); } else { - mSeries.setPrimaryRange(left, right); + mSeries.setSecondary(false); } } diff --git a/src/com/android/settings/widget/ChartGridView.java b/src/com/android/settings/widget/ChartGridView.java index ec5882c..4cd6f5f 100644 --- a/src/com/android/settings/widget/ChartGridView.java +++ b/src/com/android/settings/widget/ChartGridView.java @@ -19,22 +19,25 @@ package com.android.settings.widget; import static com.android.settings.DataUsageSummary.formatDateRange; import android.content.Context; +import android.content.res.ColorStateList; 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.Log; import android.util.TypedValue; import android.view.View; import com.android.internal.util.Preconditions; import com.android.settings.R; +import java.util.Locale; + /** * Background of {@link ChartView} that renders grid lines as requested by * {@link ChartAxis#getTickPoints()}. @@ -47,10 +50,13 @@ public class ChartGridView extends View { private Drawable mPrimary; private Drawable mSecondary; private Drawable mBorder; + + private int mLabelSize; private int mLabelColor; - private Layout mLayoutStart; - private Layout mLayoutEnd; + private Layout mLabelStart; + private Layout mLabelMid; + private Layout mLabelEnd; public ChartGridView(Context context) { this(context, null, 0); @@ -71,7 +77,17 @@ 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); - mLabelColor = a.getColor(R.styleable.ChartGridView_labelColor, Color.RED); + + final int taId = a.getResourceId(R.styleable.ChartGridView_android_textAppearance, -1); + final TypedArray ta = context.obtainStyledAttributes(taId, + com.android.internal.R.styleable.TextAppearance); + mLabelSize = ta.getDimensionPixelSize( + com.android.internal.R.styleable.TextAppearance_textSize, 0); + ta.recycle(); + + final ColorStateList labelColor = a.getColorStateList( + R.styleable.ChartGridView_android_textColor); + mLabelColor = labelColor.getDefaultColor(); a.recycle(); } @@ -83,71 +99,83 @@ public class ChartGridView extends View { void setBounds(long start, long end) { final Context context = getContext(); - mLayoutStart = makeLayout(formatDateRange(context, start, start)); - mLayoutEnd = makeLayout(formatDateRange(context, end, end)); + final long mid = (start + end) / 2; + mLabelStart = makeLabel(formatDateRange(context, start, start)); + mLabelMid = makeLabel(formatDateRange(context, mid, mid)); + mLabelEnd = makeLabel(formatDateRange(context, end, end)); invalidate(); } @Override protected void onDraw(Canvas canvas) { final int width = getWidth(); - final int height = getHeight(); + final int height = getHeight() - getPaddingBottom(); final Drawable secondary = mSecondary; - final int secondaryHeight = mSecondary.getIntrinsicHeight(); - - final float[] vertTicks = mVert.getTickPoints(); - for (float y : vertTicks) { - final int bottom = (int) Math.min(y + secondaryHeight, height); - secondary.setBounds(0, (int) y, width, bottom); - secondary.draw(canvas); + if (secondary != null) { + final int secondaryHeight = secondary.getIntrinsicHeight(); + + final float[] vertTicks = mVert.getTickPoints(); + for (float y : vertTicks) { + final int bottom = (int) Math.min(y + secondaryHeight, height); + secondary.setBounds(0, (int) y, width, bottom); + secondary.draw(canvas); + } } final Drawable primary = mPrimary; - final int primaryWidth = mPrimary.getIntrinsicWidth(); - final int primaryHeight = mPrimary.getIntrinsicHeight(); - - final float[] horizTicks = mHoriz.getTickPoints(); - for (float x : horizTicks) { - final int right = (int) Math.min(x + primaryWidth, width); - primary.setBounds((int) x, 0, right, height); - primary.draw(canvas); + if (primary != null) { + final int primaryWidth = primary.getIntrinsicWidth(); + final int primaryHeight = primary.getIntrinsicHeight(); + + final float[] horizTicks = mHoriz.getTickPoints(); + for (float x : horizTicks) { + final int right = (int) Math.min(x + primaryWidth, width); + primary.setBounds((int) x, 0, right, height); + primary.draw(canvas); + } } mBorder.setBounds(0, 0, width, height); mBorder.draw(canvas); - final int padding = mLayoutStart != null ? mLayoutStart.getHeight() / 8 : 0; + final int padding = mLabelStart != null ? mLabelStart.getHeight() / 8 : 0; - final Layout start = mLayoutStart; + final Layout start = mLabelStart; if (start != null) { - canvas.save(); + final int saveCount = canvas.save(); canvas.translate(0, height + padding); start.draw(canvas); - canvas.restore(); + canvas.restoreToCount(saveCount); + } + + final Layout mid = mLabelMid; + if (mid != null) { + final int saveCount = canvas.save(); + canvas.translate((width - mid.getWidth()) / 2, height + padding); + mid.draw(canvas); + canvas.restoreToCount(saveCount); } - final Layout end = mLayoutEnd; + final Layout end = mLabelEnd; if (end != null) { - canvas.save(); + final int saveCount = canvas.save(); canvas.translate(width - end.getWidth(), height + padding); end.draw(canvas); - canvas.restore(); + canvas.restoreToCount(saveCount); } } - private Layout makeLayout(CharSequence text) { + private Layout makeLabel(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())); + paint.setTextSize(mLabelSize); 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/ChartNetworkSeriesView.java b/src/com/android/settings/widget/ChartNetworkSeriesView.java index 6250a25..7aaba66 100644 --- a/src/com/android/settings/widget/ChartNetworkSeriesView.java +++ b/src/com/android/settings/widget/ChartNetworkSeriesView.java @@ -60,17 +60,17 @@ public class ChartNetworkSeriesView extends View { private Path mPathFill; private Path mPathEstimate; + private int mSafeRegion; + private long mStart; private long mEnd; - private long mPrimaryLeft; - private long mPrimaryRight; - /** Series will be extended to reach this end time. */ private long mEndTime = Long.MIN_VALUE; private boolean mPathValid = false; private boolean mEstimateVisible = false; + private boolean mSecondary = false; private long mMax; private long mMaxEstimate; @@ -93,8 +93,11 @@ public class ChartNetworkSeriesView extends View { final int fill = a.getColor(R.styleable.ChartNetworkSeriesView_fillColor, Color.RED); final int fillSecondary = a.getColor( R.styleable.ChartNetworkSeriesView_fillColorSecondary, Color.RED); + final int safeRegion = a.getDimensionPixelSize( + R.styleable.ChartNetworkSeriesView_safeRegion, 0); setChartColor(stroke, fill, fillSecondary); + setSafeRegion(safeRegion); setWillNotDraw(false); a.recycle(); @@ -134,6 +137,10 @@ public class ChartNetworkSeriesView extends View { mPaintEstimate.setPathEffect(new DashPathEffect(new float[] { 10, 10 }, 1)); } + public void setSafeRegion(int safeRegion) { + mSafeRegion = safeRegion; + } + public void bindNetworkStats(NetworkStatsHistory stats) { mStats = stats; invalidatePath(); @@ -145,14 +152,8 @@ public class ChartNetworkSeriesView extends View { mEnd = end; } - /** - * Set the range to paint with {@link #mPaintFill}, leaving the remaining - * area to be painted with {@link #mPaintFillSecondary}. - */ - public void setPrimaryRange(long left, long right) { - mPrimaryLeft = left; - mPrimaryRight = right; - invalidate(); + public void setSecondary(boolean secondary) { + mSecondary = secondary; } public void invalidatePath() { @@ -322,9 +323,6 @@ public class ChartNetworkSeriesView extends View { generatePath(); } - final float primaryLeftPoint = mHoriz.convertToPoint(mPrimaryLeft); - final float primaryRightPoint = mHoriz.convertToPoint(mPrimaryRight); - if (mEstimateVisible) { save = canvas.save(); canvas.clipRect(0, 0, getWidth(), getHeight()); @@ -332,21 +330,11 @@ public class ChartNetworkSeriesView extends View { canvas.restoreToCount(save); } - save = canvas.save(); - canvas.clipRect(0, 0, primaryLeftPoint, getHeight()); - canvas.drawPath(mPathFill, mPaintFillSecondary); - canvas.restoreToCount(save); + final Paint paintFill = mSecondary ? mPaintFillSecondary : mPaintFill; save = canvas.save(); - canvas.clipRect(primaryRightPoint, 0, getWidth(), getHeight()); - canvas.drawPath(mPathFill, mPaintFillSecondary); + canvas.clipRect(mSafeRegion, 0, getWidth(), getHeight() - mSafeRegion); + canvas.drawPath(mPathFill, paintFill); canvas.restoreToCount(save); - - save = canvas.save(); - canvas.clipRect(primaryLeftPoint, 0, primaryRightPoint, getHeight()); - canvas.drawPath(mPathFill, mPaintFill); - canvas.drawPath(mPathStroke, mPaintStroke); - canvas.restoreToCount(save); - } } diff --git a/src/com/android/settings/widget/ChartSweepView.java b/src/com/android/settings/widget/ChartSweepView.java index 774e5d8..04fc862 100644 --- a/src/com/android/settings/widget/ChartSweepView.java +++ b/src/com/android/settings/widget/ChartSweepView.java @@ -58,6 +58,7 @@ public class ChartSweepView extends View { private Rect mMargins = new Rect(); private float mNeighborMargin; + private int mSafeRegion; private int mFollowAxis; @@ -125,6 +126,7 @@ public class ChartSweepView extends View { setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable)); setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1)); setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0)); + setSafeRegion(a.getDimensionPixelSize(R.styleable.ChartSweepView_safeRegion, 0)); setLabelMinSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0)); setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0)); @@ -259,7 +261,6 @@ public class ChartSweepView extends View { paint.density = getResources().getDisplayMetrics().density; paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); paint.setColor(mLabelColor); - paint.setShadowLayer(4 * paint.density, 0, 0, Color.BLACK); mLabelTemplate = new SpannableStringBuilder(template); mLabelLayout = new DynamicLayout( @@ -383,6 +384,10 @@ public class ChartSweepView extends View { mNeighborMargin = neighborMargin; } + public void setSafeRegion(int safeRegion) { + mSafeRegion = safeRegion; + } + /** * Set valid range this sweep can move within, defined by the given * {@link ChartSweepView}. The most restrictive combination of all valid @@ -709,7 +714,7 @@ public class ChartSweepView extends View { mLabelLayout.draw(canvas); } canvas.restoreToCount(count); - labelSize = (int) mLabelSize; + labelSize = (int) mLabelSize + mSafeRegion; } else { labelSize = 0; } diff --git a/src/com/android/settings/widget/ChartView.java b/src/com/android/settings/widget/ChartView.java index 69e6e94..30284bc 100644 --- a/src/com/android/settings/widget/ChartView.java +++ b/src/com/android/settings/widget/ChartView.java @@ -112,12 +112,18 @@ public class ChartView extends FrameLayout { parentRect.set(mContent); - if (child instanceof ChartNetworkSeriesView || child instanceof ChartGridView) { + if (child instanceof ChartNetworkSeriesView) { // series are always laid out to fill entire graph area // TODO: handle scrolling for series larger than content area Gravity.apply(params.gravity, width, height, parentRect, childRect); child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); + } else if (child instanceof ChartGridView) { + // Grid uses some extra room for labels + Gravity.apply(params.gravity, width, height, parentRect, childRect); + child.layout(childRect.left, childRect.top, childRect.right, + childRect.bottom + child.getPaddingBottom()); + } else if (child instanceof ChartSweepView) { layoutSweep((ChartSweepView) child, parentRect, childRect); child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); @@ -154,5 +160,4 @@ public class ChartView extends FrameLayout { parentRect, childRect); } } - } diff --git a/src/com/android/settings/widget/PieChartView.java b/src/com/android/settings/widget/PieChartView.java deleted file mode 100644 index 6070190..0000000 --- a/src/com/android/settings/widget/PieChartView.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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. - */ - -package com.android.settings.widget; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.Path; -import android.graphics.Path.Direction; -import android.graphics.RadialGradient; -import android.graphics.RectF; -import android.graphics.Shader.TileMode; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; - -import com.google.android.collect.Lists; - -import java.util.ArrayList; - -/** - * Pie chart with multiple items. - */ -public class PieChartView extends View { - public static final String TAG = "PieChartView"; - public static final boolean LOGD = false; - - private static final boolean FILL_GRADIENT = false; - - private ArrayList<Slice> mSlices = Lists.newArrayList(); - - private int mOriginAngle; - private Matrix mMatrix = new Matrix(); - - private Paint mPaintOutline = new Paint(); - - private Path mPathSide = new Path(); - private Path mPathSideOutline = new Path(); - - private Path mPathOutline = new Path(); - - private int mSideWidth; - - public class Slice { - public long value; - - public Path path = new Path(); - public Path pathSide = new Path(); - public Path pathOutline = new Path(); - - public Paint paint; - - public Slice(long value, int color) { - this.value = value; - this.paint = buildFillPaint(color, getResources()); - } - } - - public PieChartView(Context context) { - this(context, null); - } - - public PieChartView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public PieChartView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - mPaintOutline.setColor(Color.BLACK); - mPaintOutline.setStyle(Style.STROKE); - mPaintOutline.setStrokeWidth(3f * getResources().getDisplayMetrics().density); - mPaintOutline.setAntiAlias(true); - - mSideWidth = (int) (20 * getResources().getDisplayMetrics().density); - - setWillNotDraw(false); - } - - private static Paint buildFillPaint(int color, Resources res) { - final Paint paint = new Paint(); - - paint.setColor(color); - paint.setStyle(Style.FILL_AND_STROKE); - paint.setAntiAlias(true); - - if (FILL_GRADIENT) { - final int width = (int) (280 * res.getDisplayMetrics().density); - paint.setShader(new RadialGradient(0, 0, width, color, darken(color), TileMode.MIRROR)); - } - - return paint; - } - - public void setOriginAngle(int originAngle) { - mOriginAngle = originAngle; - } - - public void addSlice(long value, int color) { - mSlices.add(new Slice(value, color)); - } - - public void removeAllSlices() { - mSlices.clear(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - final float centerX = getWidth() / 2; - final float centerY = getHeight() / 2; - - mMatrix.reset(); - mMatrix.postScale(0.665f, 0.95f, centerX, centerY); - mMatrix.postRotate(-40, centerX, centerY); - - generatePath(); - } - - public void generatePath() { - if (LOGD) Log.d(TAG, "generatePath()"); - - long total = 0; - for (Slice slice : mSlices) { - slice.path.reset(); - slice.pathSide.reset(); - slice.pathOutline.reset(); - total += slice.value; - } - - mPathSide.reset(); - mPathSideOutline.reset(); - mPathOutline.reset(); - - // bail when not enough stats to render - if (total == 0) { - invalidate(); - return; - } - - final int width = getWidth(); - final int height = getHeight(); - - final RectF rect = new RectF(0, 0, width, height); - final RectF rectSide = new RectF(); - rectSide.set(rect); - rectSide.offset(-mSideWidth, 0); - - mPathSide.addOval(rectSide, Direction.CW); - mPathSideOutline.addOval(rectSide, Direction.CW); - mPathOutline.addOval(rect, Direction.CW); - - int startAngle = mOriginAngle; - for (Slice slice : mSlices) { - final int sweepAngle = (int) (slice.value * 360 / total); - final int endAngle = startAngle + sweepAngle; - - final float startAngleMod = startAngle % 360; - final float endAngleMod = endAngle % 360; - final boolean startSideVisible = startAngleMod > 90 && startAngleMod < 270; - final boolean endSideVisible = endAngleMod > 90 && endAngleMod < 270; - - // draw slice - slice.path.moveTo(rect.centerX(), rect.centerY()); - slice.path.arcTo(rect, startAngle, sweepAngle); - slice.path.lineTo(rect.centerX(), rect.centerY()); - - if (startSideVisible || endSideVisible) { - - // when start is beyond horizon, push until visible - final float startAngleSide = startSideVisible ? startAngle : 450; - final float endAngleSide = endSideVisible ? endAngle : 270; - final float sweepAngleSide = endAngleSide - startAngleSide; - - // draw slice side - slice.pathSide.moveTo(rect.centerX(), rect.centerY()); - slice.pathSide.arcTo(rect, startAngleSide, 0); - slice.pathSide.rLineTo(-mSideWidth, 0); - slice.pathSide.arcTo(rectSide, startAngleSide, sweepAngleSide); - slice.pathSide.rLineTo(mSideWidth, 0); - slice.pathSide.arcTo(rect, endAngleSide, -sweepAngleSide); - } - - // draw slice outline - slice.pathOutline.moveTo(rect.centerX(), rect.centerY()); - slice.pathOutline.arcTo(rect, startAngle, 0); - if (startSideVisible) { - slice.pathOutline.rLineTo(-mSideWidth, 0); - } - slice.pathOutline.moveTo(rect.centerX(), rect.centerY()); - slice.pathOutline.arcTo(rect, startAngle + sweepAngle, 0); - if (endSideVisible) { - slice.pathOutline.rLineTo(-mSideWidth, 0); - } - - startAngle += sweepAngle; - } - - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - - canvas.concat(mMatrix); - - for (Slice slice : mSlices) { - canvas.drawPath(slice.pathSide, slice.paint); - } - canvas.drawPath(mPathSideOutline, mPaintOutline); - - for (Slice slice : mSlices) { - canvas.drawPath(slice.path, slice.paint); - canvas.drawPath(slice.pathOutline, mPaintOutline); - } - canvas.drawPath(mPathOutline, mPaintOutline); - } - - public static int darken(int color) { - float[] hsv = new float[3]; - Color.colorToHSV(color, hsv); - hsv[2] /= 2; - hsv[1] /= 2; - return Color.HSVToColor(hsv); - } - -} |