add a way for the sync adapter to specify the activity that should
be invoked to reach a settings screen for that sync adapter

Bug: 5204776
Change-Id: I4641067c1f0710c51f2633241a8c87bc4d568af2
diff --git a/api/current.txt b/api/current.txt
index 88a708c..6cf9bb4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5770,6 +5770,7 @@
     ctor public SyncAdapterType(android.os.Parcel);
     method public boolean allowParallelSyncs();
     method public int describeContents();
+    method public java.lang.String getSettingsActivity();
     method public boolean isAlwaysSyncable();
     method public boolean isUserVisible();
     method public static android.content.SyncAdapterType newKey(java.lang.String, java.lang.String);
diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java
index fcc19a2..6bffed7 100644
--- a/core/java/android/content/AbstractThreadedSyncAdapter.java
+++ b/core/java/android/content/AbstractThreadedSyncAdapter.java
@@ -34,6 +34,51 @@
  * If a cancelSync() is received that matches an existing sync operation then the thread
  * that is running that sync operation will be interrupted, which will indicate to the thread
  * that the sync has been canceled.
+ * <p>
+ * In order to be a sync adapter one must extend this class, provide implementations for the
+ * abstract methods and write a service that returns the result of {@link #getSyncAdapterBinder()}
+ * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
+ * with an intent with action <code>android.content.SyncAdapter</code>. This service
+ * must specify the following intent filter and metadata tags in its AndroidManifest.xml file
+ * <pre>
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.content.SyncAdapter" /&gt;
+ *   &lt;/intent-filter&gt;
+ *   &lt;meta-data android:name="android.content.SyncAdapter"
+ *             android:resource="@xml/syncadapter" /&gt;
+ * </pre>
+ * The <code>android:resource</code> attribute must point to a resource that looks like:
+ * <pre>
+ * &lt;sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+ *    android:contentAuthority="authority"
+ *    android:accountType="accountType"
+ *    android:userVisible="true|false"
+ *    android:supportsUploading="true|false"
+ *    android:allowParallelSyncs="true|false"
+ *    android:isAlwaysSyncable="true|false"
+ *    android:syncAdapterSettingsAction="ACTION_OF_SETTINGS_ACTIVITY"
+ * /&gt;
+ * </pre>
+ * <ul>
+ * <li>The <code>android:contentAuthority</code> and <code>android:accountType</code> attributes
+ * indicate which content authority and for which account types this sync adapter serves.
+ * <li><code>android:userVisible</code> defaults to true and controls whether or not this sync
+ * adapter shows up in the Sync Settings screen.
+ * <li><code>android:supportsUploading</code> defaults
+ * to true and if true an upload-only sync will be requested for all syncadapters associated
+ * with an authority whenever that authority's content provider does a
+ * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+ * with syncToNetwork set to true.
+ * <li><code>android:allowParallelSyncs</code> defaults to false and if true indicates that
+ * the sync adapter can handle syncs for multiple accounts at the same time. Otherwise
+ * the SyncManager will wait until the sync adapter is not in use before requesting that
+ * it sync an account's data.
+ * <li><code>android:isAlwaysSyncable</code> defaults to false and if true tells the SyncManager
+ * to intialize the isSyncable state to 1 for that sync adapter for each account that is added.
+ * <li><code>android:syncAdapterSettingsAction</code> defaults to null and if supplied it
+ * specifies an Intent action of an activity that can be used to adjust the sync adapter's
+ * sync settings. The activity must live in the same package as the sync adapter.
+ * </ul>
  */
 public abstract class AbstractThreadedSyncAdapter {
     /**
diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java
index b85346e..8a16ac9 100644
--- a/core/java/android/content/SyncAdapterType.java
+++ b/core/java/android/content/SyncAdapterType.java
@@ -32,6 +32,7 @@
     private final boolean supportsUploading;
     private final boolean isAlwaysSyncable;
     private final boolean allowParallelSyncs;
+    private final String settingsActivity;
 
     public SyncAdapterType(String authority, String accountType, boolean userVisible,
             boolean supportsUploading) {
@@ -47,6 +48,7 @@
         this.supportsUploading = supportsUploading;
         this.isAlwaysSyncable = false;
         this.allowParallelSyncs = false;
+        this.settingsActivity = null;
         this.isKey = false;
     }
 
@@ -54,7 +56,8 @@
     public SyncAdapterType(String authority, String accountType, boolean userVisible,
             boolean supportsUploading,
             boolean isAlwaysSyncable,
-            boolean allowParallelSyncs) {
+            boolean allowParallelSyncs,
+            String settingsActivity) {
         if (TextUtils.isEmpty(authority)) {
             throw new IllegalArgumentException("the authority must not be empty: " + authority);
         }
@@ -67,6 +70,7 @@
         this.supportsUploading = supportsUploading;
         this.isAlwaysSyncable = isAlwaysSyncable;
         this.allowParallelSyncs = allowParallelSyncs;
+        this.settingsActivity = settingsActivity;
         this.isKey = false;
     }
 
@@ -83,6 +87,7 @@
         this.supportsUploading = true;
         this.isAlwaysSyncable = false;
         this.allowParallelSyncs = false;
+        this.settingsActivity = null;
         this.isKey = true;
     }
 
@@ -131,6 +136,18 @@
         return isAlwaysSyncable;
     }
 
+    /**
+     * @return The activity to use to invoke this SyncAdapter's settings activity.
+     * May be null.
+     */
+    public String getSettingsActivity() {
+        if (isKey) {
+            throw new IllegalStateException(
+                    "this method is not allowed to be called when this is a key");
+        }
+        return settingsActivity;
+    }
+
     public static SyncAdapterType newKey(String authority, String accountType) {
         return new SyncAdapterType(authority, accountType);
     }
@@ -163,6 +180,7 @@
                     + ", supportsUploading=" + supportsUploading
                     + ", isAlwaysSyncable=" + isAlwaysSyncable
                     + ", allowParallelSyncs=" + allowParallelSyncs
+                    + ", settingsActivity=" + settingsActivity
                     + "}";
         }
     }
