• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.settings.dashboard.conditional;
17 
18 import android.content.Context;
19 import android.os.AsyncTask;
20 import android.os.PersistableBundle;
21 import android.util.Log;
22 import android.util.Xml;
23 
24 import com.android.settingslib.core.lifecycle.LifecycleObserver;
25 import com.android.settingslib.core.lifecycle.events.OnPause;
26 import com.android.settingslib.core.lifecycle.events.OnResume;
27 
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 import org.xmlpull.v1.XmlSerializer;
31 
32 import java.io.File;
33 import java.io.FileReader;
34 import java.io.FileWriter;
35 import java.io.IOException;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.List;
40 
41 public class ConditionManager implements LifecycleObserver, OnResume, OnPause {
42 
43     private static final String TAG = "ConditionManager";
44 
45     private static final boolean DEBUG = false;
46 
47     private static final String PKG = "com.android.settings.dashboard.conditional.";
48 
49     private static final String FILE_NAME = "condition_state.xml";
50     private static final String TAG_CONDITIONS = "cs";
51     private static final String TAG_CONDITION = "c";
52     private static final String ATTR_CLASS = "cls";
53 
54     private static ConditionManager sInstance;
55 
56     private final Context mContext;
57     private final ArrayList<Condition> mConditions;
58     private File mXmlFile;
59 
60     private final ArrayList<ConditionListener> mListeners = new ArrayList<>();
61 
ConditionManager(Context context, boolean loadConditionsNow)62     private ConditionManager(Context context, boolean loadConditionsNow) {
63         mContext = context;
64         mConditions = new ArrayList<>();
65         if (loadConditionsNow) {
66             Log.d(TAG, "conditions loading synchronously");
67             ConditionLoader loader = new ConditionLoader();
68             loader.onPostExecute(loader.doInBackground());
69         } else {
70             Log.d(TAG, "conditions loading asychronously");
71             new ConditionLoader().execute();
72         }
73     }
74 
refreshAll()75     public void refreshAll() {
76         final int N = mConditions.size();
77         for (int i = 0; i < N; i++) {
78             mConditions.get(i).refreshState();
79         }
80     }
81 
readFromXml(File xmlFile, ArrayList<Condition> conditions)82     private void readFromXml(File xmlFile, ArrayList<Condition> conditions) {
83         if (DEBUG) Log.d(TAG, "Reading from " + xmlFile.toString());
84         try {
85             XmlPullParser parser = Xml.newPullParser();
86             FileReader in = new FileReader(xmlFile);
87             parser.setInput(in);
88             int state = parser.getEventType();
89 
90             while (state != XmlPullParser.END_DOCUMENT) {
91                 if (TAG_CONDITION.equals(parser.getName())) {
92                     int depth = parser.getDepth();
93                     String clz = parser.getAttributeValue("", ATTR_CLASS);
94                     if (!clz.startsWith(PKG)) {
95                         clz = PKG + clz;
96                     }
97                     Condition condition = createCondition(Class.forName(clz));
98                     PersistableBundle bundle = PersistableBundle.restoreFromXml(parser);
99                     if (DEBUG) Log.d(TAG, "Reading " + clz + " -- " + bundle);
100                     if (condition != null) {
101                         condition.restoreState(bundle);
102                         conditions.add(condition);
103                     } else {
104                         Log.e(TAG, "failed to add condition: " + clz);
105                     }
106                     while (parser.getDepth() > depth) {
107                         parser.next();
108                     }
109                 }
110                 state = parser.next();
111             }
112             in.close();
113         } catch (XmlPullParserException | IOException | ClassNotFoundException e) {
114             Log.w(TAG, "Problem reading " + FILE_NAME, e);
115         }
116     }
117 
saveToXml()118     private void saveToXml() {
119         if (DEBUG) Log.d(TAG, "Writing to " + mXmlFile.toString());
120         try {
121             XmlSerializer serializer = Xml.newSerializer();
122             FileWriter writer = new FileWriter(mXmlFile);
123             serializer.setOutput(writer);
124 
125             serializer.startDocument("UTF-8", true);
126             serializer.startTag("", TAG_CONDITIONS);
127 
128             final int N = mConditions.size();
129             for (int i = 0; i < N; i++) {
130                 PersistableBundle bundle = new PersistableBundle();
131                 if (mConditions.get(i).saveState(bundle)) {
132                     serializer.startTag("", TAG_CONDITION);
133                     final String clz = mConditions.get(i).getClass().getSimpleName();
134                     serializer.attribute("", ATTR_CLASS, clz);
135                     bundle.saveToXml(serializer);
136                     serializer.endTag("", TAG_CONDITION);
137                 }
138             }
139 
140             serializer.endTag("", TAG_CONDITIONS);
141             serializer.flush();
142             writer.close();
143         } catch (XmlPullParserException | IOException e) {
144             Log.w(TAG, "Problem writing " + FILE_NAME, e);
145         }
146     }
147 
addMissingConditions(ArrayList<Condition> conditions)148     private void addMissingConditions(ArrayList<Condition> conditions) {
149         addIfMissing(AirplaneModeCondition.class, conditions);
150         addIfMissing(HotspotCondition.class, conditions);
151         addIfMissing(DndCondition.class, conditions);
152         addIfMissing(BatterySaverCondition.class, conditions);
153         addIfMissing(CellularDataCondition.class, conditions);
154         addIfMissing(BackgroundDataCondition.class, conditions);
155         addIfMissing(WorkModeCondition.class, conditions);
156         addIfMissing(NightDisplayCondition.class, conditions);
157         addIfMissing(RingerMutedCondition.class, conditions);
158         addIfMissing(RingerVibrateCondition.class, conditions);
159         Collections.sort(conditions, CONDITION_COMPARATOR);
160     }
161 
addIfMissing(Class<? extends Condition> clz, ArrayList<Condition> conditions)162     private void addIfMissing(Class<? extends Condition> clz, ArrayList<Condition> conditions) {
163         if (getCondition(clz, conditions) == null) {
164             if (DEBUG) Log.d(TAG, "Adding missing " + clz.getName());
165             Condition condition = createCondition(clz);
166             if (condition != null) {
167                 conditions.add(condition);
168             }
169         }
170     }
171 
createCondition(Class<?> clz)172     private Condition createCondition(Class<?> clz) {
173         if (AirplaneModeCondition.class == clz) {
174             return new AirplaneModeCondition(this);
175         } else if (HotspotCondition.class == clz) {
176             return new HotspotCondition(this);
177         } else if (DndCondition.class == clz) {
178             return new DndCondition(this);
179         } else if (BatterySaverCondition.class == clz) {
180             return new BatterySaverCondition(this);
181         } else if (CellularDataCondition.class == clz) {
182             return new CellularDataCondition(this);
183         } else if (BackgroundDataCondition.class == clz) {
184             return new BackgroundDataCondition(this);
185         } else if (WorkModeCondition.class == clz) {
186             return new WorkModeCondition(this);
187         } else if (NightDisplayCondition.class == clz) {
188             return new NightDisplayCondition(this);
189         } else if (RingerMutedCondition.class == clz) {
190             return new RingerMutedCondition(this);
191         } else if (RingerVibrateCondition.class == clz) {
192             return new RingerVibrateCondition(this);
193         }
194         Log.e(TAG, "unknown condition class: " + clz.getSimpleName());
195         return null;
196     }
197 
getContext()198     Context getContext() {
199         return mContext;
200     }
201 
getCondition(Class<T> clz)202     public <T extends Condition> T getCondition(Class<T> clz) {
203         return getCondition(clz, mConditions);
204     }
205 
getCondition(Class<T> clz, List<Condition> conditions)206     private <T extends Condition> T getCondition(Class<T> clz, List<Condition> conditions) {
207         final int N = conditions.size();
208         for (int i = 0; i < N; i++) {
209             if (clz.equals(conditions.get(i).getClass())) {
210                 return (T) conditions.get(i);
211             }
212         }
213         return null;
214     }
215 
getConditions()216     public List<Condition> getConditions() {
217         return mConditions;
218     }
219 
getVisibleConditions()220     public List<Condition> getVisibleConditions() {
221         List<Condition> conditions = new ArrayList<>();
222         final int N = mConditions.size();
223         for (int i = 0; i < N; i++) {
224             if (mConditions.get(i).shouldShow()) {
225                 conditions.add(mConditions.get(i));
226             }
227         }
228         return conditions;
229     }
230 
notifyChanged(Condition condition)231     public void notifyChanged(Condition condition) {
232         saveToXml();
233         Collections.sort(mConditions, CONDITION_COMPARATOR);
234         final int N = mListeners.size();
235         for (int i = 0; i < N; i++) {
236             mListeners.get(i).onConditionsChanged();
237         }
238     }
239 
addListener(ConditionListener listener)240     public void addListener(ConditionListener listener) {
241         mListeners.add(listener);
242         listener.onConditionsChanged();
243     }
244 
remListener(ConditionListener listener)245     public void remListener(ConditionListener listener) {
246         mListeners.remove(listener);
247     }
248 
249     @Override
onResume()250     public void onResume() {
251         for (int i = 0, size = mConditions.size(); i < size; i++) {
252             mConditions.get(i).onResume();
253         }
254     }
255 
256     @Override
onPause()257     public void onPause() {
258         for (int i = 0, size = mConditions.size(); i < size; i++) {
259             mConditions.get(i).onPause();
260         }
261     }
262 
263     private class ConditionLoader extends AsyncTask<Void, Void, ArrayList<Condition>> {
264         @Override
doInBackground(Void... params)265         protected ArrayList<Condition> doInBackground(Void... params) {
266             Log.d(TAG, "loading conditions from xml");
267             ArrayList<Condition> conditions = new ArrayList<>();
268             mXmlFile = new File(mContext.getFilesDir(), FILE_NAME);
269             if (mXmlFile.exists()) {
270                 readFromXml(mXmlFile, conditions);
271             }
272             addMissingConditions(conditions);
273             return conditions;
274         }
275 
276         @Override
onPostExecute(ArrayList<Condition> conditions)277         protected void onPostExecute(ArrayList<Condition> conditions) {
278             Log.d(TAG, "conditions loaded from xml, refreshing conditions");
279             mConditions.clear();
280             mConditions.addAll(conditions);
281             refreshAll();
282         }
283     }
284 
get(Context context)285     public static ConditionManager get(Context context) {
286         return get(context, true);
287     }
288 
get(Context context, boolean loadConditionsNow)289     public static ConditionManager get(Context context, boolean loadConditionsNow) {
290         if (sInstance == null) {
291             sInstance = new ConditionManager(context.getApplicationContext(), loadConditionsNow);
292         }
293         return sInstance;
294     }
295 
296     public interface ConditionListener {
onConditionsChanged()297         void onConditionsChanged();
298     }
299 
300     private static final Comparator<Condition> CONDITION_COMPARATOR = new Comparator<Condition>() {
301         @Override
302         public int compare(Condition lhs, Condition rhs) {
303             return Long.compare(lhs.getLastChange(), rhs.getLastChange());
304         }
305     };
306 }
307