Fixing callback delivery for ContactSaveService

Bug: 3500229
Change-Id: Ia50aa5878344f182e33141ba891c3dea56a170ec
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index f7ede90..9b56f5b 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -23,6 +23,7 @@
 import com.google.android.collect.Sets;
 
 import android.accounts.Account;
+import android.app.Activity;
 import android.app.IntentService;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderOperation.Builder;
@@ -51,6 +52,7 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -112,9 +114,34 @@
 
     private static final int PERSIST_TRIES = 3;
 
+    public interface Listener {
+        public void onServiceCompleted(Intent callbackIntent);
+    }
+
+    private static final LinkedList<Listener> sListeners = new LinkedList<Listener>();
+
+    private Handler mMainHandler;
+
     public ContactSaveService() {
         super(TAG);
         setIntentRedelivery(true);
+        mMainHandler = new Handler(Looper.getMainLooper());
+    }
+
+    public static void registerListener(Listener listener) {
+        if (!(listener instanceof Activity)) {
+            throw new ClassCastException("Only activities can be registered to"
+                    + " receive callback from " + ContactSaveService.class.getName());
+        }
+        synchronized (sListeners) {
+            sListeners.addFirst(listener);
+        }
+    }
+
+    public static void unregisterListener(Listener listener) {
+        synchronized (sListeners) {
+            sListeners.remove(listener);
+        }
     }
 
     @Override
@@ -175,8 +202,6 @@
         // the callback intent.
         Intent callbackIntent = new Intent(context, callbackActivity);
         callbackIntent.setAction(callbackAction);
-        callbackIntent.setFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
         return serviceIntent;
     }
@@ -214,7 +239,7 @@
         Uri rawContactUri = results[0].uri;
         callbackIntent.setData(RawContacts.getContactLookupUri(resolver, rawContactUri));
 
-        startActivity(callbackIntent);
+        deliverCallback(callbackIntent);
     }
 
     /**
@@ -235,8 +260,6 @@
         Intent callbackIntent = new Intent(context, callbackActivity);
         callbackIntent.putExtra(saveModeExtraKey, saveMode);
         callbackIntent.setAction(callbackAction);
-        callbackIntent.setFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
         return serviceIntent;
     }
@@ -309,7 +332,7 @@
 
         callbackIntent.setData(lookupUri);
 
-        startActivity(callbackIntent);
+        deliverCallback(callbackIntent);
     }
 
     private long getRawContactId(EntityDeltaList state,
@@ -348,8 +371,6 @@
         // of the callback intent.
         Intent callbackIntent = new Intent(context, callbackActivity);
         callbackIntent.setAction(callbackAction);
-        callbackIntent.setFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
 
         return serviceIntent;
@@ -377,7 +398,7 @@
         Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
         callbackIntent.putExtra(ContactsContract.Intents.Insert.DATA, Lists.newArrayList(values));
 
-        startActivity(callbackIntent);
+        deliverCallback(callbackIntent);
     }
 
     /**
@@ -541,8 +562,6 @@
         // Callback intent will be invoked by the service once the contacts are joined.
         Intent callbackIntent = new Intent(context, callbackActivity);
         callbackIntent.setAction(callbackAction);
-        callbackIntent.setFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
 
         return serviceIntent;
@@ -657,7 +676,7 @@
                     ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
             callbackIntent.setData(uri);
         }
-        startActivity(callbackIntent);
+        deliverCallback(callbackIntent);
     }
 
     /**
@@ -677,7 +696,7 @@
      * Shows a toast on the UI thread.
      */
     private void showToast(final int message) {
-        new Handler(Looper.getMainLooper()).post(new Runnable() {
+        mMainHandler.post(new Runnable() {
 
             @Override
             public void run() {
@@ -685,4 +704,29 @@
             }
         });
     }
+
+    private void deliverCallback(final Intent callbackIntent) {
+        mMainHandler.post(new Runnable() {
+
+            @Override
+            public void run() {
+                deliverCallbackOnUiThread(callbackIntent);
+            }
+        });
+    }
+
+    void deliverCallbackOnUiThread(final Intent callbackIntent) {
+        // TODO: this assumes that if there are multiple instances of the same
+        // activity registered, the last one registered is the one waiting for
+        // the callback. Validity of this assumption needs to be verified.
+        synchronized (sListeners) {
+            for (Listener listener : sListeners) {
+                if (callbackIntent.getComponent().equals(
+                        ((Activity) listener).getIntent().getComponent())) {
+                    listener.onServiceCompleted(callbackIntent);
+                    return;
+                }
+            }
+        }
+    }
 }
diff --git a/src/com/android/contacts/ContactsActivity.java b/src/com/android/contacts/ContactsActivity.java
index 79ebecb..b78fad2 100644
--- a/src/com/android/contacts/ContactsActivity.java
+++ b/src/com/android/contacts/ContactsActivity.java
@@ -20,12 +20,16 @@
 
 import android.app.Activity;
 import android.content.ContentResolver;
+import android.content.Intent;
 import android.content.SharedPreferences;
+import android.os.Bundle;
 
 /**
  * A common superclass for Contacts activities that handles application-wide services.
  */
-public abstract class ContactsActivity extends Activity {
+public abstract class ContactsActivity extends Activity
+    implements ContactSaveService.Listener
+{
 
     private ContentResolver mContentResolver;
 
@@ -65,4 +69,21 @@
 
         return getApplicationContext().getSystemService(name);
     }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        ContactSaveService.registerListener(this);
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    protected void onDestroy() {
+        ContactSaveService.unregisterListener(this);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onServiceCompleted(Intent callbackIntent) {
+        onNewIntent(callbackIntent);
+    }
 }