@@ -182,6 +200,7 @@
         dest.writeInt(supportsUploading ? 1 : 0);
         dest.writeInt(isAlwaysSyncable ? 1 : 0);
         dest.writeInt(allowParallelSyncs ? 1 : 0);
+        dest.writeString(settingsActivity);
     }
 
     public SyncAdapterType(Parcel source) {
@@ -191,7 +210,8 @@
                 source.readInt() != 0,
                 source.readInt() != 0,
                 source.readInt() != 0,
-                source.readInt() != 0);
+                source.readInt() != 0,
+                source.readString());
     }
 
     public static final Creator<SyncAdapterType> CREATOR = new Creator<SyncAdapterType>() {
diff --git a/core/java/android/content/SyncAdaptersCache.java b/core/java/android/content/SyncAdaptersCache.java
index 33a713b..7b643a0 100644
--- a/core/java/android/content/SyncAdaptersCache.java
+++ b/core/java/android/content/SyncAdaptersCache.java
@@ -66,8 +66,11 @@
             final boolean allowParallelSyncs =
                     sa.getBoolean(com.android.internal.R.styleable.SyncAdapter_allowParallelSyncs,
                             false);
+            final String settingsActivity =
+                    sa.getString(com.android.internal.R.styleable
+                            .SyncAdapter_settingsActivity);
             return new SyncAdapterType(authority, accountType, userVisible, supportsUploading,
-		    isAlwaysSyncable, allowParallelSyncs);
+                    isAlwaysSyncable, allowParallelSyncs, settingsActivity);
         } finally {
             sa.recycle();
         }
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a536961..93cbde5 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5110,6 +5110,10 @@
              Defaults to false.
              -->
         <attr name="isAlwaysSyncable" format="boolean"/>
+        <!-- If provided, specifies the action of the settings
+             activity for this SyncAdapter.
+             -->
+        <attr name="settingsActivity"/>
     </declare-styleable>
 
     <!-- =============================== -->