Skip to content
This repository has been archived by the owner on Oct 8, 2024. It is now read-only.

Commit

Permalink
For android, Add account, syncadapter for contacts, and allow use the…
Browse files Browse the repository at this point in the history
…m from find / save contacts.
  • Loading branch information
jacquarg committed Mar 31, 2015
1 parent 30b2163 commit f938b7c
Show file tree
Hide file tree
Showing 14 changed files with 1,238 additions and 187 deletions.
30 changes: 30 additions & 0 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,42 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<!--uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /-->
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<service android:name="org.apache.cordova.contacts.authenticator.AuthenticationService" android:exported="false" >
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<service android:name="org.apache.cordova.contacts.syncadapter.SyncService" android:exported="false" >
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
<meta-data android:name="android.provider.CONTACTS_STRUCTURE"
android:resource="@xml/contacts" />
</service>

</config-file>

<source-file src="src/android/ContactAccessor.java" target-dir="src/org/apache/cordova/contacts" />
<source-file src="src/android/ContactAccessorSdk5.java" target-dir="src/org/apache/cordova/contacts" />
<source-file src="src/android/ContactManager.java" target-dir="src/org/apache/cordova/contacts" />
<source-file src="src/android/ContactInfoDTO.java" target-dir="src/org/apache/cordova/contacts" />
<source-file src="src/android/authenticator/AuthenticationService.java" target-dir="src/org/apache/cordova/contacts/authenticator" />
<source-file src="src/android/authenticator/Authenticator.java" target-dir="src/org/apache/cordova/contacts/authenticator" />
<source-file src="src/android/syncadapter/SyncAdapter.java" target-dir="src/org/apache/cordova/contacts/syncadapter" />
<source-file src="src/android/syncadapter/SyncService.java" target-dir="src/org/apache/cordova/contacts/syncadapter" />

<source-file src="src/android/res/authenticator.xml" target-dir="res/xml" />
<source-file src="src/android/res/syncadapter.xml" target-dir="res/xml" />
<source-file src="src/android/res/contacts.xml" target-dir="res/xml" />

</platform>

<!-- amazon-fireos -->
Expand Down
9 changes: 5 additions & 4 deletions src/android/ContactAccessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ protected String getJsonString(JSONObject obj, String property) {
*/
public abstract String save(JSONObject contact);

public abstract String save(JSONObject contact, String accountType, String accountName);
/**
* Handles searching through SDK-specific contacts API.
*/
Expand All @@ -157,10 +158,10 @@ protected String getJsonString(JSONObject obj, String property) {
* @throws JSONException
*/
public abstract JSONObject getContactById(String id) throws JSONException;

/**
* Handles searching through SDK-specific contacts API.
* @param desiredFields fields that will filled. All fields will be filled if null
* @param desiredFields fields that will filled. All fields will be filled if null
* @throws JSONException
*/
public abstract JSONObject getContactById(String id, JSONArray desiredFields) throws JSONException;
Expand All @@ -169,9 +170,9 @@ protected String getJsonString(JSONObject obj, String property) {
* Handles removing a contact from the database.
*/
public abstract boolean remove(String id);

/**
* A class that represents the where clause to be used in the database query
* A class that represents the where clause to be used in the database query
*/
class WhereOptions {
private String where;
Expand Down
388 changes: 215 additions & 173 deletions src/android/ContactAccessorSdk5.java

Large diffs are not rendered by default.

46 changes: 39 additions & 7 deletions src/android/ContactManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,19 @@ Licensed to the Apache Software Foundation (ASF) under one
import android.provider.ContactsContract.RawContacts;
import android.util.Log;

// account
import android.accounts.Account;
import android.accounts.AccountManager;
import java.lang.Runnable;
import android.provider.ContactsContract;
import android.content.ContentResolver;

public class ContactManager extends CordovaPlugin {

private ContactAccessor contactAccessor;
private CallbackContext callbackContext; // The callback context from which we were invoked.
private JSONArray executeArgs;

private static final String LOG_TAG = "Contact Query";

public static final int UNKNOWN_ERROR = 0;
Expand All @@ -64,10 +71,10 @@ public ContactManager() {
* @return True if the action was valid, false otherwise.
*/
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {

this.callbackContext = callbackContext;
this.executeArgs = args;
this.executeArgs = args;

/**
* Check to see if we are on an Android 1.X device. If we are return an error as we
* do not support this as of Cordova 1.0.
Expand Down Expand Up @@ -97,10 +104,12 @@ public void run() {
}
else if (action.equals("save")) {
final JSONObject contact = args.getJSONObject(0);
final String accountType = args.optString(1, null);
final String accountName = args.optString(1, null);
this.cordova.getThreadPool().execute(new Runnable(){
public void run() {
JSONObject res = null;
String id = contactAccessor.save(contact);
String id = contactAccessor.save(contact, accountType, accountName);
if (id != null) {
try {
res = contactAccessor.getContactById(id);
Expand Down Expand Up @@ -131,12 +140,35 @@ public void run() {
else if (action.equals("pickContact")) {
pickContactAsync();
}
else if (action.equals("createAccount")) {
createAccount();

// listAccounts();
}

else {
return false;
}
return true;
}



private void createAccount() {
AccountManager accountManager = AccountManager.get(ContactManager.this.cordova.getActivity());
Account account = new Account("myCozy", "io.cozy");

// ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
// ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
accountManager.addAccountExplicitly(account, null, null);
}

private void listAccounts() {
AccountManager accountManager = AccountManager.get(ContactManager.this.cordova.getActivity());
for (Account c: accountManager.getAccounts()) {
Log.d("CordovaContacts", c.type + ", " + c.name);
}
}

/**
* Launches the Contact Picker to select a single contact.
*/
Expand All @@ -150,7 +182,7 @@ public void run() {
};
this.cordova.getThreadPool().execute(worker);
}

/**
* Called when user picks contact.
* @param requestCode The request code originally supplied to startActivityForResult(),
Expand Down
57 changes: 57 additions & 0 deletions src/android/authenticator/AuthenticationService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2010 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 org.apache.cordova.contacts.authenticator;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

/**
* Service to handle Account authentication. It instantiates the authenticator
* and returns its IBinder.
*/
public class AuthenticationService extends Service {

private static final String TAG = "AuthenticationService";

private Authenticator mAuthenticator;

@Override
public void onCreate() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "SampleSyncAdapter Authentication Service started.");
}
mAuthenticator = new Authenticator(this);
}

@Override
public void onDestroy() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "SampleSyncAdapter Authentication Service stopped.");
}
}

@Override
public IBinder onBind(Intent intent) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getBinder()... returning the AccountAuthenticator binder for intent "
+ intent);
}
return mAuthenticator.getIBinder();
}
}
157 changes: 157 additions & 0 deletions src/android/authenticator/Authenticator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright (C) 2010 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 org.apache.cordova.contacts.authenticator;

