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