Adding deferred action support to partner content provider

We want partners to be able to provide the appropriate information for
us to include their actions in our deferred notification.

Test: Tested in SUW sample app

Change-Id: Ie62841d143bc9829c00ecd59df8d6a0dea6564ed
diff --git a/library/main/src/com/android/car/setupwizardlib/summary/DeferredAction.java b/library/main/src/com/android/car/setupwizardlib/summary/DeferredAction.java
new file mode 100644
index 0000000..3add5be
--- /dev/null
+++ b/library/main/src/com/android/car/setupwizardlib/summary/DeferredAction.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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 com.android.car.setupwizardlib.summary;
+
+/**
+ * An instance of a summary action's deferred state. This includes the deferred notification
+ * description and the completion state.
+ */
+public class DeferredAction implements Comparable<DeferredAction> {
+
+    public final String deferredNotificationDescription;
+    public final boolean completed;
+    public final int priority;
+
+    public DeferredAction(String deferredNotificationDescription, boolean completed, int priority) {
+        this.deferredNotificationDescription = deferredNotificationDescription;
+        this.completed = completed;
+        this.priority = priority;
+    }
+
+    @Override
+    public int compareTo(DeferredAction o) {
+        if (o == null) {
+            return 1;
+        }
+        return priority - o.priority;
+    }
+}
diff --git a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
index 15f180a..aae89dc 100644
--- a/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
+++ b/library/main/src/com/android/car/setupwizardlib/summary/PartnerSummaryActionsCollector.java
@@ -47,6 +47,8 @@
     // Methods for fetching info from the content provider.
     private static final String METHOD_GET_ACTION_COMPLETION_STATE = "get_action_completion_state";
     private static final String METHOD_GET_ACTION_SUMMARY_STATE = "get_action_summary_state";
+    private static final String METHOD_GET_DEFERRED_ACTION_STATE =
+            "get_deferred_action_state";
     private static final String METHOD_GET_SUMMARY_ACTIONS = "get_summary_actions";
 
     // Constants for fetching information from the bundles passed back by the content provider.
@@ -68,15 +70,30 @@
             "summary_action_icon_resource_name";
     private static final String EXTRA_SUMMARY_COMPLETED_DESCRIPTION =
             "summary_action_completed_description";
+    private static final String EXTRA_SUMMARY_ACTION_DEFERRED_NOTIFICATION_DESCRIPTION =
+            "summary_action_deferred_notification_description";
 
     // Extra used as a key for the action id passed in to query summary action state.
     private static final String EXTRA_ACTION_ID = "action_id";
     private static PartnerSummaryActionsCollector partnerSummaryActionsCollector;
     private final Context context;
+    private Uri mContentProviderUri;
 
     /** private constructor, should use getter. */
     private PartnerSummaryActionsCollector(Context context) {
         this.context = context;
+        ResolveInfo resolveInfo = getSummaryContentProviderResolveInfo(context.getPackageManager());
+
+        if (resolveInfo == null) {
+            Log.e(TAG, "Could not find partner content provider, ignoring partner summary items.");
+            return;
+        }
+
+        mContentProviderUri = getSummaryContentProviderUri(resolveInfo);
+
+        if (mContentProviderUri == null) {
+            Log.e(TAG, "Could not fetch content provider URI, ignoring partner summary items.");
+        }
     }
 
     /** Gets the current instance of the {@link PartnerSummaryActionsCollector}. */
@@ -146,6 +163,28 @@
                 completedDescription);
     }
 