// import com.example.android.samplesync.Constants;
// import com.example.android.samplesync.client.NetworkUtilities;

import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;

/**
* This class is an implementation of AbstractAccountAuthenticator for
* authenticating accounts in the com.example.android.samplesync domain. The
* interesting thing that this class demonstrates is the use of authTokens as
* part of the authentication process. In the account setup UI, the user enters
* their username and password. But for our subsequent calls off to the service
* for syncing, we want to use an authtoken instead - so we're not continually
* sending the password over the wire. getAuthToken() will be called when
* SyncAdapter calls AccountManager.blockingGetAuthToken(). When we get called,
* we need to return the appropriate authToken for the specified account. If we
* already have an authToken stored in the account, we return that authToken. If
* we don't, but we do have a username and password, then we'll attempt to talk
* to the sample service to fetch an authToken. If that fails (or we didn't have
* a username/password), then we need to prompt the user - so we create an
* AuthenticatorActivity intent and return that. That will display the dialog
* that prompts the user for their login information.
*/
class Authenticator extends AbstractAccountAuthenticator {

/** The tag used to log to adb console. **/
private static final String TAG = "Authenticator";

// Authentication Service context
private final Context mContext;

public Authenticator(Context context) {
super(context);
mContext = context;
}

@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
String authTokenType, String[] requiredFeatures, Bundle options) {
Log.v(TAG, "addAccount()");
// final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
// intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putString(AccountManager.KEY_ACCOUNT_NAME, "myCozy");
bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, "io.cozy");

// bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
// return null;
}

@Override
public Bundle confirmCredentials(
AccountAuthenticatorResponse response, Account account, Bundle options) {
Log.v(TAG, "confirmCredentials()");
return null;
}

@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
Log.v(TAG, "editProperties()");
throw new UnsupportedOperationException();
}

@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle loginOptions) throws NetworkErrorException {
// Log.v(TAG, "getAuthToken()");

// // If the caller requested an authToken type we don't support, then
// // return an error
// if (!authTokenType.equals(Constants.AUTHTOKEN_TYPE)) {
// final Bundle result = new Bundle();
// result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
// return result;
// }

// // Extract the username and password from the Account Manager, and ask
// // the server for an appropriate AuthToken.
// final AccountManager am = AccountManager.get(mContext);
// final String password = am.getPassword(account);
// if (password != null) {
// final String authToken = NetworkUtilities.authenticate(account.name, password);
// if (!TextUtils.isEmpty(authToken)) {
// final Bundle result = new Bundle();
// result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
// result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
// result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
// return result;
// }
// }

// // If we get here, then we couldn't access the user's password - so we
// // need to re-prompt them for their credentials. We do that by creating
// // an intent to display our AuthenticatorActivity panel.
// final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
// intent.putExtra(AuthenticatorActivity.PARAM_USERNAME, account.name);
// intent.putExtra(AuthenticatorActivity.PARAM_AUTHTOKEN_TYPE, authTokenType);
// intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
// bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
// return null;
}

@Override
public String getAuthTokenLabel(String authTokenType) {
// null means we don't support multiple authToken types
Log.v(TAG, "getAuthTokenLabel()");
return null;
}

@Override
public Bundle hasFeatures(
AccountAuthenticatorResponse response, Account account, String[] features) {
// This call is used to query whether the Authenticator supports
// specific features. We don't expect to get called, so we always
// return false (no) for any queries.
Log.v(TAG, "hasFeatures()");
final Bundle result = new Bundle();
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
return result;
}

@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle loginOptions) {
Log.v(TAG, "updateCredentials()");
return null;
}
}
Loading

0 comments on commit f938b7c

Please sign in to comment.