Skip to content

Instantly share code, notes, and snippets.

@rburgosnavas
Last active August 29, 2015 14:11
Show Gist options
  • Save rburgosnavas/f3cd7c7c2e3ff640bda4 to your computer and use it in GitHub Desktop.
Save rburgosnavas/f3cd7c7c2e3ff640bda4 to your computer and use it in GitHub Desktop.
Sync Adapter
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rburgosnavas.droidfs" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
<uses-permission android:name="android.permission.READ_SYNC_STATS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:enabled="true" >
<provider
android:name=".syncadapter.provider.StubProvider"
android:authorities="com.rburgosnavas.droidfs.provider"
android:label="droidfs provider"
android:enabled="true"
android:exported="false"
android:syncable="true" >
</provider>
<service
android:name=".syncadapter.SyncService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter" />
</service>
<service
android:name=".syncadapter.AuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".RecentSoundsActivity"
android:label="@string/title_activity_recent_sounds" >
</activity>
</application>
</manifest>
package com.rburgosnavas.droidfs.syncadapter;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.NetworkErrorException;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
/**
* Created by rburgosnavas on 12/11/14.
*/
public class Authenticator extends AbstractAccountAuthenticator{
public Authenticator(Context context) {
super(context);
Log.i("Authenticator", "created");
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
throw new UnsupportedOperationException();
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
@Override
public String getAuthTokenLabel(String authTokenType) {
throw new UnsupportedOperationException();
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
throw new UnsupportedOperationException();
}
}
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator
xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.rburgosnavas.droifs"
android:icon="@drawable/ic_launcher"
android:smallIcon="@drawable/ic_launcher"
android:label="@string/app_name"
/>
package com.rburgosnavas.droidfs.syncadapter;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class AuthenticatorService extends Service {
private Authenticator authenticator;
@Override
public void onCreate() {
super.onCreate();
authenticator = new Authenticator(this);
Log.i("AuthenticatorService", "created");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("AuthenticatorService", "on start command");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("AuthenticatorService", "destroyed");
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("AuthenticatorService", "unbound");
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.i("AuthenticatorService", "rebound");
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
Log.i("AuthenticatorService", "binding...");
return authenticator.getIBinder();
}
}
package com.rburgosnavas.droidfs.syncadapter;
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SyncResult;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.rburgosnavas.droidfs.clients.HttpAuthClient;
import com.rburgosnavas.droidfs.utils.AuthUtils;
import java.io.IOException;
import java.util.Calendar;
/**
* Created by rburgosnavas on 12/11/14.
*/
public class AuthenticatorSyncAdapter extends AbstractThreadedSyncAdapter{
private static final String TAG = AuthenticatorSyncAdapter.class.getSimpleName();
private SharedPreferences prefs;
public AuthenticatorSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
Log.i(TAG, "sync adapter created");
prefs = context.getSharedPreferences("OAUTH_PREFS", Context.MODE_PRIVATE);
}
public AuthenticatorSyncAdapter(Context context, boolean autoInitialize,
boolean allowParallelSyncs) {
super(context, autoInitialize, allowParallelSyncs);
Log.i(TAG, "sync adapter created");
prefs = context.getSharedPreferences("OAUTH_PREFS", Context.MODE_PRIVATE);
}
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
Log.w(TAG, "performing sync");
if (prefs.contains("ACCESS_TOKEN")) {
long expiration = prefs.getLong("EXPIRATION_TIMESTAMP", -1);
if (AuthUtils.isExpired(expiration)) {
Log.w(TAG, "access_token=" + prefs.getString("ACCESS_TOKEN", "") + " expired.");
final String refreshToken = prefs.getString("REFRESH_TOKEN", "");
if (!"".equals(refreshToken)) {
// TODO network IO
new AsyncTask<String, String, JsonObject>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected void onPostExecute(JsonObject token) {
super.onPostExecute(token);
SharedPreferences prefs = getContext().getSharedPreferences("OAUTH_PREFS", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("ACCESS_TOKEN", token.get("access_token").getAsString());
Calendar c = Calendar.getInstance();
editor.putLong("ACCESS_TIMESTAMP", c.getTimeInMillis());
int time = token.get("expires_in").getAsInt();
c.set(Calendar.SECOND, time);
editor.putInt("EXPIRES_IN", time);
editor.putLong("EXPIRATION_TIMESTAMP", c.getTimeInMillis());
editor.putString("REFRESH_TOKEN", token.get("refresh_token").getAsString());
editor.apply();
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
}
@Override
protected void onCancelled(JsonObject jsonObject) {
super.onCancelled(jsonObject);
}
@Override
protected JsonObject doInBackground(String... params) {
String bodyString = null;
try {
Log.i(TAG, "getting refresh token");
bodyString = HttpAuthClient.getRefreshToken(params[0]).body().string();
Log.i(TAG, "RESPONSE = " + bodyString);
} catch (IOException e) {
e.printStackTrace();
}
JsonObject j = new JsonParser().parse(bodyString).getAsJsonObject();
return j;
}
};
}
} else {
Log.i(TAG, "access_token=" + prefs.getString("ACCESS_TOKEN", "") + " valid.");
}
}
}
}
package com.rburgosnavas.droidfs;
import // ...
public class MainActivity extends Activity implements LoginWebViewClient.OnAuthorizationCodeListener, AuthTask.AuthListener {
// Constants ----------------------------------------------------------------------------------
private static final String TAG = MainActivity.class.getSimpleName();
// ...
private SharedPreferences prefs;
private Account account;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createSyncAccount(this);
prefs = getApplicationContext().getSharedPreferences("OAUTH_PREFS", Context.MODE_PRIVATE);
// ...
}
// ...
/**
* Create a new dummy account for the sync adapter
*
* @param context The application context
*/
public static void createSyncAccount(Context context) {
// Create the account type and default account
Account newAccount = new Account("Test Account", "com.rburgosnavas.droifs");
Log.i(TAG, "account created");
// Get an instance of the Android account manager
AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
/*
* Add the account and account type, no password or user data
* If successful, return the Account object, otherwise report an error.
*/
boolean isAdded = accountManager.addAccountExplicitly(newAccount, null, null);
if (isAdded) {
ContentResolver.setIsSyncable(newAccount, "com.rburgosnavas.droidfs.provider", 1);
ContentResolver.setSyncAutomatically(newAccount, "com.rburgosnavas.droidfs.provider", true);
ContentResolver.addPeriodicSync(newAccount, "com.rburgosnavas.droidfs.provider", Bundle.EMPTY, 60);
Log.i(TAG, "added periodically");
} else {
Log.i(TAG, "request sync");
ContentResolver.requestSync(newAccount, "com.rburgosnavas.droidfs.provider", Bundle.EMPTY);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.rburgosnavas.droifs.provider"
android:accountType="com.rburgosnavas.droifs"
android:userVisible="true"
android:supportsUploading="false"
android:allowParallelSyncs="false"
android:isAlwaysSyncable="false"/>
package com.rburgosnavas.droidfs.syncadapter;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class SyncService extends Service {
private static AuthenticatorSyncAdapter syncAdapter = null;
private static final Object syncAdapterLock = new Object();
@Override
public void onCreate() {
super.onCreate();
Log.i("SyncService", "service created");
synchronized (syncAdapterLock) {
if (syncAdapter == null) {
syncAdapter = new AuthenticatorSyncAdapter(getApplicationContext(), true);
Log.i("SyncService", "SyncAdapter created");
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("SyncService", "service destroyed");
}
@Override
public boolean onUnbind(Intent intent) {
Log.i("SyncService", "service unbound | intent=" + intent.getDataString());
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
Log.i("SyncService", "service rebound");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("SyncService", "on start command");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.i("SyncService", "binding...");
return syncAdapter.getSyncAdapterBinder();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment