• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
11  * KIND, either express or implied. See the License for the specific language governing
12  * permissions and limitations under the License.
13  */
14 package com.android.settings.datausage;
15 
16 import android.content.Context;
17 import android.net.NetworkPolicy;
18 import android.net.NetworkPolicyManager;
19 import android.net.NetworkStatsHistory;
20 import android.text.format.DateUtils;
21 import android.util.Pair;
22 import android.widget.AdapterView;
23 import android.widget.ArrayAdapter;
24 
25 import com.android.settings.R;
26 import com.android.settings.Utils;
27 import com.android.settingslib.net.ChartData;
28 
29 import java.time.ZonedDateTime;
30 import java.util.Iterator;
31 import java.util.Objects;
32 
33 public class CycleAdapter extends ArrayAdapter<CycleAdapter.CycleItem> {
34 
35     private final SpinnerInterface mSpinner;
36     private final AdapterView.OnItemSelectedListener mListener;
37 
CycleAdapter(Context context, SpinnerInterface spinner, AdapterView.OnItemSelectedListener listener, boolean isHeader)38     public CycleAdapter(Context context, SpinnerInterface spinner,
39             AdapterView.OnItemSelectedListener listener, boolean isHeader) {
40         super(context, isHeader ? R.layout.filter_spinner_item
41                 : R.layout.data_usage_cycle_item);
42         setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
43         mSpinner = spinner;
44         mListener = listener;
45         mSpinner.setAdapter(this);
46         mSpinner.setOnItemSelectedListener(mListener);
47     }
48 
49     /**
50      * Find position of {@link CycleItem} in this adapter which is nearest
51      * the given {@link CycleItem}.
52      */
findNearestPosition(CycleItem target)53     public int findNearestPosition(CycleItem target) {
54         if (target != null) {
55             final int count = getCount();
56             for (int i = count - 1; i >= 0; i--) {
57                 final CycleItem item = getItem(i);
58                 if (item.compareTo(target) >= 0) {
59                     return i;
60                 }
61             }
62         }
63         return 0;
64     }
65 
66     /**
67      * Rebuild list based on {@link NetworkPolicy} and available
68      * {@link NetworkStatsHistory} data. Always selects the newest item,
69      * updating the inspection range on chartData.
70      */
updateCycleList(NetworkPolicy policy, ChartData chartData)71      public boolean updateCycleList(NetworkPolicy policy, ChartData chartData) {
72         // stash away currently selected cycle to try restoring below
73         final CycleAdapter.CycleItem previousItem = (CycleAdapter.CycleItem)
74                 mSpinner.getSelectedItem();
75         clear();
76 
77         final Context context = getContext();
78         NetworkStatsHistory.Entry entry = null;
79 
80         long historyStart = Long.MAX_VALUE;
81         long historyEnd = Long.MIN_VALUE;
82         if (chartData != null) {
83             historyStart = chartData.network.getStart();
84             historyEnd = chartData.network.getEnd();
85         }
86 
87         final long now = System.currentTimeMillis();
88         if (historyStart == Long.MAX_VALUE) historyStart = now;
89         if (historyEnd == Long.MIN_VALUE) historyEnd = now + 1;
90 
91         boolean hasCycles = false;
92         if (policy != null) {
93             final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = NetworkPolicyManager
94                     .cycleIterator(policy);
95             while (it.hasNext()) {
96                 final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
97                 final long cycleStart = cycle.first.toInstant().toEpochMilli();
98                 final long cycleEnd = cycle.second.toInstant().toEpochMilli();
99 
100                 final boolean includeCycle;
101                 if (chartData != null) {
102                     entry = chartData.network.getValues(cycleStart, cycleEnd, entry);
103                     includeCycle = (entry.rxBytes + entry.txBytes) > 0;
104                 } else {
105                     includeCycle = true;
106                 }
107 
108                 if (includeCycle) {
109                     add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
110                     hasCycles = true;
111                 }
112             }
113         }
114 
115         if (!hasCycles) {
116             // no policy defined cycles; show entry for each four-week period
117             long cycleEnd = historyEnd;
118             while (cycleEnd > historyStart) {
119                 final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
120 
121                 final boolean includeCycle;
122                 if (chartData != null) {
123                     entry = chartData.network.getValues(cycleStart, cycleEnd, entry);
124                     includeCycle = (entry.rxBytes + entry.txBytes) > 0;
125                 } else {
126                     includeCycle = true;
127                 }
128 
129                 if (includeCycle) {
130                     add(new CycleAdapter.CycleItem(context, cycleStart, cycleEnd));
131                 }
132                 cycleEnd = cycleStart;
133             }
134         }
135 
136         // force pick the current cycle (first item)
137         if (getCount() > 0) {
138             final int position = findNearestPosition(previousItem);
139             mSpinner.setSelection(position);
140 
141             // only force-update cycle when changed; skipping preserves any
142             // user-defined inspection region.
143             final CycleAdapter.CycleItem selectedItem = getItem(position);
144             if (!Objects.equals(selectedItem, previousItem)) {
145                 mListener.onItemSelected(null, null, position, 0);
146                 return false;
147             }
148         }
149         return true;
150     }
151 
152     /**
153      * List item that reflects a specific data usage cycle.
154      */
155     public static class CycleItem implements Comparable<CycleItem> {
156         public CharSequence label;
157         public long start;
158         public long end;
159 
CycleItem(CharSequence label)160         public CycleItem(CharSequence label) {
161             this.label = label;
162         }
163 
CycleItem(Context context, long start, long end)164         public CycleItem(Context context, long start, long end) {
165             this.label = Utils.formatDateRange(context, start, end);
166             this.start = start;
167             this.end = end;
168         }
169 
170         @Override
toString()171         public String toString() {
172             return label.toString();
173         }
174 
175         @Override
equals(Object o)176         public boolean equals(Object o) {
177             if (o instanceof CycleItem) {
178                 final CycleItem another = (CycleItem) o;
179                 return start == another.start && end == another.end;
180             }
181             return false;
182         }
183 
184         @Override
compareTo(CycleItem another)185         public int compareTo(CycleItem another) {
186             return Long.compare(start, another.start);
187         }
188     }
189 
190     public interface SpinnerInterface {
setAdapter(CycleAdapter cycleAdapter)191         void setAdapter(CycleAdapter cycleAdapter);
setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener)192         void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener);
getSelectedItem()193         Object getSelectedItem();
setSelection(int position)194         void setSelection(int position);
195     }
196 }
197