Modifier to combine/split mobile network policy.

Create NetworkPolicyModifier which knows about which mobile network
policies can coexist.  Settings UI uses this modifier to drive UI and
persist policies.

Change-Id: Ib3f3841b0a74c14eefb99209dd644a2e7b7e525d
This commit is contained in:
Jeff Sharkey
2011-06-12 23:11:24 -07:00
parent 8a50364a71
commit 05cc0cc4a6
3 changed files with 285 additions and 76 deletions

View File

@@ -16,6 +16,7 @@
package com.android.settings; package com.android.settings;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.computeNextCycleBoundary; import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER; import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
@@ -37,6 +38,8 @@ import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.SwitchPreference;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.text.format.Time; import android.text.format.Time;
@@ -64,6 +67,7 @@ import android.widget.TabHost.TabSpec;
import android.widget.TabWidget; import android.widget.TabWidget;
import android.widget.TextView; import android.widget.TextView;
import com.android.settings.net.NetworkPolicyModifier;
import com.android.settings.widget.DataUsageChartView; import com.android.settings.widget.DataUsageChartView;
import com.android.settings.widget.DataUsageChartView.DataUsageChartListener; import com.android.settings.widget.DataUsageChartView.DataUsageChartListener;
import com.google.android.collect.Lists; import com.google.android.collect.Lists;
@@ -99,7 +103,7 @@ public class DataUsageSummary extends Fragment {
private View mHeader; private View mHeader;
private LinearLayout mSwitches; private LinearLayout mSwitches;
private CheckBoxPreference mDataEnabled; private SwitchPreference mDataEnabled;
private CheckBoxPreference mDisableAtLimit; private CheckBoxPreference mDisableAtLimit;
private View mDataEnabledView; private View mDataEnabledView;
private View mDisableAtLimitView; private View mDisableAtLimitView;
@@ -109,25 +113,28 @@ public class DataUsageSummary extends Fragment {
private Spinner mCycleSpinner; private Spinner mCycleSpinner;
private CycleAdapter mCycleAdapter; private CycleAdapter mCycleAdapter;
private boolean mSplit4G = false; // TODO: persist show wifi flag
private boolean mShowWifi = false; private boolean mShowWifi = false;
private int mTemplate = TEMPLATE_INVALID; private int mTemplate = TEMPLATE_INVALID;
private NetworkPolicy mPolicy; private NetworkPolicyModifier mPolicyModifier;
private NetworkStatsHistory mHistory; private NetworkStatsHistory mHistory;
// TODO: policy service should always provide valid stub policy
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mStatsService = INetworkStatsService.Stub.asInterface( mStatsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
mPolicyService = INetworkPolicyManager.Stub.asInterface( mPolicyService = INetworkPolicyManager.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
final Context context = getActivity();
final String subscriberId = getActiveSubscriberId(context);
mPolicyModifier = new NetworkPolicyModifier(mPolicyService, subscriberId);
setHasOptionsMenu(true);
} }
@Override @Override
@@ -147,7 +154,7 @@ public class DataUsageSummary extends Fragment {
mHeader = inflater.inflate(R.layout.data_usage_header, mListView, false); mHeader = inflater.inflate(R.layout.data_usage_header, mListView, false);
mListView.addHeaderView(mHeader, null, false); mListView.addHeaderView(mHeader, null, false);
mDataEnabled = new CheckBoxPreference(context); mDataEnabled = new SwitchPreference(context);
mDisableAtLimit = new CheckBoxPreference(context); mDisableAtLimit = new CheckBoxPreference(context);
// kick refresh once to force-create views // kick refresh once to force-create views
@@ -185,6 +192,9 @@ public class DataUsageSummary extends Fragment {
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
// read current policy state from service
mPolicyModifier.read();
// this kicks off chain reaction which creates tabs, binds the body to // this kicks off chain reaction which creates tabs, binds the body to
// selected network, and binds chart, cycles and detail list. // selected network, and binds chart, cycles and detail list.
updateTabs(); updateTabs();
@@ -196,13 +206,18 @@ public class DataUsageSummary extends Fragment {
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public void onPrepareOptionsMenu(Menu menu) {
// TODO: persist checked-ness of options to restore tabs later final MenuItem split4g = menu.findItem(R.id.action_split_4g);
split4g.setChecked(mPolicyModifier.isMobilePolicySplit());
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_split_4g: { case R.id.action_split_4g: {
mSplit4G = !item.isChecked(); final boolean mobileSplit = !item.isChecked();
item.setChecked(mSplit4G); mPolicyModifier.setMobilePolicySplit(mobileSplit);
item.setChecked(mPolicyModifier.isMobilePolicySplit());
updateTabs(); updateTabs();
return true; return true;
} }
@@ -217,24 +232,23 @@ public class DataUsageSummary extends Fragment {
} }
/** /**
* Rebuild all tabs based on {@link #mSplit4G} and {@link #mShowWifi}, * Rebuild all tabs based on {@link NetworkPolicyModifier} and
* hiding the tabs entirely when applicable. Selects first tab, and kicks * {@link #mShowWifi}, hiding the tabs entirely when applicable. Selects
* off a full rebind of body contents. * first tab, and kicks off a full rebind of body contents.
*/ */
private void updateTabs() { private void updateTabs() {
// TODO: persist/restore if user wants mobile split, or wifi visibility final boolean mobileSplit = mPolicyModifier.isMobilePolicySplit();
final boolean tabsVisible = mobileSplit || mShowWifi;
final boolean tabsVisible = mSplit4G || mShowWifi;
mTabWidget.setVisibility(tabsVisible ? View.VISIBLE : View.GONE); mTabWidget.setVisibility(tabsVisible ? View.VISIBLE : View.GONE);
mTabHost.clearAllTabs(); mTabHost.clearAllTabs();
if (mSplit4G) { if (mobileSplit) {
mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g)); mTabHost.addTab(buildTabSpec(TAB_3G, R.string.data_usage_tab_3g));
mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g)); mTabHost.addTab(buildTabSpec(TAB_4G, R.string.data_usage_tab_4g));
} }
if (mShowWifi) { if (mShowWifi) {
if (!mSplit4G) { if (!mobileSplit) {
mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile)); mTabHost.addTab(buildTabSpec(TAB_MOBILE, R.string.data_usage_tab_mobile));
} }
mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi)); mTabHost.addTab(buildTabSpec(TAB_WIFI, R.string.data_usage_tab_wifi));
@@ -323,8 +337,7 @@ public class DataUsageSummary extends Fragment {
mDataEnabled.setChecked(true); mDataEnabled.setChecked(true);
try { try {
// load policy and stats for current template // load stats for current template
mPolicy = mPolicyService.getNetworkPolicy(mTemplate, null);
mHistory = mStatsService.getHistoryForNetwork(mTemplate); mHistory = mStatsService.getHistoryForNetwork(mTemplate);
} catch (RemoteException e) { } catch (RemoteException e) {
// since we can't do much without policy or history, and we don't // since we can't do much without policy or history, and we don't
@@ -332,20 +345,10 @@ public class DataUsageSummary extends Fragment {
throw new RuntimeException("problem reading network policy or stats", e); throw new RuntimeException("problem reading network policy or stats", e);
} }
// TODO: eventually service will always provide stub policy
if (mPolicy == null) {
mPolicy = new NetworkPolicy(1, 4 * GB_IN_BYTES, -1);
}
// bind chart to historical stats // bind chart to historical stats
mChart.bindNetworkPolicy(mPolicy);
mChart.bindNetworkStats(mHistory); mChart.bindNetworkStats(mHistory);
// generate cycle list based on policy and available history updatePolicy();
updateCycleList();
// reflect policy limit in checkbox
mDisableAtLimit.setChecked(mPolicy.limitBytes != -1);
// force scroll to top of body // force scroll to top of body
mListView.smoothScrollToPosition(0); mListView.smoothScrollToPosition(0);
@@ -354,6 +357,24 @@ public class DataUsageSummary extends Fragment {
refreshPreferenceViews(); refreshPreferenceViews();
} }
/**
* Update chart sweeps and cycle list to reflect {@link NetworkPolicy} for
* current {@link #mTemplate}.
*/
private void updatePolicy() {
final NetworkPolicy policy = mPolicyModifier.getPolicy(mTemplate);
// reflect policy limit in checkbox
mDisableAtLimit.setChecked(policy != null && policy.limitBytes != LIMIT_DISABLED);
mChart.bindNetworkPolicy(policy);
// generate cycle list based on policy and available history
updateCycleList(policy);
// kick preference views so they rebind from changes above
refreshPreferenceViews();
}
/** /**
* Return full time bounds (earliest and latest time recorded) of the given * Return full time bounds (earliest and latest time recorded) of the given
* {@link NetworkStatsHistory}. * {@link NetworkStatsHistory}.
@@ -376,7 +397,7 @@ public class DataUsageSummary extends Fragment {
* and available {@link NetworkStatsHistory} data. Always selects the newest * and available {@link NetworkStatsHistory} data. Always selects the newest
* item, updating the inspection range on {@link #mChart}. * item, updating the inspection range on {@link #mChart}.
*/ */
private void updateCycleList() { private void updateCycleList(NetworkPolicy policy) {
mCycleAdapter.clear(); mCycleAdapter.clear();
final Context context = mCycleSpinner.getContext(); final Context context = mCycleSpinner.getContext();
@@ -385,28 +406,36 @@ public class DataUsageSummary extends Fragment {
final long historyStart = bounds[0]; final long historyStart = bounds[0];
final long historyEnd = bounds[1]; final long historyEnd = bounds[1];
// find the next cycle boundary if (policy != null) {
long cycleEnd = computeNextCycleBoundary(historyEnd, mPolicy); // find the next cycle boundary
long cycleEnd = computeNextCycleBoundary(historyEnd, policy);
int guardCount = 0; int guardCount = 0;
// walk backwards, generating all valid cycle ranges // walk backwards, generating all valid cycle ranges
while (cycleEnd > historyStart) { while (cycleEnd > historyStart) {
final long cycleStart = computeLastCycleBoundary(cycleEnd, mPolicy); final long cycleStart = computeLastCycleBoundary(cycleEnd, policy);
Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs=" Log.d(TAG, "generating cs=" + cycleStart + " to ce=" + cycleEnd + " waiting for hs="
+ historyStart); + historyStart);
mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd)); mCycleAdapter.add(new CycleItem(context, cycleStart, cycleEnd));
cycleEnd = cycleStart; cycleEnd = cycleStart;
// TODO: remove this guard once we have better testing // TODO: remove this guard once we have better testing
if (guardCount++ > 50) { if (guardCount++ > 50) {
Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds) Log.wtf(TAG, "stuck generating ranges for bounds=" + Arrays.toString(bounds)
+ " and policy=" + mPolicy); + " and policy=" + policy);
}
} }
}
// one last cycle entry to change date // one last cycle entry to modify policy cycle day
mCycleAdapter.add(new CycleChangeItem(context)); mCycleAdapter.add(new CycleChangeItem(context));
} else {
// no valid cycle; show all data
// TODO: offer simple ranges like "last week" etc
mCycleAdapter.add(new CycleItem(context, historyStart, historyEnd));
}
// force pick the current cycle (first item) // force pick the current cycle (first item)
mCycleSpinner.setSelection(0); mCycleSpinner.setSelection(0);
@@ -438,11 +467,11 @@ public class DataUsageSummary extends Fragment {
mDisableAtLimit.setChecked(disableAtLimit); mDisableAtLimit.setChecked(disableAtLimit);
refreshPreferenceViews(); refreshPreferenceViews();
// TODO: push updated policy to service // TODO: create policy if none exists
// TODO: show interstitial warning dialog to user // TODO: show interstitial warning dialog to user
final long limitBytes = disableAtLimit ? 5 * GB_IN_BYTES : -1; final long limitBytes = disableAtLimit ? 5 * GB_IN_BYTES : LIMIT_DISABLED;
mPolicy = new NetworkPolicy(mPolicy.cycleDay, mPolicy.warningBytes, limitBytes); mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes);
mChart.bindNetworkPolicy(mPolicy); updatePolicy();
} }
}; };
@@ -500,6 +529,12 @@ public class DataUsageSummary extends Fragment {
} }
} }
private static String getActiveSubscriberId(Context context) {
final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
Context.TELEPHONY_SERVICE);
return telephony.getSubscriberId();
}
private DataUsageChartListener mChartListener = new DataUsageChartListener() { private DataUsageChartListener mChartListener = new DataUsageChartListener() {
/** {@inheritDoc} */ /** {@inheritDoc} */
public void onInspectRangeChanged() { public void onInspectRangeChanged() {
@@ -508,26 +543,20 @@ public class DataUsageSummary extends Fragment {
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
public void onLimitsChanged() { public void onWarningChanged() {
if (LOGD) Log.d(TAG, "onLimitsChanged()"); if (LOGD) Log.d(TAG, "onWarningChanged()");
// redefine policy and persist into service
// TODO: kick this onto background thread, since service touches disk
// TODO: remove this mPolicy null check, since later service will
// always define baseline value.
final int cycleDay = mPolicy != null ? mPolicy.cycleDay : 1;
final long warningBytes = mChart.getWarningBytes(); final long warningBytes = mChart.getWarningBytes();
final long limitBytes = mDisableAtLimit.isChecked() ? -1 : mChart.getLimitBytes(); mPolicyModifier.setPolicyWarningBytes(mTemplate, warningBytes);
updatePolicy();
}
mPolicy = new NetworkPolicy(cycleDay, warningBytes, limitBytes); /** {@inheritDoc} */
if (LOGD) Log.d(TAG, "persisting policy=" + mPolicy); public void onLimitChanged() {
if (LOGD) Log.d(TAG, "onLimitChanged()");
try { final long limitBytes = mDisableAtLimit.isChecked() ? mChart.getLimitBytes()
mPolicyService.setNetworkPolicy(mTemplate, null, mPolicy); : LIMIT_DISABLED;
} catch (RemoteException e) { mPolicyModifier.setPolicyLimitBytes(mTemplate, limitBytes);
Log.w(TAG, "problem persisting policy", e); updatePolicy();
}
} }
}; };
@@ -605,7 +634,7 @@ public class DataUsageSummary extends Fragment {
public void bindStats(NetworkStats stats) { public void bindStats(NetworkStats stats) {
mItems.clear(); mItems.clear();
for (int i = 0; i < stats.length(); i++) { for (int i = 0; i < stats.size; i++) {
final AppUsageItem item = new AppUsageItem(); final AppUsageItem item = new AppUsageItem();
item.uid = stats.uid[i]; item.uid = stats.uid[i];
item.total = stats.rx[i] + stats.tx[i]; item.total = stats.rx[i] + stats.tx[i];

View File

@@ -0,0 +1,145 @@
/*
* 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.net;
import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
import static com.android.internal.util.Preconditions.checkNotNull;
import android.net.INetworkPolicyManager;
import android.net.NetworkPolicy;
import android.os.AsyncTask;
import android.os.RemoteException;
import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import java.util.ArrayList;
/**
* Utility class to modify list of {@link NetworkPolicy}. Specifically knows
* about which policies can coexist.
*/
public class NetworkPolicyModifier {
private INetworkPolicyManager mPolicyService;
private String mSubscriberId;
private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
public NetworkPolicyModifier(INetworkPolicyManager policyService, String subscriberId) {
mPolicyService = checkNotNull(policyService);
mSubscriberId = subscriberId;
}
public void read() {
try {
final NetworkPolicy[] policies = mPolicyService.getNetworkPolicies();
mPolicies.clear();
for (NetworkPolicy policy : policies) {
mPolicies.add(policy);
}
} catch (RemoteException e) {
throw new RuntimeException("problem reading policies", e);
}
}
public void writeAsync() {
// TODO: consider making more robust by passing through service
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
write();
return null;
}
}.execute();
}
public void write() {
try {
final NetworkPolicy[] policies = mPolicies.toArray(new NetworkPolicy[mPolicies.size()]);
mPolicyService.setNetworkPolicies(policies);
} catch (RemoteException e) {
throw new RuntimeException("problem reading policies", e);
}
}
public NetworkPolicy getPolicy(int networkTemplate) {
for (NetworkPolicy policy : mPolicies) {
if (policy.networkTemplate == networkTemplate
&& Objects.equal(policy.subscriberId, mSubscriberId)) {
return policy;
}
}
return null;
}
public void setPolicyCycleDay(int networkTemplate, int cycleDay) {
getPolicy(networkTemplate).cycleDay = cycleDay;
writeAsync();
}
public void setPolicyWarningBytes(int networkTemplate, long warningBytes) {
getPolicy(networkTemplate).warningBytes = warningBytes;
writeAsync();
}
public void setPolicyLimitBytes(int networkTemplate, long limitBytes) {
getPolicy(networkTemplate).limitBytes = limitBytes;
writeAsync();
}
public boolean isMobilePolicySplit() {
return getPolicy(TEMPLATE_MOBILE_3G_LOWER) != null && getPolicy(TEMPLATE_MOBILE_4G) != null;
}
public void setMobilePolicySplit(boolean split) {
final boolean beforeSplit = isMobilePolicySplit();
if (split == beforeSplit) {
// already in requested state; skip
return;
} else if (beforeSplit && !split) {
// combine, picking most restrictive policy
final NetworkPolicy policy3g = getPolicy(TEMPLATE_MOBILE_3G_LOWER);
final NetworkPolicy policy4g = getPolicy(TEMPLATE_MOBILE_4G);
final NetworkPolicy restrictive = policy3g.compareTo(policy4g) < 0 ? policy3g
: policy4g;
mPolicies.remove(policy3g);
mPolicies.remove(policy4g);
mPolicies.add(new NetworkPolicy(TEMPLATE_MOBILE_ALL, restrictive.subscriberId,
restrictive.cycleDay, restrictive.warningBytes, restrictive.limitBytes));
writeAsync();
} else if (!beforeSplit && split) {
// duplicate existing policy into two rules
final NetworkPolicy policyAll = getPolicy(TEMPLATE_MOBILE_ALL);
mPolicies.remove(policyAll);
mPolicies.add(
new NetworkPolicy(TEMPLATE_MOBILE_3G_LOWER, policyAll.subscriberId,
policyAll.cycleDay, policyAll.warningBytes, policyAll.limitBytes));
mPolicies.add(
new NetworkPolicy(TEMPLATE_MOBILE_4G, policyAll.subscriberId,
policyAll.cycleDay, policyAll.warningBytes, policyAll.limitBytes));
writeAsync();
}
}
}

View File

@@ -21,6 +21,7 @@ import android.graphics.Color;
import android.net.NetworkPolicy; import android.net.NetworkPolicy;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.View;
import com.android.settings.widget.ChartSweepView.OnSweepListener; import com.android.settings.widget.ChartSweepView.OnSweepListener;
@@ -44,7 +45,8 @@ public class DataUsageChartView extends ChartView {
public interface DataUsageChartListener { public interface DataUsageChartListener {
public void onInspectRangeChanged(); public void onInspectRangeChanged();
public void onLimitsChanged(); public void onWarningChanged();
public void onLimitChanged();
} }
private DataUsageChartListener mListener; private DataUsageChartListener mListener;
@@ -78,6 +80,9 @@ public class DataUsageChartView extends ChartView {
mSeries.bindSweepRange(mSweepTime1, mSweepTime2); mSeries.bindSweepRange(mSweepTime1, mSweepTime2);
mSweepDataWarn.addOnSweepListener(mWarningListener);
mSweepDataLimit.addOnSweepListener(mLimitListener);
mSweepTime1.addOnSweepListener(mSweepListener); mSweepTime1.addOnSweepListener(mSweepListener);
mSweepTime2.addOnSweepListener(mSweepListener); mSweepTime2.addOnSweepListener(mSweepListener);
@@ -92,15 +97,29 @@ public class DataUsageChartView extends ChartView {
} }
public void bindNetworkPolicy(NetworkPolicy policy) { public void bindNetworkPolicy(NetworkPolicy policy) {
if (policy.limitBytes != -1) { if (policy == null) {
mSweepDataLimit.setVisibility(View.INVISIBLE);
mSweepDataWarn.setVisibility(View.INVISIBLE);
return;
}
if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
mSweepDataLimit.setVisibility(View.VISIBLE);
mSweepDataLimit.setValue(policy.limitBytes); mSweepDataLimit.setValue(policy.limitBytes);
mSweepDataLimit.setEnabled(true); mSweepDataLimit.setEnabled(true);
} else { } else {
// TODO: set limit default based on axis maximum
mSweepDataLimit.setVisibility(View.VISIBLE);
mSweepDataLimit.setValue(5 * GB_IN_BYTES); mSweepDataLimit.setValue(5 * GB_IN_BYTES);
mSweepDataLimit.setEnabled(false); mSweepDataLimit.setEnabled(false);
} }
mSweepDataWarn.setValue(policy.warningBytes); if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) {
mSweepDataWarn.setVisibility(View.VISIBLE);
mSweepDataWarn.setValue(policy.warningBytes);
} else {
mSweepDataWarn.setVisibility(View.INVISIBLE);
}
} }
private OnSweepListener mSweepListener = new OnSweepListener() { private OnSweepListener mSweepListener = new OnSweepListener() {
@@ -115,6 +134,22 @@ public class DataUsageChartView extends ChartView {
} }
}; };
private OnSweepListener mWarningListener = new OnSweepListener() {
public void onSweep(ChartSweepView sweep, boolean sweepDone) {
if (sweepDone && mListener != null) {
mListener.onWarningChanged();
}
}
};
private OnSweepListener mLimitListener = new OnSweepListener() {
public void onSweep(ChartSweepView sweep, boolean sweepDone) {
if (sweepDone && mListener != null) {
mListener.onLimitChanged();
}
}
};
/** /**
* Return current inspection range (start and end time) based on internal * Return current inspection range (start and end time) based on internal
* {@link ChartSweepView} positions. * {@link ChartSweepView} positions.