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
17package com.android.calendar.selectcalendars;
18
19import android.accounts.Account;
20import android.app.Activity;
21import android.app.ListFragment;
22import android.app.LoaderManager;
23import android.content.ContentResolver;
24import android.content.ContentUris;
25import android.content.ContentValues;
26import android.content.CursorLoader;
27import android.content.Intent;
28import android.content.Loader;
29import android.content.res.Resources;
30import android.database.ContentObserver;
31import android.database.Cursor;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.Handler;
35import android.provider.CalendarContract;
36import android.provider.CalendarContract.Calendars;
37import android.view.LayoutInflater;
38import android.view.View;
39import android.view.ViewGroup;
40import android.widget.Button;
41import android.widget.ListAdapter;
42import android.widget.TextView;
43
44import com.android.calendar.AsyncQueryService;
45import com.android.calendar.R;
46import com.android.calendar.Utils;
47import com.android.calendar.selectcalendars.SelectCalendarsSyncAdapter.CalendarRow;
48
49import java.util.HashMap;
50
51public 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
89    public SelectCalendarsSyncFragment() {
90    }
91
92    public SelectCalendarsSyncFragment(Bundle bundle) {
93        mAccount = new Account(bundle.getString(Calendars.ACCOUNT_NAME),
94                bundle.getString(Calendars.ACCOUNT_TYPE));
95    }
96
97    @Override
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
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
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
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
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
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
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
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
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