1 /***
2 * Ambient - A music player for the Android platform
3 Copyright (C) 2007 Martin Vysny
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 package sk.baka.ambient;
20
21 import java.util.Collections;
22 import java.util.Map;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.ConcurrentMap;
25
26 import sk.baka.ambient.AppState.IAppStateAware;
27 import sk.baka.ambient.activity.ConfigActivity;
28 import sk.baka.ambient.commons.Binder;
29 import sk.baka.ambient.commons.MiscUtils;
30 import sk.baka.ambient.commons.ObjectStorage;
31 import sk.baka.ambient.commons.Ref;
32 import android.content.Context;
33 import android.content.SharedPreferences;
34 import android.content.SharedPreferences.Editor;
35
36 /***
37 * Handles application configuration and state storage.
38 *
39 * @author Martin Vysny
40 */
41 public final class AppStateHandler {
42
43 /***
44 * Stored state.
45 */
46 private final ObjectStorage stateStorage;
47
48 private final AmbientApplication app;
49
50 /***
51 * Creates new handler. The application configuration is loaded
52 * automatically. The handler is essentially a singleton and caller should
53 * make sure there is at most single instance of the handler.
54 *
55 * @param app
56 * owning application.
57 */
58 public AppStateHandler(final AmbientApplication app) {
59 super();
60 this.app = app;
61 stateStorage = new ObjectStorage(".state");
62
63 config = new ConfigurationBean();
64 final Map<String, Object> prefs = Collections
65 .unmodifiableMap(getConfigPrefs().getAll());
66 Binder.bindBeanMap(config, prefs, false, false);
67
68 if (!stateStorage.contains("state")) {
69 state = new AppState();
70 } else {
71 AppState state = new AppState();
72 try {
73 state = (AppState) stateStorage.loadObject("state");
74 } catch (final Exception e) {
75 app.error(AppStateHandler.class, true, app
76 .getString(R.string.appRestoreStateFailed), e);
77 }
78 this.state = state;
79 }
80 }
81
82 /***
83 * A list of components aware of the global application state.
84 */
85 private final ConcurrentMap<Ref<? extends IAppStateAware>, Object> stateAware = new ConcurrentHashMap<Ref<? extends IAppStateAware>, Object>();
86
87 /***
88 * Start-time stored state.
89 */
90 private final AppState state;
91
92 /***
93 * The configuration bean.
94 */
95 private final ConfigurationBean config;
96
97 /***
98 * Checks if a handler with given class is present in the state aware
99 * handlers.
100 *
101 * @param clazz
102 * the handler class, must not be <code>null</code>.
103 * @return <code>true</code> if given handler is present, <code>false</code>
104 * otherwise.
105 */
106 public boolean containsStateAware(
107 final Class<? extends IAppStateAware> clazz) {
108 for (final Ref<? extends IAppStateAware> aware : stateAware.keySet()) {
109 if (clazz.isAssignableFrom(aware.get().getClass())) {
110 return true;
111 }
112 }
113 return false;
114 }
115
116 /***
117 * Saves the state. Polls all global state aware objects.
118 */
119 public synchronized void saveState() {
120 for (final Ref<? extends IAppStateAware> aware : stateAware.keySet()) {
121 aware.get().storeState(state);
122 }
123 try {
124 stateStorage.saveObject("state", state);
125 } catch (final Exception e) {
126 app.error(AppStateHandler.class, true, app
127 .getString(R.string.appSaveStateFailed), e);
128 }
129 app.getBus().getInvocator(IApplicationListener.class, true)
130 .stateChanged(state);
131 }
132
133 /***
134 * Registers given state aware object.
135 *
136 * @param aware
137 * the object instance.
138 */
139 public void registerStateAware(final IAppStateAware aware) {
140 stateAware.put(MiscUtils.getIdentity(aware), MiscUtils.NULL);
141 }
142
143 /***
144 * Unregisters given state aware object.
145 *
146 * @param aware
147 * the object instance.
148 */
149 public void unregisterStateAware(final IAppStateAware aware) {
150 stateAware.remove(MiscUtils.getIdentity(aware));
151 }
152
153 /***
154 * Returns the startup application state.
155 *
156 * @return the application state. Never <code>null</code>.
157 */
158 public AppState getStartupState() {
159 return state;
160 }
161
162 /***
163 * Stores the configuration bean to the shared preferences.
164 */
165 public synchronized void saveConfig() {
166 final Editor editor = getConfigPrefs().edit();
167 Binder.bindBeanToEditor(config, editor, false);
168 editor.commit();
169 app.getBus().getInvocator(IApplicationListener.class, true)
170 .configChanged(config);
171 app.getLibrary().setWatchedDirectories(config.scanDirs);
172 app.getCovers().setMaxStorageSize(config.coverCache * 1024);
173 app.getCovers()
174 .setCacheOverflowBehavior(config.dontDownloadOnCacheFull);
175 app.getCovers().cleanup();
176 }
177
178 /***
179 * Returns the configuration bean.
180 *
181 * @return the configuration bean. Only the {@link ConfigActivity} may
182 * modify this bean!
183 */
184 public ConfigurationBean getConfig() {
185 return config;
186 }
187
188 private SharedPreferences getConfigPrefs() {
189 return app.getSharedPreferences("config", Context.MODE_PRIVATE);
190 }
191 }