1 /*
2  * Copyright (C) 2010 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 
17 package com.android.calendar.selectcalendars;
18 
19 import android.accounts.Account;
20 import android.app.Activity;
21 import android.app.ListFragment;
22 import android.app.LoaderManager;
23 import android.content.ContentResolver;
24 import android.content.ContentUris;
25 import android.content.ContentValues;
26 import android.content.CursorLoader;
27 import android.content.Intent;
28 import android.content.Loader;
29 import android.content.res.Resources;
30 import android.database.ContentObserver;
31 import android.database.Cursor;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.provider.CalendarContract;
36 import android.provider.CalendarContract.Calendars;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.widget.Button;
41 import android.widget.ListAdapter;
42 import android.widget.TextView;
43 
44 import com.android.calendar.AsyncQueryService;
45 import com.android.calendar.R;
46 import com.android.calendar.Utils;
47 import com.android.calendar.selectcalendars.SelectCalendarsSyncAdapter.CalendarRow;
48 
49 import java.util.HashMap;
50 
51 public class SelectCalendarsSyncFragment extends ListFragment
52         implements View.OnClickListener, LoaderManager.LoaderCallbacks<Cursor> {
53 
54     private static final String TAG = "SelectCalendarSync";
55 
56     private static final String COLLATE_NOCASE = " COLLATE NOCASE";
57     private static final String SELECTION = Calendars.ACCOUNT_NAME + "=? AND "
58             + Calendars.ACCOUNT_TYPE + "=?";
59     // is primary lets us sort the user's main calendar to the top of the list
60     private static final String IS_PRIMARY = "\"primary\"";
61     private static final String SORT_ORDER = IS_PRIMARY + " DESC," + Calendars.CALENDAR_DISPLAY_NAME
62             + COLLATE_NOCASE;
63 
64     private static final String[] PROJECTION = new String[] {
65         Calendars._ID,
66         Calendars.CALENDAR_DISPLAY_NAME,
67         Calendars.CALENDAR_COLOR,
68         Calendars.SYNC_EVENTS,
69         Calendars.ACCOUNT_NAME,
70         Calendars.ACCOUNT_TYPE,
71         "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + IS_PRIMARY, };
72 
73     private TextView mSyncStatus;
74     private Button mAccountsButton;
75     private Account mAccount;
76     private final String[] mArgs = new String[2];
77     private AsyncQueryService mService;
78     private Handler mHandler = new Handler();
79     private ContentObserver mCalendarsObserver = new ContentObserver(mHandler) {
80         @Override
81         public void onChange(boolean selfChange) {
82             // We don't need our own sync changes to trigger refreshes.
83             if (!selfChange) {
84                 getLoaderManager().initLoader(0, null, SelectCalendarsSyncFragment.this);
85             }
86         }
87     };
88 
SelectCalendarsSyncFragment()89     public SelectCalendarsSyncFragment() {
90     }
91 
SelectCalendarsSyncFragment(Bundle bundle)92     public SelectCalendarsSyncFragment(Bundle bundle) {
93         mAccount = new Account(bundle.getString(Calendars.ACCOUNT_NAME),
94                 bundle.getString(Calendars.ACCOUNT_TYPE));
95     }
96 
97     @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)98     public View onCreateView(
99             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
100         View v = inflater.inflate(R.layout.account_calendars, null);
101         mSyncStatus = (TextView) v.findViewById(R.id.account_status);
102         mSyncStatus.setVisibility(View.GONE);
103 
104         mAccountsButton = (Button) v.findViewById(R.id.sync_settings);
105         mAccountsButton.setVisibility(View.GONE);
106         mAccountsButton.setOnClickListener(this);
107 
108         return v;
109     }
110 
111     @Override
onActivityCreated(Bundle savedInstanceState)112     public void onActivityCreated(Bundle savedInstanceState) {
113         super.onActivityCreated(savedInstanceState);
114         // Give some text to display if there is no data. In a real
115         // application this would come from a resource.
116         setEmptyText(getActivity().getText(R.string.no_syncable_calendars));
117         // Prepare the loader. Either re-connect with an existing one,
118         // or start a new one.
119         getLoaderManager().initLoader(0, null, this);
120     }
121 
122     @Override
onResume()123     public void onResume() {
124         super.onResume();
125         if (!ContentResolver.getMasterSyncAutomatically()
126                 || !ContentResolver.getSyncAutomatically(mAccount, CalendarContract.AUTHORITY)) {
127             Resources res = getActivity().getResources();
128             mSyncStatus.setText(res.getString(R.string.acct_not_synced));
129             mSyncStatus.setVisibility(View.VISIBLE);
130             mAccountsButton.setText(res.getString(R.string.accounts));
131             mAccountsButton.setVisibility(View.VISIBLE);
132         } else {
133             mSyncStatus.setVisibility(View.GONE);
134             mAccountsButton.setVisibility(View.GONE);
135 
136             // Start a background sync to get the list of calendars from the server.
137             Utils.startCalendarMetafeedSync(mAccount);
138             getActivity().getContentResolver().registerContentObserver(
139                     Calendars.CONTENT_URI, true, mCalendarsObserver);
140        }
141     }
142 
143     @Override
onAttach(Activity activity)144     public void onAttach(Activity activity) {
145         super.onAttach(activity);
146         mService = new AsyncQueryService(activity);
147 
148         Bundle bundle = getArguments();
149         if (bundle != null && bundle.containsKey(Calendars.ACCOUNT_NAME)
150                 && bundle.containsKey(Calendars.ACCOUNT_TYPE)) {
151             mAccount = new Account(bundle.getString(Calendars.ACCOUNT_NAME),
152                     bundle.getString(Calendars.ACCOUNT_TYPE));
153         }
154     }
155 
156     @Override
onPause()157     public void onPause() {
158         final ListAdapter listAdapter = getListAdapter();
159         if (listAdapter != null) {
160             HashMap<Long, CalendarRow> changes = ((SelectCalendarsSyncAdapter) listAdapter)
161                     .getChanges();
162             if (changes != null && changes.size() > 0) {
163                 for (CalendarRow row : changes.values()) {
164                     if (row.synced == row.originalSynced) {
165                         continue;
166                     }
167                     long id = row.id;
168                     mService.cancelOperation((int) id);
169                     // Use the full long id in case it makes a difference
170                     Uri uri = ContentUris.withAppendedId(Calendars.CONTENT_URI, row.id);
171                     ContentValues values = new ContentValues();
172                     // Toggle the current setting
173                     int synced = row.synced ? 1 : 0;
174                     values.put(Calendars.SYNC_EVENTS, synced);
175                     values.put(Calendars.VISIBLE, synced);
176                     mService.startUpdate((int) id, null, uri, values, null, null, 0);
177                 }
178                 changes.clear();
179             }
180         }
181         getActivity().getContentResolver().unregisterContentObserver(mCalendarsObserver);
182         super.onPause();
183     }
184 
185     @Override
onCreateLoader(int id, Bundle args)186     public Loader<Cursor> onCreateLoader(int id, Bundle args) {
187         mArgs[0] = mAccount.name;
188         mArgs[1] = mAccount.type;
189         return new CursorLoader(
190                 getActivity(), Calendars.CONTENT_URI, PROJECTION, SELECTION, mArgs, SORT_ORDER);
191     }
192 
193     @Override
onLoadFinished(Loader<Cursor> loader, Cursor data)194     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
195         SelectCalendarsSyncAdapter adapter = (SelectCalendarsSyncAdapter) getListAdapter();
196         if (adapter == null) {
197             adapter = new SelectCalendarsSyncAdapter(getActivity(), data, getFragmentManager());
198             setListAdapter(adapter);
199         } else {
200             adapter.changeCursor(data);
201         }
202         getListView().setOnItemClickListener(adapter);
203     }
204 
onLoaderReset(Loader<Cursor> loader)205     public void onLoaderReset(Loader<Cursor> loader) {
206         setListAdapter(null);
207     }
208 
209     // Called when the Accounts button is pressed. Takes the user to the
210     // Accounts and Sync settings page.
211     @Override
onClick(View v)212     public void onClick(View v) {
213         Intent intent = new Intent();
214         intent.setAction("android.settings.SYNC_SETTINGS");
215         getActivity().startActivity(intent);
216     }
217 }
218