1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.car.settings.datausage;
17 
18 import android.car.drivingstate.CarUxRestrictions;
19 import android.content.Context;
20 import android.text.format.DateUtils;
21 
22 import androidx.annotation.CallSuper;
23 import androidx.annotation.VisibleForTesting;
24 import androidx.preference.ListPreference;
25 
26 import com.android.car.settings.common.FragmentController;
27 import com.android.car.settings.common.PreferenceController;
28 import com.android.settingslib.net.DataUsageController;
29 import com.android.settingslib.net.NetworkCycleData;
30 
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 
36 /**
37  * Base preference controller for loading data cycle information
38  *
39  * @param <T> type of the {@link NetworkCycleData} objects that will be loaded.
40  */
41 public abstract class DataUsageCycleBasePreferenceController<T extends NetworkCycleData> extends
42         PreferenceController<ListPreference> {
43     private List<T> mList;
44     private boolean mIsLoaded = false;
45     private DataCyclePickedListener<T> mDataCyclePickedListener;
46     private Map<CharSequence, T> mDataUsages = new HashMap<>();
47     private DataUsageController.DataUsageInfo mDataUsageInfo;
48 
49     /** A listener that is called when a data cycle is selected.
50      *
51      * @param <T> type of the {@link NetworkCycleData} objects that was loaded.
52      */
53     public interface DataCyclePickedListener<T> {
54         /** Callback when a new data cycle is selected */
onDataCyclePicked(String cycle, Map<CharSequence, T> usages)55         void onDataCyclePicked(String cycle, Map<CharSequence, T> usages);
56     }
57 
DataUsageCycleBasePreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)58     public DataUsageCycleBasePreferenceController(Context context, String preferenceKey,
59             FragmentController fragmentController, CarUxRestrictions uxRestrictions) {
60         super(context, preferenceKey, fragmentController, uxRestrictions);
61     }
62 
63     @Override
getPreferenceType()64     protected Class<ListPreference> getPreferenceType() {
65         return ListPreference.class;
66     }
67 
68     @Override
69     @CallSuper
updateState(ListPreference preference)70     protected void updateState(ListPreference preference) {
71         CharSequence entry = getPreference().getEntry();
72         if (entry != null) {
73             preference.setEnabled(true);
74             getPreference().setSummary(entry);
75         } else {
76             preference.setEnabled(false);
77             getPreference().setSummary(mDataUsageInfo.period);
78         }
79         String cycle = getPreference().getValue();
80         if (mIsLoaded) {
81             mDataCyclePickedListener.onDataCyclePicked(cycle, mDataUsages);
82         }
83     }
84 
85     @Override
86     @CallSuper
checkInitialized()87     protected void checkInitialized() {
88         if (mDataCyclePickedListener == null || mDataUsageInfo == null) {
89             throw new IllegalStateException("DataCyclePickedListener and DataUsageInfo should be "
90                     + "set before calling this function.");
91         }
92     }
93 
94     /** Set DataCyclePickedListener */
setDataCyclePickedListener( DataCyclePickedListener<T> listener)95     public DataUsageCycleBasePreferenceController<T> setDataCyclePickedListener(
96             DataCyclePickedListener<T> listener) {
97         mDataCyclePickedListener = listener;
98         return this;
99     }
100 
101     /** Set DataUsageInfo */
setDataUsageInfo(DataUsageController.DataUsageInfo dataUsageInfo)102     public void setDataUsageInfo(DataUsageController.DataUsageInfo dataUsageInfo) {
103         mDataUsageInfo = dataUsageInfo;
104     }
105 
106     /** Called to parse loaded data */
onLoaded(List<T> data)107     protected void onLoaded(List<T> data) {
108         mList = data;
109         // TODO: (b/202973988) The data loaded is empty occasionally. In that case, we will not
110         //  refresh the UI or mark it as loaded.
111         if (mList != null && !mList.isEmpty()) {
112             updateCycle(getPreference());
113             mIsLoaded = true;
114             refreshUi();
115         }
116     }
117 
updateCycle(ListPreference preference)118     private void updateCycle(ListPreference preference) {
119         if (mIsLoaded) {
120             return;
121         }
122         ArrayList<CharSequence> entries = new ArrayList<>();
123         ArrayList<CharSequence> entryValues = new ArrayList<>();
124         for (int i = 0; i < mList.size(); i++) {
125             T networkCycleData = mList.get(i);
126             CharSequence entry = formatDate(
127                     networkCycleData.getStartTime(), networkCycleData.getEndTime());
128             CharSequence entryValue = String.valueOf(i);
129             entries.add(entry);
130             entryValues.add(entryValue);
131             mDataUsages.put(entryValue, networkCycleData);
132         }
133         preference.setEntries(entries.toArray(new CharSequence[entries.size()]));
134         preference.setEntryValues(entryValues.toArray(new CharSequence[entries.size()]));
135         preference.setSummary(entries.get(0));
136         preference.setValueIndex(0);
137     }
138 
139     @VisibleForTesting
formatDate(long startTime, long endTime)140     String formatDate(long startTime, long endTime) {
141         return DateUtils.formatDateRange(getContext(), startTime, endTime,
142                 DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE);
143     }
144 }
145