375 lines
15 KiB
Java
375 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.development;
|
|
|
|
import android.app.Activity;
|
|
import android.app.PendingIntent;
|
|
import android.app.Dialog;
|
|
import android.app.AlertDialog;
|
|
import android.content.res.TypedArray;
|
|
import android.content.pm.RegisteredServicesCache;
|
|
import android.content.pm.RegisteredServicesCacheListener;
|
|
import android.content.SyncAdapterType;
|
|
import android.content.ISyncAdapter;
|
|
import android.content.ISyncContext;
|
|
import android.content.ServiceConnection;
|
|
import android.content.ComponentName;
|
|
import android.content.SyncResult;
|
|
import android.content.Intent;
|
|
import android.content.Context;
|
|
import android.os.Bundle;
|
|
import android.os.IBinder;
|
|
import android.os.RemoteException;
|
|
import android.widget.ArrayAdapter;
|
|
import android.widget.AdapterView;
|
|
import android.widget.Spinner;
|
|
import android.widget.Button;
|
|
import android.widget.TextView;
|
|
import android.widget.ListView;
|
|
import android.util.AttributeSet;
|
|
import android.provider.Settings;
|
|
import android.accounts.Account;
|
|
import android.accounts.AccountManager;
|
|
import android.view.View;
|
|
import android.view.LayoutInflater;
|
|
|
|
import java.util.Collection;
|
|
|
|
public class SyncAdapterDriver extends Activity
|
|
implements RegisteredServicesCacheListener<SyncAdapterType>,
|
|
AdapterView.OnItemClickListener {
|
|
private Spinner mSyncAdapterSpinner;
|
|
|
|
private Button mBindButton;
|
|
private Button mUnbindButton;
|
|
private TextView mBoundAdapterTextView;
|
|
private Button mStartSyncButton;
|
|
private Button mCancelSyncButton;
|
|
private TextView mStatusTextView;
|
|
private Object[] mSyncAdapters;
|
|
private SyncAdaptersCache mSyncAdaptersCache;
|
|
private final Object mSyncAdaptersLock = new Object();
|
|
|
|
private static final int DIALOG_ID_PICK_ACCOUNT = 1;
|
|
private ListView mAccountPickerView = null;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
mSyncAdaptersCache = new SyncAdaptersCache(this);
|
|
setContentView(R.layout.sync_adapter_driver);
|
|
|
|
mSyncAdapterSpinner = (Spinner) findViewById(R.id.sync_adapters_spinner);
|
|
mBindButton = (Button) findViewById(R.id.bind_button);
|
|
mUnbindButton = (Button) findViewById(R.id.unbind_button);
|
|
mBoundAdapterTextView = (TextView) findViewById(R.id.bound_adapter_text_view);
|
|
|
|
mStartSyncButton = (Button) findViewById(R.id.start_sync_button);
|
|
mCancelSyncButton = (Button) findViewById(R.id.cancel_sync_button);
|
|
|
|
mStatusTextView = (TextView) findViewById(R.id.status_text_view);
|
|
|
|
getSyncAdapters();
|
|
mSyncAdaptersCache.setListener(this, null /* Handler */);
|
|
}
|
|
|
|
protected void onDestroy() {
|
|
mSyncAdaptersCache.close();
|
|
super.onDestroy();
|
|
}
|
|
|
|
private void getSyncAdapters() {
|
|
Collection<RegisteredServicesCache.ServiceInfo<SyncAdapterType>> all =
|
|
mSyncAdaptersCache.getAllServices();
|
|
synchronized (mSyncAdaptersLock) {
|
|
mSyncAdapters = new Object[all.size()];
|
|
String[] names = new String[mSyncAdapters.length];
|
|
int i = 0;
|
|
for (RegisteredServicesCache.ServiceInfo<SyncAdapterType> item : all) {
|
|
mSyncAdapters[i] = item;
|
|
names[i] = item.type.authority + " - " + item.type.accountType;
|
|
i++;
|
|
}
|
|
|
|
ArrayAdapter<String> adapter =
|
|
new ArrayAdapter<String>(this,
|
|
R.layout.sync_adapter_item, names);
|
|
mSyncAdapterSpinner.setAdapter(adapter);
|
|
}
|
|
}
|
|
|
|
void updateUi() {
|
|
boolean isBound;
|
|
boolean hasServiceConnection;
|
|
synchronized (mServiceConnectionLock) {
|
|
hasServiceConnection = mActiveServiceConnection != null;
|
|
isBound = hasServiceConnection && mActiveServiceConnection.mBoundSyncAdapter != null;
|
|
}
|
|
mStartSyncButton.setEnabled(isBound);
|
|
mCancelSyncButton.setEnabled(isBound);
|
|
mBindButton.setEnabled(!hasServiceConnection);
|
|
mUnbindButton.setEnabled(hasServiceConnection);
|
|
}
|
|
|
|
public void startSyncSelected(View view) {
|
|
synchronized (mServiceConnectionLock) {
|
|
ISyncAdapter syncAdapter = null;
|
|
if (mActiveServiceConnection != null) {
|
|
syncAdapter = mActiveServiceConnection.mBoundSyncAdapter;
|
|
}
|
|
|
|
if (syncAdapter != null) {
|
|
removeDialog(DIALOG_ID_PICK_ACCOUNT);
|
|
|
|
mAccountPickerView = (ListView) LayoutInflater.from(this).inflate(
|
|
R.layout.account_list_view, null);
|
|
mAccountPickerView.setOnItemClickListener(this);
|
|
Account accounts[] = AccountManager.get(this).getAccountsByType(
|
|
mActiveServiceConnection.mSyncAdapter.type.accountType);
|
|
String[] accountNames = new String[accounts.length];
|
|
for (int i = 0; i < accounts.length; i++) {
|
|
accountNames[i] = accounts[i].name;
|
|
}
|
|
ArrayAdapter<String> adapter =
|
|
new ArrayAdapter<String>(SyncAdapterDriver.this,
|
|
android.R.layout.simple_list_item_1, accountNames);
|
|
mAccountPickerView.setAdapter(adapter);
|
|
|
|
showDialog(DIALOG_ID_PICK_ACCOUNT);
|
|
}
|
|
}
|
|
updateUi();
|
|
}
|
|
|
|
private void startSync(String accountName) {
|
|
synchronized (mServiceConnectionLock) {
|
|
ISyncAdapter syncAdapter = null;
|
|
if (mActiveServiceConnection != null) {
|
|
syncAdapter = mActiveServiceConnection.mBoundSyncAdapter;
|
|
}
|
|
|
|
if (syncAdapter != null) {
|
|
try {
|
|
mStatusTextView.setText(
|
|
getString(R.string.status_starting_sync_format, accountName));
|
|
Account account = new Account(accountName,
|
|
mActiveServiceConnection.mSyncAdapter.type.accountType);
|
|
syncAdapter.startSync(mActiveServiceConnection,
|
|
mActiveServiceConnection.mSyncAdapter.type.authority,
|
|
account, new Bundle());
|
|
} catch (RemoteException e) {
|
|
mStatusTextView.setText(
|
|
getString(R.string.status_remote_exception_while_starting_sync));
|
|
}
|
|
}
|
|
}
|
|
updateUi();
|
|
}
|
|
|
|
public void cancelSync(View view) {
|
|
synchronized (mServiceConnectionLock) {
|
|
ISyncAdapter syncAdapter = null;
|
|
if (mActiveServiceConnection != null) {
|
|
syncAdapter = mActiveServiceConnection.mBoundSyncAdapter;
|
|
}
|
|
|
|
if (syncAdapter != null) {
|
|
try {
|
|
mStatusTextView.setText(getString(R.string.status_canceled_sync));
|
|
syncAdapter.cancelSync(mActiveServiceConnection);
|
|
} catch (RemoteException e) {
|
|
mStatusTextView.setText(
|
|
getString(R.string.status_remote_exception_while_canceling_sync));
|
|
}
|
|
}
|
|
}
|
|
updateUi();
|
|
}
|
|
|
|
public void onServiceChanged(SyncAdapterType type, boolean removed) {
|
|
getSyncAdapters();
|
|
}
|
|
|
|
@Override
|
|
protected Dialog onCreateDialog(final int id) {
|
|
if (id == DIALOG_ID_PICK_ACCOUNT) {
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
builder.setMessage(R.string.select_account_to_sync);
|
|
builder.setInverseBackgroundForced(true);
|
|
builder.setView(mAccountPickerView);
|
|
return builder.create();
|
|
}
|
|
return super.onCreateDialog(id);
|
|
}
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
TextView item = (TextView) view;
|
|
final String accountName = item.getText().toString();
|
|
dismissDialog(DIALOG_ID_PICK_ACCOUNT);
|
|
startSync(accountName);
|
|
}
|
|
|
|
private class MyServiceConnection extends ISyncContext.Stub implements ServiceConnection {
|
|
private volatile ISyncAdapter mBoundSyncAdapter;
|
|
final RegisteredServicesCache.ServiceInfo<SyncAdapterType> mSyncAdapter;
|
|
|
|
public MyServiceConnection(
|
|
RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter) {
|
|
mSyncAdapter = syncAdapter;
|
|
}
|
|
|
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
mBoundSyncAdapter = ISyncAdapter.Stub.asInterface(service);
|
|
final SyncAdapterType type = mActiveServiceConnection.mSyncAdapter.type;
|
|
mBoundAdapterTextView.setText(getString(R.string.binding_connected_format,
|
|
type.authority, type.accountType));
|
|
updateUi();
|
|
}
|
|
|
|
public void onServiceDisconnected(ComponentName name) {
|
|
mBoundAdapterTextView.setText(getString(R.string.binding_not_connected));
|
|
mBoundSyncAdapter = null;
|
|
updateUi();
|
|
}
|
|
|
|
public void sendHeartbeat() {
|
|
runOnUiThread(new Runnable() {
|
|
public void run() {
|
|
uiThreadSendHeartbeat();
|
|
}
|
|
});
|
|
}
|
|
|
|
public void uiThreadSendHeartbeat() {
|
|
mStatusTextView.setText(getString(R.string.status_received_heartbeat));
|
|
}
|
|
|
|
public void uiThreadOnFinished(SyncResult result) {
|
|
if (result.hasError()) {
|
|
mStatusTextView.setText(
|
|
getString(R.string.status_sync_failed_format, result.toString()));
|
|
} else {
|
|
mStatusTextView.setText(
|
|
getString(R.string.status_sync_succeeded_format, result.toString()));
|
|
}
|
|
}
|
|
|
|
public void onFinished(final SyncResult result) throws RemoteException {
|
|
runOnUiThread(new Runnable() {
|
|
public void run() {
|
|
uiThreadOnFinished(result);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
final Object mServiceConnectionLock = new Object();
|
|
MyServiceConnection mActiveServiceConnection;
|
|
|
|
public void initiateBind(View view) {
|
|
synchronized (mServiceConnectionLock) {
|
|
if (mActiveServiceConnection != null) {
|
|
mStatusTextView.setText(getString(R.string.status_already_bound));
|
|
return;
|
|
}
|
|
|
|
RegisteredServicesCache.ServiceInfo<SyncAdapterType> syncAdapter =
|
|
getSelectedSyncAdapter();
|
|
if (syncAdapter == null) {
|
|
mStatusTextView.setText(getString(R.string.status_sync_adapter_not_selected));
|
|
return;
|
|
}
|
|
|
|
mActiveServiceConnection = new MyServiceConnection(syncAdapter);
|
|
|
|
Intent intent = new Intent();
|
|
intent.setAction("android.content.SyncAdapter");
|
|
intent.setComponent(syncAdapter.componentName);
|
|
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
|
|
com.android.internal.R.string.sync_binding_label);
|
|
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
|
|
this, 0, new Intent(Settings.ACTION_SYNC_SETTINGS), 0));
|
|
if (!bindService(intent, mActiveServiceConnection, Context.BIND_AUTO_CREATE)) {
|
|
mBoundAdapterTextView.setText(getString(R.string.binding_bind_failed));
|
|
mActiveServiceConnection = null;
|
|
return;
|
|
}
|
|
mBoundAdapterTextView.setText(getString(R.string.binding_waiting_for_connection));
|
|
}
|
|
updateUi();
|
|
}
|
|
|
|
public void initiateUnbind(View view) {
|
|
synchronized (mServiceConnectionLock) {
|
|
if (mActiveServiceConnection == null) {
|
|
return;
|
|
}
|
|
mBoundAdapterTextView.setText("");
|
|
unbindService(mActiveServiceConnection);
|
|
mActiveServiceConnection = null;
|
|
}
|
|
updateUi();
|
|
}
|
|
|
|
private RegisteredServicesCache.ServiceInfo<SyncAdapterType> getSelectedSyncAdapter() {
|
|
synchronized (mSyncAdaptersLock) {
|
|
final int position = mSyncAdapterSpinner.getSelectedItemPosition();
|
|
if (position == AdapterView.INVALID_POSITION) {
|
|
return null;
|
|
}
|
|
try {
|
|
//noinspection unchecked
|
|
return (RegisteredServicesCache.ServiceInfo<SyncAdapterType>)
|
|
mSyncAdapters[position];
|
|
} catch (Exception e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
static class SyncAdaptersCache extends RegisteredServicesCache<SyncAdapterType> {
|
|
private static final String SERVICE_INTERFACE = "android.content.SyncAdapter";
|
|
private static final String SERVICE_META_DATA = "android.content.SyncAdapter";
|
|
private static final String ATTRIBUTES_NAME = "sync-adapter";
|
|
|
|
SyncAdaptersCache(Context context) {
|
|
super(context, SERVICE_INTERFACE, SERVICE_META_DATA, ATTRIBUTES_NAME, null);
|
|
}
|
|
|
|
public SyncAdapterType parseServiceAttributes(String packageName, AttributeSet attrs) {
|
|
TypedArray sa = mContext.getResources().obtainAttributes(attrs,
|
|
com.android.internal.R.styleable.SyncAdapter);
|
|
try {
|
|
final String authority =
|
|
sa.getString(com.android.internal.R.styleable.SyncAdapter_contentAuthority);
|
|
final String accountType =
|
|
sa.getString(com.android.internal.R.styleable.SyncAdapter_accountType);
|
|
if (authority == null || accountType == null) {
|
|
return null;
|
|
}
|
|
final boolean userVisible = sa.getBoolean(
|
|
com.android.internal.R.styleable.SyncAdapter_userVisible, true);
|
|
final boolean supportsUploading = sa.getBoolean(
|
|
com.android.internal.R.styleable.SyncAdapter_supportsUploading, true);
|
|
return new SyncAdapterType(authority, accountType, userVisible, supportsUploading);
|
|
} finally {
|
|
sa.recycle();
|
|
}
|
|
}
|
|
}
|
|
}
|