From 567294f07b6af4a5b6525645233c974a92296eb1 Mon Sep 17 00:00:00 2001 From: Nikolai Lazarov Date: Thu, 22 Mar 2018 22:31:26 +0200 Subject: [PATCH] Block SyncAdapter.onPerformSync() until the javascript task is executed if the task is user visible. This way when a manual sync is requested from the settings, an indication that the sync is still happening will be shown to the user, thus making a better user experience. --- .../HeadlessService.java | 42 ++++++++++++++++++ .../reactnativesyncadapter/SyncAdapter.java | 43 +++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/android/src/main/java/com/fnp/reactnativesyncadapter/HeadlessService.java b/android/src/main/java/com/fnp/reactnativesyncadapter/HeadlessService.java index e96acf2..3f28096 100644 --- a/android/src/main/java/com/fnp/reactnativesyncadapter/HeadlessService.java +++ b/android/src/main/java/com/fnp/reactnativesyncadapter/HeadlessService.java @@ -3,16 +3,58 @@ import android.app.ActivityManager; import android.content.Context; import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.support.annotation.Nullable; import com.facebook.react.HeadlessJsTaskService; import com.facebook.react.jstasks.HeadlessJsTaskConfig; +import java.util.ArrayList; import java.util.List; public class HeadlessService extends HeadlessJsTaskService { private static final String TASK_ID = "TASK_SYNC_ADAPTER"; + private final IBinder mBinder = new LocalBinder(); + private List mCallbacks = new ArrayList<>(); + + public class LocalBinder extends Binder { + HeadlessService getService() { + return HeadlessService.this; + } + } + + public interface Callback { + void onTaskCompletion(); + } + + @Override + public @Nullable + IBinder onBind(Intent intent) { + return mBinder; + } + + public void notifyOnTaskCompletion(Callback cb) { + mCallbacks.add(cb); + } + + @Override + public void onHeadlessJsTaskFinish(int taskId) { + super.onHeadlessJsTaskFinish(taskId); + for (Callback cb : mCallbacks) { + cb.onTaskCompletion(); + } + mCallbacks.clear(); + } + + public void startHeadlessTask(Intent intent) { + HeadlessJsTaskConfig taskConfig = getTaskConfig(intent); + if (taskConfig != null) { + startTask(taskConfig); + } + } @Override protected HeadlessJsTaskConfig getTaskConfig(Intent intent) { boolean allowForeground = Boolean.valueOf(getString(R.string.rnsb_allow_foreground)); diff --git a/android/src/main/java/com/fnp/reactnativesyncadapter/SyncAdapter.java b/android/src/main/java/com/fnp/reactnativesyncadapter/SyncAdapter.java index 05a21c3..a77b165 100644 --- a/android/src/main/java/com/fnp/reactnativesyncadapter/SyncAdapter.java +++ b/android/src/main/java/com/fnp/reactnativesyncadapter/SyncAdapter.java @@ -3,25 +3,62 @@ import android.accounts.Account; import android.accounts.AccountManager; import android.content.AbstractThreadedSyncAdapter; +import android.content.ComponentName; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.SyncRequest; import android.content.SyncResult; import android.os.Build; import android.os.Bundle; +import android.os.IBinder; -class SyncAdapter extends AbstractThreadedSyncAdapter { +import java.util.concurrent.CountDownLatch; + +class SyncAdapter extends AbstractThreadedSyncAdapter implements HeadlessService.Callback { + + private CountDownLatch doneSignal = new CountDownLatch(1); + private Intent mIntent; + private ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder binder) { + HeadlessService service = ((HeadlessService.LocalBinder)binder).getService(); + service.notifyOnTaskCompletion(SyncAdapter.this); + service.startHeadlessTask(mIntent); + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + } + }; public SyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); } + @Override + public void onTaskCompletion() { + getContext().unbindService(mConnection); + doneSignal.countDown(); + } + @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { - Intent service = new Intent(getContext(), HeadlessService.class); - getContext().startService(service); + mIntent = new Intent(getContext(), HeadlessService.class); + Context context = getContext(); + + if (context.getString(R.string.rnsb_user_visible).equals("true")) { + context.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); + try { + doneSignal.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + context.startService(mIntent); + } } /**