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 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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    198     Context getContext() {
    199         return mContext;
    200     }
    201 
    202     public <T extends Condition> T getCondition(Class<T> clz) {
    203         return getCondition(clz, mConditions);
    204     }
    205 
    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 
    216     public List<Condition> getConditions() {
    217         return mConditions;
    218     }
    219 
    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 
    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 
    240     public void addListener(ConditionListener listener) {
    241         mListeners.add(listener);
    242         listener.onConditionsChanged();
    243     }
    244 
    245     public void remListener(ConditionListener listener) {
    246         mListeners.remove(listener);
    247     }
    248 
    249     @Override
    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
    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
    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
    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 
    285     public static ConditionManager get(Context context) {
    286         return get(context, true);
    287     }
    288 
    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 {
    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