Hack around inexplicable force close from market report.
[acal.git] / src / com / morphoss / acal / activity / CollectionConfigList.java
blob056183535b58a8b5215cee58d0599eaf427c3c20
1 /*
2 * Copyright (C) 2011 Morphoss Ltd
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 package com.morphoss.acal.activity;
21 import java.util.Map;
22 import java.util.TreeMap;
24 import android.accounts.Account;
25 import android.accounts.AccountAuthenticatorResponse;
26 import android.accounts.AccountManager;
27 import android.content.ContentResolver;
28 import android.content.ContentValues;
29 import android.content.Intent;
30 import android.database.Cursor;
31 import android.database.DatabaseUtils;
32 import android.os.Bundle;
33 import android.os.RemoteException;
34 import android.preference.Preference;
35 import android.preference.Preference.OnPreferenceClickListener;
36 import android.preference.PreferenceActivity;
37 import android.preference.PreferenceCategory;
38 import android.preference.PreferenceScreen;
39 import android.util.Log;
40 import android.view.ContextMenu;
41 import android.view.ContextMenu.ContextMenuInfo;
42 import android.view.MenuItem;
43 import android.view.View;
44 import android.view.View.OnCreateContextMenuListener;
45 import android.widget.AdapterView.AdapterContextMenuInfo;
46 import android.widget.Toast;
48 import com.morphoss.acal.Constants;
49 import com.morphoss.acal.R;
50 import com.morphoss.acal.ServiceManager;
51 import com.morphoss.acal.ServiceManagerCallBack;
52 import com.morphoss.acal.providers.DavCollections;
53 import com.morphoss.acal.providers.Servers;
54 import com.morphoss.acal.service.AcalAuthenticator;
55 import com.morphoss.acal.service.ServiceRequest;
57 /**
58 * <h3>Collection Configuration List - A list of collections that can be configured</h3>
60 * <p>
61 * This class generates and displays the list of collections available in the dav_collection table. Selecting
62 * a collection will start the CollectionConfig activity.
63 * </p>
65 * @author Morphoss Ltd
68 public class CollectionConfigList extends PreferenceActivity
69 implements OnPreferenceClickListener, OnCreateContextMenuListener {
71 public static final String TAG = "aCal CollectionConfigList";
74 // Data from the Collection Table
75 int collectionListCount = 0;
76 private int[] collectionListIds;
77 private Map<Integer, ContentValues> collectionData;
79 private Map<Integer, ContentValues> serverData;
81 // Context Menu Options
82 public static final int CONTEXT_SYNC_NOW = 1;
83 public static final int CONTEXT_DISABLE = 2;
85 private static final int CONTEXT_FORCE_FULL_RESYNC = 3;
88 public static final int UPDATE_COLLECTION_CONFIG = 0;
89 private boolean updateRequested = false;
90 private int updateId = -1;
92 private Cursor mCursor;
94 private ServiceManager serviceManager = null;
96 private PreferenceScreen preferenceRoot;
98 private int serverListCount;
100 private int[] preferenceListIds;
102 // Needed for AcalAuthenticator
103 public static final String ACTION_CHOOSE = "com.morphoss.acal.activity.CollectionConfigList.ACTION_CHOOSE";
106 * Get the list of collections and create the list view.
108 * @see android.app.Activity#onCreate(android.os.Bundle)
109 * @author Morphoss Ltd
111 @Override
112 public void onCreate(Bundle savedInstanceState) {
113 super.onCreate(savedInstanceState);
114 setContentView(R.layout.collections_list);
116 // Get all of the collections from the database
117 getCollectionListItems();
119 // Create configuration screen
120 createPreferenceHierarchy();
121 setPreferenceScreen(this.preferenceRoot);
122 this.preferenceRoot.setOnPreferenceClickListener(this);
124 registerForContextMenu(this.getListView());
129 * <p>
130 * This method connects to the database and gets all collection information. It creates a new ListAdapter and
131 * applies it to the ListView. It also updates our Fields and causes this activity to redraw itself.
132 * Should be called whenever there has been a change to the collection table
133 * </p>
135 * @author Morphoss Ltd
137 private void getCollectionListItems() {
139 // Get Servers Data
140 ContentResolver cr = getContentResolver();
141 mCursor = cr.query(Servers.CONTENT_URI, null, Servers.ACTIVE, null, Servers._ID );
142 this.serverListCount = mCursor.getCount();
144 this.serverData = new TreeMap<Integer,ContentValues>();
145 mCursor.moveToFirst();
146 while( ! mCursor.isAfterLast() ) {
147 ContentValues cv = new ContentValues();
148 DatabaseUtils.cursorRowToContentValues(mCursor, cv);
149 int serverId = cv.getAsInteger(Servers._ID);
150 this.serverData.put(serverId, cv);
151 mCursor.moveToNext();
153 mCursor.close();
155 // Get Collections Data
156 mCursor = cr.query(DavCollections.CONTENT_URI, null, null, null, DavCollections.SERVER_ID+",lower("+DavCollections.DISPLAYNAME+")" );
157 collectionListCount = mCursor.getCount();
159 // Store data in useful structures
160 this.collectionListIds = new int[collectionListCount];
162 this.collectionData = new TreeMap<Integer,ContentValues>();
163 mCursor.moveToFirst();
164 int i = 0;
165 while( ! mCursor.isAfterLast() ) {
166 ContentValues cv = new ContentValues();
167 DatabaseUtils.cursorRowToContentValues(mCursor, cv);
168 int collectionId = cv.getAsInteger(DavCollections._ID);
169 this.collectionListIds[i++] = collectionId;
170 this.collectionData.put(collectionId, cv);
171 mCursor.moveToNext();
173 mCursor.close();
177 * <p>This method constructs all of the preference elements required</p>
178 * @return The preference screen that was created.
180 private void createPreferenceHierarchy() {
182 // Root
183 this.preferenceRoot = getPreferenceManager().createPreferenceScreen(this);
184 this.preferenceRoot.setPersistent(false); //We are not using the SharedPrefs system, we will persist data manually.
186 this.preferenceListIds = new int[collectionListCount+serverListCount];
187 PreferenceCategory currentCategory = null;
188 int lastServerId = -1;
189 int prefRowId = 0;
190 for (int i = 0; i < this.collectionListCount; i++) {
191 int collectionId = collectionListIds[i];
192 ContentValues cv = collectionData.get(collectionId);
193 int serverId = cv.getAsInteger(DavCollections.SERVER_ID);
194 if (serverData.get(serverId) == null || 1 != serverData.get(serverId).getAsInteger(Servers.ACTIVE))
195 continue;
196 if (lastServerId != serverId) {
197 currentCategory = new PreferenceCategory(this);
198 currentCategory.setTitle(serverData.get(serverId).getAsString(Servers.FRIENDLY_NAME));
199 preferenceRoot.addPreference(currentCategory);
200 preferenceListIds[prefRowId++] = 0;
201 lastServerId = serverId;
203 String collectionColour = cv.getAsString(DavCollections.COLOUR);
204 CollectionConfigListItemPreference thisPreference = new CollectionConfigListItemPreference(this);
205 thisPreference.setLayoutResource(R.layout.collections_list_item);
206 thisPreference.setTitle(cv.getAsString(DavCollections.DISPLAYNAME));
207 thisPreference.setSummary(cv.getAsString(DavCollections.COLLECTION_PATH));
208 thisPreference.setCollectionColour(collectionColour);
209 thisPreference.setPersistent(false);
210 thisPreference.setKey(Integer.toString(collectionId));
211 thisPreference.setOnPreferenceClickListener(this);
212 preferenceListIds[prefRowId++] = collectionId;
213 currentCategory.addPreference(thisPreference);
219 * <p>
220 * Called when a user selects 'Sync Now' from the context menu. Schedules an immediate sync
221 * for this collection.
222 * </p>
224 * @param position
225 * The position in CollectionNames of the name of the collection we are to synchronise
226 * @return true if operation was successful
228 * @author Morphoss Ltd
230 private boolean syncCollection( int collectionId, boolean fullCollectionResync ) {
231 try {
232 if ( serviceManager == null ) serviceManager = new ServiceManager(this);
233 if ( fullCollectionResync ) {
234 serviceManager.getServiceRequest().fullCollectionResync(collectionId);
236 else {
237 serviceManager.getServiceRequest().syncCollectionNow(collectionId);
239 return true;
241 catch ( RemoteException re ) {
242 Log.e(TAG, "Unable to send synchronisation request to service: "+re.getMessage());
243 Toast.makeText(CollectionConfigList.this, "Request failed: "+re.getMessage(), Toast.LENGTH_SHORT).show();
245 return false;
248 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
249 if (requestCode == UPDATE_COLLECTION_CONFIG && resultCode == RESULT_OK) {
250 if (data.hasExtra("UpdateRequired")) {
251 int cId = data.getIntExtra("UpdateRequired", -1);
252 if (cId < 0) return;
253 updateId = cId;
254 updateRequested = true;
261 * <p>
262 * Called when a user selects 'Disable Collection' from the context menu.
263 * </p>
265 * @param position
266 * The position in CollectionNames of the name of the collection we are to disable
267 * @return true if operation was successful
269 * @author Morphoss Ltd
271 private boolean disableCollection(int collectionId) {
272 return DavCollections.collectionEnabled(false, collectionId, getContentResolver());
275 public void createAuthenticatedAccount(int collectionId) {
276 ContentValues collectionValues = collectionData.get(collectionId);
277 int serverId = collectionValues.getAsInteger(DavCollections.SERVER_ID);
278 ContentValues serverValues = Servers.getRow(serverId, getContentResolver());
279 String collectionName = collectionValues.getAsString(DavCollections.DISPLAYNAME);
280 String serverName = serverValues.getAsString(Servers.FRIENDLY_NAME);
281 Account account = new Account(serverName + " - " + collectionName, getString(R.string.AcalAccountType));
282 Bundle userData = new Bundle();
283 userData.putString(AcalAuthenticator.SERVER_ID, serverValues.getAsString(Servers._ID));
284 userData.putString(AcalAuthenticator.COLLECTION_ID, collectionValues.getAsString(DavCollections._ID));
285 userData.putString(AcalAuthenticator.USERNAME, serverValues.getAsString(Servers.USERNAME));
286 AccountManager am = AccountManager.get(this);
287 boolean accountCreated = false;
288 try {
289 accountCreated = am.addAccountExplicitly(account, "", userData);
290 } catch( Exception e) {
291 Log.println(Constants.LOGD, TAG, Log.getStackTraceString(e));
294 if ( accountCreated ) {
297 Intent creator = getIntent();
298 Bundle extras = creator.getExtras();
299 if (accountCreated && extras != null) {
300 AccountAuthenticatorResponse response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
301 Bundle result = new Bundle();
302 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
303 result.putString(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.AcalAccountType));
304 response.onResult(result);
306 finish();
310 * <P>
311 * Handles context menu clicks
312 * </P>
314 * @see android.app.Activity#onContextItemSelected(android.view.MenuItem)
315 * @author Morphoss Ltd
317 @Override
318 public boolean onContextItemSelected(MenuItem item) {
319 try {
320 AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
321 int id = preferenceListIds[info.position];
322 if (Constants.LOG_DEBUG) Log.println(Constants.LOGD, TAG, "Context menu on preferenceItem " + info.position + " which I reckon is id " + id);
323 switch (item.getItemId()) {
324 case CONTEXT_SYNC_NOW:
325 return syncCollection(id, false);
326 case CONTEXT_DISABLE:
327 return disableCollection(id);
328 case CONTEXT_FORCE_FULL_RESYNC:
329 return syncCollection(id, true);
330 default:
331 return false;
334 catch (ClassCastException e) {
335 return false;
341 * <h3>Click listener for Collection Configuration List.</h3>
343 * <P>
344 * Called when a collection is selected from the list.
345 * <p>
346 * Gets the appropriate String from this.collectionNames and uses it as a key to get the data from
347 * this.collectionData. Will start the Collection Configuration activity with this data. if 'Add Collection' was
348 * selected, Collection Configuration is sent a blank data set with MODEKEY=MODE_CREATE
349 * </p>
351 * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView,
352 * android.view.View, int, long)
353 * @author Morphoss Ltd
355 public boolean onPreferenceClick(Preference id) {
356 int collectionId = Integer.parseInt(id.getKey());
357 Intent i = this.getIntent();
358 if ( android.os.Build.VERSION.SDK_INT >= 8 && i != null && ACTION_CHOOSE.equals(i.getAction()) ) {
359 createAuthenticatedAccount(collectionId);
361 else {
362 // Create Intent to start new Activity
363 Intent collectionConfigIntent = new Intent();
366 // Get the collection data for the selected collection
367 ContentValues toPass = collectionData.get(collectionId);
369 // Begin new activity
370 collectionConfigIntent.setClassName("com.morphoss.acal", "com.morphoss.acal.activity.CollectionConfiguration");
371 collectionConfigIntent.putExtra("CollectionData", toPass);
372 CollectionConfigList.this.startActivityForResult(collectionConfigIntent, UPDATE_COLLECTION_CONFIG);
374 return true;
378 * Creates the context menus for each item in the list.
380 * @author Morphoss Ltd
382 @Override
383 public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
384 menu.setHeaderTitle(getString(R.string.Collection_Options));
385 menu.add(0, CollectionConfigList.CONTEXT_SYNC_NOW, 0, getString(R.string.Sync_collection_now));
386 menu.add(0, CollectionConfigList.CONTEXT_DISABLE, 0, getString(R.string.Disable_collection));
387 menu.add(0, CollectionConfigList.CONTEXT_FORCE_FULL_RESYNC, 0, getString(R.string.Force_full_resync));
391 * Whenever this activity is resumed, update the collection list as it may have changed.
393 protected void onResume() {
394 super.onResume();
396 //we must have a connection to continue
397 if (updateRequested) {
398 updateRequested = false;
399 if (updateId > 0) {
400 try {
401 if (serviceManager != null) serviceManager.close();
403 catch ( Exception e ) {};
404 this.serviceManager = new ServiceManager(this, new ServiceManagerCallBack() {
406 @Override
407 public void serviceConnected(ServiceRequest serviceRequest) {
408 // TODO Auto-generated method stub
409 try {
410 serviceRequest.fullCollectionResync(updateId);
411 } catch (RemoteException e) {
412 // TODO Auto-generated catch block
413 Log.w(TAG,Log.getStackTraceString(e));
420 updateId = -1;
422 if (this.serviceManager == null) serviceManager = new ServiceManager(this);
425 @Override
426 public void onDestroy() {
427 super.onDestroy();
428 if (this.serviceManager != null) this.serviceManager.close();
429 if (mCursor != null && !mCursor.isClosed()) mCursor.close();
432 @Override
433 public void onPause() {
434 super.onPause();
435 if (this.serviceManager != null) this.serviceManager.close();
436 serviceManager = null;