View Javadoc

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  		// load the global configuration
63  		config = new ConfigurationBean();
64  		final Map<String, Object> prefs = Collections
65  				.unmodifiableMap(getConfigPrefs().getAll());
66  		Binder.bindBeanMap(config, prefs, false, false);
67  		// load the application state
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 }