am b5723869: Merge change I85aa2b1d into eclair
Merge commit 'b57238690e7e4afa66416e3ad25183a576f35075' into eclair-mr2 * commit 'b57238690e7e4afa66416e3ad25183a576f35075': create a test app that allows direct control of a sync adapter for testing purposes
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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, 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);
|
||||
}
|
||||
|
||||
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 onRegisteredServicesCacheChanged() {
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user