+    /**
+     * Creates a {@link DeferredAction} based on the passed in completion state and deferred action
+     * state bundle. Will return null if there is no notification description or a null bundle.
+     */
+    private static DeferredAction buildDeferredAction(boolean completed,
+            Bundle deferredActionState) {
+        if (deferredActionState == null) {
+            Log.e(TAG, "Cannot build deferred action with null deferredActionState");
+            return null;
+        }
+
+        String deferredNotificationDescription = deferredActionState.getString(
+                EXTRA_SUMMARY_ACTION_DEFERRED_NOTIFICATION_DESCRIPTION);
+        if (deferredNotificationDescription == null) {
+            Log.v(TAG, "Cannot build deferred action with no notification description");
+            return null;
+        }
+
+        int priority = deferredActionState.getInt(EXTRA_SUMMARY_ACTION_PRIORITY, 0);
+        return new DeferredAction(deferredNotificationDescription, completed, priority);
+    }
+
     private static ResolveInfo getSummaryContentProviderResolveInfo(PackageManager packageManager) {
         Intent contentProviderQueryIntent = new Intent(CONTENT_PROVIDER_INTENT_ACTION);
         List<ResolveInfo> queryResults =
@@ -188,26 +227,17 @@
 
     /**
      * Gets the list of provided partner summary actions. Will return an empty list if none are
-     * found
-     * or there is an error loading them.
+     * found or there is an error loading them.
      */
     public List<SummaryAction> getPartnerSummaryActions() {
-        ResolveInfo resolveInfo = getSummaryContentProviderResolveInfo(context.getPackageManager());
-
-        if (resolveInfo == null) {
-            Log.v(TAG, "Could not find partner content provider.");
-            return new ArrayList<>();
-        }
-
-        Uri contentProviderUri = getSummaryContentProviderUri(resolveInfo);
-
-        if (contentProviderUri == null) {
-            Log.e(TAG, "Could not fetch content provider URI, ignoring partner summary items");
+        if (mContentProviderUri == null) {
+            Log.e(TAG, "No content provider URI found, summary actions ignored");
             return new ArrayList<>();
         }
         ArrayList<String> partnerSummaryActions;
         try {
-            partnerSummaryActions = getPartnerSummaryActionsFromContentProvider(contentProviderUri);
+            partnerSummaryActions = getPartnerSummaryActionsFromContentProvider(
+                    mContentProviderUri);
         } catch (NullPointerException | IllegalArgumentException e) {
             Log.e(TAG, "Unable to find or successfully query content provider, ignoring action", e);
             return new ArrayList<>();
@@ -223,9 +253,9 @@
             Log.v(TAG, "Attempting to generate summary action for id: " + actionId);
             try {
                 boolean completed =
-                        getActionCompletionStateFromContentProvider(actionId, contentProviderUri);
+                        getActionCompletionStateFromContentProvider(actionId, mContentProviderUri);
                 Bundle summaryActionBundle =
-                        getActionSummaryStateFromContentProvider(actionId, contentProviderUri);
+                        getActionSummaryStateFromContentProvider(actionId, mContentProviderUri);
                 SummaryAction summaryAction = buildSummaryAction(completed, summaryActionBundle);
                 if (summaryAction != null) {
                     summaryActionList.add(summaryAction);
@@ -236,12 +266,60 @@
                         "Unable to load the completion or config state for summary action: "
                                 + actionId,
                         e);
-                continue;
             }
         }
         return summaryActionList;
     }
 
+    /** Returns the set of partner deferred actions provided by the partner content provider. */
+    public List<DeferredAction> getPartnerDeferredActions() {
+        if (mContentProviderUri == null) {
+            Log.e(TAG, "No content provider URI found, deferred actions ignored");
+            return new ArrayList<>();
+        }
+
+        ArrayList<String> partnerSummaryActions;
+        try {
+            partnerSummaryActions = getPartnerSummaryActionsFromContentProvider(
+                    mContentProviderUri);
+        } catch (NullPointerException | IllegalArgumentException e) {
+            Log.e(TAG, "Unable to find or successfully query content provider, ignoring action", e);
+            return new ArrayList<>();
+        }
+
+        if (partnerSummaryActions == null || partnerSummaryActions.isEmpty()) {
+            Log.v(TAG, "No actions were fetched for partners");
+            return new ArrayList<>();
+        }
+
+        List<DeferredAction> deferredActions = new ArrayList<>();
+        for (String actionId : partnerSummaryActions) {
+            Log.v(TAG, "Attempting to generate deferred action for id: " + actionId);
+            try {
+                boolean completed =
+                        getActionCompletionStateFromContentProvider(actionId, mContentProviderUri);
+                Bundle deferredActionBundle =
+                        getDeferredActionStateFromContentProvider(actionId, mContentProviderUri);
+                if (deferredActionBundle == null) {
+                    Log.v(TAG, "No valid deferredActionBundle for action: " + actionId);
+                    continue;
+                }
+                DeferredAction deferredAction = buildDeferredAction(completed,
+                        deferredActionBundle);
+                if (deferredAction != null) {
+                    deferredActions.add(deferredAction);
+                }
+            } catch (NullPointerException e) {
+                Log.e(
+                        TAG,
+                        "Unable to load the completion or config state for deferred action: "
+                                + actionId,
+                        e);
+            }
+        }
+        return deferredActions;
+    }
+
     /**
      * Gets the list of actionId's for the partner summary actions form the passed in content
      * provider
@@ -253,10 +331,10 @@
      */
     private ArrayList<String> getPartnerSummaryActionsFromContentProvider(Uri contentProviderUri) {
         Bundle result = context.getContentResolver().call(
-                        contentProviderUri,
-                        METHOD_GET_SUMMARY_ACTIONS,
-                        /* arg= */ null,
-                        /* extras= */ null);
+                contentProviderUri,
+                METHOD_GET_SUMMARY_ACTIONS,
+                /* arg= */ null,
+                /* extras= */ null);
         if (result == null || result.getStringArrayList(EXTRA_SUMMARY_ACTIONS_LIST) == null) {
             Log.e(
                     TAG,
@@ -281,10 +359,10 @@
         Bundle completionStateArgs = new Bundle();
         completionStateArgs.putString(EXTRA_ACTION_ID, actionId);
         Bundle result = context.getContentResolver().call(
-                        contentProviderUri,
-                        METHOD_GET_ACTION_COMPLETION_STATE,
-                        /* arg= */ null,
-                        completionStateArgs);
+                contentProviderUri,
+                METHOD_GET_ACTION_COMPLETION_STATE,
+                /* arg= */ null,
+                completionStateArgs);
         if (result == null || !result.containsKey(EXTRA_IS_ACTION_COMPLETED)) {
             throw new IllegalArgumentException(
                     "No action with id " + actionId + " found in content provider");
@@ -297,14 +375,26 @@
         Bundle summaryStateArgs = new Bundle();
         summaryStateArgs.putString(EXTRA_ACTION_ID, actionId);
         Bundle result = context.getContentResolver().call(
-                        contentProviderUri,
-                        METHOD_GET_ACTION_SUMMARY_STATE,
-                        /* arg= */ null,
-                        summaryStateArgs);
+                contentProviderUri,
+                METHOD_GET_ACTION_SUMMARY_STATE,
+                /* arg= */ null,
+                summaryStateArgs);
         if (result == null) {
             throw new IllegalArgumentException(
                     "No action summary found in content provider for " + actionId);
         }
         return result;
     }
+
+    private Bundle getDeferredActionStateFromContentProvider(String actionId,
+            Uri contentProviderUri) {
+        Bundle deferredStateArgs = new Bundle();
+        deferredStateArgs.putString(EXTRA_ACTION_ID, actionId);
+        Bundle result = context.getContentResolver().call(
+                contentProviderUri,
+                METHOD_GET_DEFERRED_ACTION_STATE,
+                /* arg= */ null,
+                deferredStateArgs);
+        return result;
+    }
 }
diff --git a/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java b/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java
index 3a84651..aac4ce7 100644
--- a/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java
+++ b/library/main/src/com/android/car/setupwizardlib/summary/SummaryAction.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package com.android.car.setupwizardlib.summary;
 
 import android.annotation.NonNull;