ConditionManager.java revision db4ed191deefeda79af5644634415597c1c70217
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 */
16package com.android.settings.dashboard.conditional;
17
18import android.content.Context;
19import android.os.PersistableBundle;
20import android.util.Log;
21import android.util.Xml;
22import org.xmlpull.v1.XmlPullParser;
23import org.xmlpull.v1.XmlPullParserException;
24import org.xmlpull.v1.XmlSerializer;
25
26import java.io.File;
27import java.io.FileReader;
28import java.io.FileWriter;
29import java.io.IOException;
30import java.lang.reflect.Constructor;
31import java.lang.reflect.InvocationTargetException;
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.Comparator;
35import java.util.List;
36
37public class ConditionManager {
38
39    private static final String TAG = "ConditionManager";
40
41    private static final boolean DEBUG = true;
42
43    private static final String FILE_NAME = "condition_state.xml";
44    private static final String TAG_CONDITIONS = "conditions";
45    private static final String TAG_CONDITION = "condition";
46    private static final String ATTR_CLASS = "class";
47
48    private static ConditionManager sInstance;
49
50    private final Context mContext;
51    private final ArrayList<Condition> mConditions;
52    private final File mXmlFile;
53
54    private final ArrayList<ConditionListener> mListeners = new ArrayList<>();
55
56    private ConditionManager(Context context) {
57        mContext = context;
58        mConditions = new ArrayList<Condition>();
59        mXmlFile = new File(context.getFilesDir(), FILE_NAME);
60        if (mXmlFile.exists()) {
61            readFromXml();
62        }
63        addMissingConditions();
64    }
65
66    public void refreshAll() {
67        final int N = mConditions.size();
68        for (int i = 0; i < N; i++) {
69            mConditions.get(i).refreshState();
70        }
71    }
72
73    private void readFromXml() {
74        if (DEBUG) Log.d(TAG, "Reading from " + mXmlFile.toString());
75        try {
76            XmlPullParser parser = Xml.newPullParser();
77            FileReader in = new FileReader(mXmlFile);
78            parser.setInput(in);
79            int state = parser.getEventType();
80
81            while (state != XmlPullParser.END_DOCUMENT) {
82                if (TAG_CONDITION.equals(parser.getName())) {
83                    int depth = parser.getDepth();
84                    String clz = parser.getAttributeValue("", ATTR_CLASS);
85                    Condition condition = createCondition(Class.forName(clz));
86                    PersistableBundle bundle = PersistableBundle.restoreFromXml(parser);
87                    if (DEBUG) Log.d(TAG, "Reading " + clz + " -- " + bundle);
88                    condition.restoreState(bundle);
89                    mConditions.add(condition);
90                    while (parser.getDepth() > depth) {
91                        parser.next();
92                    }
93                }
94                state = parser.next();
95            }
96            in.close();
97        } catch (XmlPullParserException | IOException | ClassNotFoundException e) {
98            Log.w(TAG, "Problem reading " + FILE_NAME, e);
99        }
100    }
101
102    private void saveToXml() {
103        if (DEBUG) Log.d(TAG, "Writing to " + mXmlFile.toString());
104        try {
105            XmlSerializer serializer = Xml.newSerializer();
106            FileWriter writer = new FileWriter(mXmlFile);
107            serializer.setOutput(writer);
108
109            serializer.startDocument("UTF-8", true);
110            serializer.startTag("", TAG_CONDITIONS);
111
112            final int N = mConditions.size();
113            for (int i = 0; i < N; i++) {
114                serializer.startTag("", TAG_CONDITION);
115                serializer.attribute("", ATTR_CLASS, mConditions.get(i).getClass().getName());
116                PersistableBundle bundle = new PersistableBundle();
117                mConditions.get(i).saveState(bundle);
118                bundle.saveToXml(serializer);
119                serializer.endTag("", TAG_CONDITION);
120            }
121
122            serializer.endTag("", TAG_CONDITIONS);
123            serializer.flush();
124            writer.close();
125        } catch (XmlPullParserException | IOException e) {
126            Log.w(TAG, "Problem writing " + FILE_NAME, e);
127        }
128    }
129
130    private void addMissingConditions() {
131        addIfMissing(AirplaneModeCondition.class);
132        addIfMissing(HotspotCondition.class);
133    }
134
135    private void addIfMissing(Class<? extends Condition> clz) {
136        if (getCondition(clz) == null) {
137            if (DEBUG) Log.d(TAG, "Adding missing " + clz.getName());
138            mConditions.add(createCondition(clz));
139        }
140    }
141
142    private Condition createCondition(Class<?> clz) {
143        if (AirplaneModeCondition.class == clz) {
144            return new AirplaneModeCondition(this);
145        } else if (HotspotCondition.class == clz) {
146            return new HotspotCondition(this);
147        }
148        try {
149            Constructor<?> constructor = clz.getConstructor(ConditionManager.class);
150            return (Condition) constructor.newInstance(this);
151        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException
152                | InvocationTargetException e) {
153        }
154        return null;
155    }
156
157    Context getContext() {
158        return mContext;
159    }
160
161    public <T extends Condition> T getCondition(Class<T> clz) {
162        final int N = mConditions.size();
163        for (int i = 0; i < N; i++) {
164            if (clz.equals(mConditions.get(i).getClass())) {
165                return (T) mConditions.get(i);
166            }
167        }
168        return null;
169    }
170
171    public List<Condition> getConditions() {
172        return mConditions;
173    }
174
175    public List<Condition> getVisibleConditions() {
176        List<Condition> conditions = new ArrayList<>();
177        final int N = mConditions.size();
178        for (int i = 0; i < N; i++) {
179            if (mConditions.get(i).shouldShow()) {
180                conditions.add(mConditions.get(i));
181            }
182        }
183        Collections.sort(conditions, CONDITION_COMPARATOR);
184        return conditions;
185    }
186
187    public void notifyChanged(Condition condition) {
188        saveToXml();
189        final int N = mListeners.size();
190        for (int i = 0; i < N; i++) {
191            mListeners.get(i).onConditionsChanged();
192        }
193    }
194
195    public void addListener(ConditionListener listener) {
196        mListeners.add(listener);
197    }
198
199    public void remListener(ConditionListener listener) {
200        mListeners.remove(listener);
201    }
202
203    public static ConditionManager get(Context context) {
204        if (sInstance == null) {
205            sInstance = new ConditionManager(context);
206        }
207        return sInstance;
208    }
209
210    public interface ConditionListener {
211        void onConditionsChanged();
212    }
213
214    private static final Comparator<Condition> CONDITION_COMPARATOR = new Comparator<Condition>() {
215        @Override
216        public int compare(Condition lhs, Condition rhs) {
217            return Long.compare(lhs.getLastChange(), rhs.getLastChange());
218        }
219    };
220}
221