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