OSDN Git Service

c37075f9bee169b8d37bc2c75821a9f4a885255a
[android-x86/development.git] / apps / Development / src / com / android / development / SyncAdapterDriver.java
1 /*
2  * Copyright (C) 2009 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.development;
18
19 import android.app.Activity;
20 import android.app.PendingIntent;
21 import android.app.Dialog;
22 import android.app.AlertDialog;
23 import android.content.res.TypedArray;
24 import android.content.pm.RegisteredServicesCache;
25 import android.content.pm.RegisteredServicesCacheListener;
26 import android.content.SyncAdapterType;
27 import android.content.ISyncAdapter;
28 import android.content.ISyncContext;
29 import android.content.ServiceConnection;
30 import android.content.ComponentName;
31 import android.content.SyncResult;
32 import android.content.Intent;
33 import android.content.Context;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.RemoteException;
37 import android.widget.ArrayAdapter;
38 import android.widget.AdapterView;
39 import android.widget.Spinner;
40 import android.widget.Button;
41 import android.widget.TextView;
42 import android.widget.ListView;
43 import android.util.AttributeSet;
44 import android.provider.Settings;
45 import android.accounts.Account;
46 import android.accounts.AccountManager;
47 import android.view.View;
48 import android.view.LayoutInflater;
49
50 import java.util.Collection;
51
52 public class SyncAdapterDriver extends Activity
53         implements RegisteredServicesCacheListener<SyncAdapterType>,
54         AdapterView.OnItemClickListener {
55     private Spinner mSyncAdapterSpinner;
56
57     private Button mBindButton;
58     private Button mUnbindButton;
59     private TextView mBoundAdapterTextView;
60     private Button mStartSyncButton;
61     private Button mCancelSyncButton;
62     private TextView mStatusTextView;
63     private Object[] mSyncAdapters;
64     private SyncAdaptersCache mSyncAdaptersCache;
65     private final Object mSyncAdaptersLock = new Object();
66
67     private static final int DIALOG_ID_PICK_ACCOUNT = 1;
68     private ListView mAccountPickerView = null;
69
70     @Override
71     protected void onCreate(Bundle savedInstanceState) {
72         super.onCreate(savedInstanceState);
73         mSyncAdaptersCache = new SyncAdaptersCache(this);
74         setContentView(R.layout.sync_adapter_driver);
75
76         mSyncAdapterSpinner = (Spinner) findViewById(R.id.sync_adapters_spinner);
77         mBindButton = (Button) findViewById(R.id.bind_button);
78         mUnbindButton = (Button) findViewById(R.id.unbind_button);
79         mBoundAdapterTextView = (TextView) findViewById(R.id.bound_adapter_text_view);
80
81         mStartSyncButton = (Button) findViewById(R.id.start_sync_button);
82         mCancelSyncButton = (Button) findViewById(R.id.cancel_sync_button);
83
84         mStatusTextView = (TextView) findViewById(R.id.status_text_view);
85
86         getSyncAdapters();
87         mSyncAdaptersCache.setListener(this, null /* Handler */);
88     }
89
90     protected void onDestroy() {
91         mSyncAdaptersCache.close();
92         super.onDestroy();
93     }
94
95     private void getSyncAdapters() {
96         Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> all =
97                 mSyncAdaptersCache.getAllServices();
98         synchronized (mSyncAdaptersLock) {
99             mSyncAdapters = new Object[all.size()];
100             String[] names = new String[mSyncAdapters.length];
101             int i = 0;
102             for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> item : all) {
103                 mSyncAdapters[i] = item;
104                 names[i] = item.type.authority + " - " + item.type.accountType;
105                 i++;
106             }
107
108             ArrayAdapter<String> adapter =
109                     new ArrayAdapter<String>(this,
110                     R.layout.sync_adapter_item, names);
111             mSyncAdapterSpinner.setAdapter(adapter);
112         }
113     }
114
115     void updateUi() {
116         boolean isBound;
117         boolean hasServiceConnection;
118         synchronized (mServiceConnectionLock) {
119             hasServiceConnection = mActiveServiceConnection != null;
120             isBound = hasServiceConnection && mActiveServiceConnection.mBoundSyncAdapter != null;
121         }
122         mStartSyncButton.setEnabled(isBound);
123         mCancelSyncButton.setEnabled(isBound);
124         mBindButton.setEnabled(!hasServiceConnection);
125         mUnbindButton.setEnabled(hasServiceConnection);
126     }
127
128     public void startSyncSelected(View view) {
129         synchronized (mServiceConnectionLock) {
130             ISyncAdapter syncAdapter = null;
131             if (mActiveServiceConnection != null) {
132                 syncAdapter = mActiveServiceConnection.mBoundSyncAdapter;
133             }
134
135             if (syncAdapter != null) {
136                 removeDialog(DIALOG_ID_PICK_ACCOUNT);
137
138                 mAccountPickerView = (ListView) LayoutInflater.from(this).inflate(
139                         R.layout.account_list_view, null);
140                 mAccountPickerView.setOnItemClickListener(this);
141                 Account accounts[] = AccountManager.get(this).getAccountsByType(
142                         mActiveServiceConnection.mSyncAdapter.type.accountType);
143                 String[] accountNames = new String[accounts.length];
144                 for (int i = 0; i < accounts.length; i++) {
145                     accountNames[i] = accounts[i].name;
146                 }
147                 ArrayAdapter<String> adapter =
148                         new ArrayAdapter<String>(SyncAdapterDriver.this,
149                         android.R.layout.simple_list_item_1, accountNames);
150                 mAccountPickerView.setAdapter(adapter);
151
152                 showDialog(DIALOG_ID_PICK_ACCOUNT);
153             }
154         }
155         updateUi();
156     }
157
158     private void startSync(String accountName) {
159         synchronized (mServiceConnectionLock) {
160             ISyncAdapter syncAdapter = null;
161             if (mActiveServiceConnection != null) {
162                 syncAdapter = mActiveServiceConnection.mBoundSyncAdapter;
163             }
164
165             if (syncAdapter != null) {
166                 try {
167                     mStatusTextView.setText(
168                             getString(R.string.status_starting_sync_format, accountName));
169                     Account account = new Account(accountName,
170                             mActiveServiceConnection.mSyncAdapter.type.accountType);
171                     syncAdapter.startSync(mActiveServiceConnection,
172                             mActiveServiceConnection.mSyncAdapter.type.authority,
173                             account, new Bundle());
174                 } catch (RemoteException e) {
175                     mStatusTextView.setText(
176                             getString(R.string.status_remote_exception_while_starting_sync));
177                 }
178             }
179         }
180         updateUi();
181     }
182
183     public void cancelSync(View view) {
184         synchronized (mServiceConnectionLock) {
185             ISyncAdapter syncAdapter = null;
186             if (mActiveServiceConnection != null) {
187                 syncAdapter = mActiveServiceConnection.mBoundSyncAdapter;
188             }
189
190             if (syncAdapter != null) {
191                 try {
192                     mStatusTextView.setText(getString(R.string.status_canceled_sync));
193                     syncAdapter.cancelSync(mActiveServiceConnection);
194                 } catch (RemoteException e) {
195                     mStatusTextView.setText(
196                             getString(R.string.status_remote_exception_while_canceling_sync));
197                 }
198             }
199         }
200         updateUi();
201     }
202
203     public void onServiceChanged(SyncAdapterType type, boolean removed) {
204         getSyncAdapters();
205     }
206
207     @Override
208     protected Dialog onCreateDialog(final int id) {
209         if (id == DIALOG_ID_PICK_ACCOUNT) {
210             AlertDialog.Builder builder = new AlertDialog.Builder(this);
211             builder.setMessage(R.string.select_account_to_sync);
212             builder.setInverseBackgroundForced(true);
213             builder.setView(mAccountPickerView);
214             return builder.create();
215         }
216         return super.onCreateDialog(id);
217     }
218
219     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
220         TextView item = (TextView) view;
221         final String accountName = item.getText().toString();
222         dismissDialog(DIALOG_ID_PICK_ACCOUNT);
223         startSync(accountName);
224     }
225
226     private class MyServiceConnection extends ISyncContext.Stub implements ServiceConnection {
227         private volatile ISyncAdapter mBoundSyncAdapter;
228         final RegisteredServicesCache.ServiceInfo<SyncAdapterType> mSyncAdapter;
229
230         public MyServiceConnection(
231                 RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter) {
232             mSyncAdapter = syncAdapter;
233         }
234
235         public void onServiceConnected(ComponentName name, IBinder service) {
236             mBoundSyncAdapter = ISyncAdapter.Stub.asInterface(service);
237             final SyncAdapterType type = mActiveServiceConnection.mSyncAdapter.type;
238             mBoundAdapterTextView.setText(getString(R.string.binding_connected_format,
239                     type.authority, type.accountType));
240             updateUi();
241         }
242
243         public void onServiceDisconnected(ComponentName name) {
244             mBoundAdapterTextView.setText(getString(R.string.binding_not_connected));
245             mBoundSyncAdapter = null;
246             updateUi();
247         }
248
249         public void sendHeartbeat() {
250             runOnUiThread(new Runnable() {
251                 public void run() {
252                     uiThreadSendHeartbeat();
253                 }
254             });
255         }
256
257         public void uiThreadSendHeartbeat() {
258             mStatusTextView.setText(getString(R.string.status_received_heartbeat));
259         }
260
261         public void uiThreadOnFinished(SyncResult result) {
262             if (result.hasError()) {
263                 mStatusTextView.setText(
264                         getString(R.string.status_sync_failed_format, result.toString()));
265             } else {
266                 mStatusTextView.setText(
267                         getString(R.string.status_sync_succeeded_format, result.toString()));
268             }
269         }
270
271         public void onFinished(final SyncResult result) throws RemoteException {
272             runOnUiThread(new Runnable() {
273                 public void run() {
274                     uiThreadOnFinished(result);
275                 }
276             });
277         }
278     }
279
280     final Object mServiceConnectionLock = new Object();
281     MyServiceConnection mActiveServiceConnection;
282
283     public void initiateBind(View view) {
284         synchronized (mServiceConnectionLock) {
285             if (mActiveServiceConnection != null) {
286                 mStatusTextView.setText(getString(R.string.status_already_bound));
287                 return;
288             }
289
290             RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter =
291                     getSelectedSyncAdapter();
292             if (syncAdapter == null) {
293                 mStatusTextView.setText(getString(R.string.status_sync_adapter_not_selected));
294                 return;
295             }
296
297             mActiveServiceConnection = new MyServiceConnection(syncAdapter);
298
299             Intent intent = new Intent();
300             intent.setAction("android.content.SyncAdapter");
301             intent.setComponent(syncAdapter.componentName);
302             intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
303                     com.android.internal.R.string.sync_binding_label);
304             intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
305                     this, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0));
306             if (!bindService(intent, mActiveServiceConnection, Context.BIND_AUTO_CREATE)) {
307                 mBoundAdapterTextView.setText(getString(R.string.binding_bind_failed));
308                 mActiveServiceConnection = null;
309                 return;
310             }
311             mBoundAdapterTextView.setText(getString(R.string.binding_waiting_for_connection));
312         }
313         updateUi();
314     }
315
316     public void initiateUnbind(View view) {
317         synchronized (mServiceConnectionLock) {
318             if (mActiveServiceConnection == null) {
319                 return;
320             }
321             mBoundAdapterTextView.setText("");
322             unbindService(mActiveServiceConnection);
323             mActiveServiceConnection = null;
324         }
325         updateUi();
326     }
327
328     private RegisteredServicesCache.ServiceInfo<SyncAdapterType> getSelectedSyncAdapter() {
329         synchronized (mSyncAdaptersLock) {
330             final int position = mSyncAdapterSpinner.getSelectedItemPosition();
331             if (position == AdapterView.INVALID_POSITION) {
332                 return null;
333             }
334             try {
335                 //noinspection unchecked
336                 return (RegisteredServicesCache.ServiceInfo<SyncAdapterType>)
337                         mSyncAdapters[position];
338             } catch (Exception e) {
339                 return null;
340             }
341         }
342     }
343
344     static class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
345         private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
346         private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
347         private static final String ATTRIBUTES_NAME = "sync-adapter";
348
349         SyncAdaptersCache(Context context) {
350             super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, null);
351         }
352
353         public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
354             TypedArray sa = mContext.getResources().obtainAttributes(attrs,
355                     com.android.internal.R.styleable.SyncAdapter);
356             try {
357                 final String authority =
358                         sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
359                 final String accountType =
360                         sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
361                 if (authority == null || accountType == null) {
362                     return null;
363                 }
364                 final boolean userVisible = sa.getBoolean(
365                         com.android.internal.R.styleable.SyncAdapter_userVisible, true);
366                 final boolean supportsUploading = sa.getBoolean(
367                         com.android.internal.R.styleable.SyncAdapter_supportsUploading, true);
368                 return new SyncAdapterType(authority, accountType, userVisible, supportsUploading);
369             } finally {
370                 sa.recycle();
371             }
372         }
373     }
374 }