OSDN Git Service

Moved notification builder creation to the background
authorSelim Cinek <cinek@google.com>
Thu, 20 Apr 2017 22:16:10 +0000 (15:16 -0700)
committerSelim Cinek <cinek@google.com>
Thu, 27 Apr 2017 19:36:32 +0000 (12:36 -0700)
In preparation of future colorization work, the creation
of the notification builder is moved to the background.

Test: manual, add notifications, update them etc.
Test: runtest systemui
Bug: 36561228
Change-Id: Iaec5febf4d8d9da348d77e0d4f6f61b9746fae16

packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
packages/SystemUI/src/com/android/systemui/util/Assert.java
packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java [new file with mode: 0644]
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationinflaterTest.java [deleted file]
packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java

index 9829920..5171cb1 100644 (file)
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.statusbar.notification.NotificationInflater.InflationExceptionHandler;
+import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -48,6 +48,7 @@ import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.NotificationColorUtil;
@@ -62,7 +63,6 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.statusbar.NotificationGuts.GutsContent;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
-import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationInflater;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -315,14 +315,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
         }
     }
 
-    public void updateNotification(NotificationData.Entry entry) throws InflationException {
+    public void updateNotification(NotificationData.Entry entry) {
         mEntry = entry;
         mStatusBarNotification = entry.notification;
         mNotificationInflater.inflateNotificationViews();
-        onNotificationUpdated();
     }
 
-    private void onNotificationUpdated() {
+    public void onNotificationUpdated() {
         for (NotificationContentView l : mLayouts) {
             l.onNotificationUpdated(mEntry);
         }
@@ -483,9 +482,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
         boolean childInGroup = StatusBar.ENABLE_CHILD_NOTIFICATIONS && isChildInGroup;
         mNotificationParent = childInGroup ? parent : null;
         mPrivateLayout.setIsChildInGroup(childInGroup);
-        if (mNotificationInflater.setIsChildInGroup(childInGroup)) {
-            onNotificationUpdated();
-        }
+        mNotificationInflater.setIsChildInGroup(childInGroup);
         resetBackgroundAlpha();
         updateBackgroundForGroupState();
         updateClickAndFocus();
@@ -1114,14 +1111,19 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
         mNotificationInflater.setRemoteViewClickHandler(remoteViewClickHandler);
     }
 
-    public void setInflateExceptionHandler(InflationExceptionHandler inflateExceptionHandler) {
-        mNotificationInflater.setInflateExceptionHandler(inflateExceptionHandler);
+    public void setInflationCallback(InflationCallback callback) {
+        mNotificationInflater.setInflationCallback(callback);
     }
 
     public void setNeedsRedaction(boolean needsRedaction) {
         mNotificationInflater.setRedactAmbient(needsRedaction);
     }
 
+    @VisibleForTesting
+    public NotificationInflater getNotificationInflater() {
+        return mNotificationInflater;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
index 90e908b..b5f7ee6 100644 (file)
@@ -24,6 +24,7 @@ import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.Context;
 import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.notification.NotificationListenerService;
@@ -32,6 +33,7 @@ import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -41,6 +43,7 @@ import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationInflater;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -83,6 +86,7 @@ public class NotificationData {
         public List<SnoozeCriterion> snoozeCriteria;
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
+        private ArraySet<AsyncTask> mRunningTasks = new ArraySet();
 
         public Entry(StatusBarNotification n) {
             this.key = n.getKey();
@@ -210,6 +214,19 @@ public class NotificationData {
             mCachedContrastColor = contrasted;
             return mCachedContrastColor;
         }
+
+        /**
+         * Abort all existing inflation tasks
+         */
+        public void abortInflation() {
+            for (AsyncTask task : mRunningTasks) {
+                task.cancel(true /* mayInterruptIfRunning */);
+            }
+        }
+
+        public void addInflationTask(AsyncTask asyncInflationTask) {
+            mRunningTasks.add(asyncInflationTask);
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -302,12 +319,12 @@ public class NotificationData {
         return mEntries.get(key);
     }
 
-    public void add(Entry entry, RankingMap ranking) {
+    public void add(Entry entry) {
         synchronized (mEntries) {
             mEntries.put(entry.notification.getKey(), entry);
         }
         mGroupManager.onEntryAdded(entry);
-        updateRankingAndSort(ranking);
+        filterAndSort();
     }
 
     public Entry remove(String key, RankingMap ranking) {
index 2e34f24..3bad5cc 100644 (file)
@@ -18,11 +18,10 @@ package com.android.systemui.statusbar.notification;
 
 import android.app.Notification;
 import android.content.Context;
+import android.os.AsyncTask;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.widget.RemoteViews;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -39,7 +38,8 @@ public class NotificationInflater {
     @VisibleForTesting
     static final int FLAG_REINFLATE_ALL = ~0;
     private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
-    private static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
+    @VisibleForTesting
+    static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
     private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
     private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
     private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
@@ -50,7 +50,7 @@ public class NotificationInflater {
     private boolean mUsesIncreasedHeadsUpHeight;
     private RemoteViews.OnClickHandler mRemoteViewClickHandler;
     private boolean mIsChildInGroup;
-    private InflationExceptionHandler mInflateExceptionHandler;
+    private InflationCallback mCallback;
     private boolean mRedactAmbient;
 
     public NotificationInflater(ExpandableNotificationRow row) {
@@ -66,21 +66,14 @@ public class NotificationInflater {
      *
      * @return whether the view was re-inflated
      */
-    public boolean setIsChildInGroup(boolean childInGroup) {
+    public void setIsChildInGroup(boolean childInGroup) {
         if (childInGroup != mIsChildInGroup) {
             mIsChildInGroup = childInGroup;
             if (mIsLowPriority) {
-                try {
-                    int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
-                    inflateNotificationViews(flags);
-                } catch (InflationException e) {
-                    mInflateExceptionHandler.handleInflationException(
-                            mRow.getStatusBarNotification(), e);
-                }
+                int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
+                inflateNotificationViews(flags);
             }
-            return true;
-        }
-        return false;
+        } ;
     }
 
     public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
@@ -101,39 +94,29 @@ public class NotificationInflater {
             if (mRow.getEntry() == null) {
                 return;
             }
-            try {
-                inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
-            } catch (InflationException e) {
-                mInflateExceptionHandler.handleInflationException(
-                        mRow.getStatusBarNotification(), e);
-            }
+            inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
         }
     }
 
-    public void inflateNotificationViews() throws InflationException {
+    /**
+     * Inflate all views of this notification on a background thread. This is asynchronous and will
+     * notify the callback once it's finished.
+     */
+    public void inflateNotificationViews() {
         inflateNotificationViews(FLAG_REINFLATE_ALL);
     }
 
     /**
-     * reinflate all views for the specified flags
+     * Reinflate all views for the specified flags on a background thread. This is asynchronous and
+     * will notify the callback once it's finished.
+     *
      * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
      *                       to reinflate all of views.
-     * @throws InflationException
      */
-    private void inflateNotificationViews(int reInflateFlags)
-            throws InflationException {
+    @VisibleForTesting
+    void inflateNotificationViews(int reInflateFlags) {
         StatusBarNotification sbn = mRow.getEntry().notification;
-        try {
-            final Notification.Builder recoveredBuilder
-                    = Notification.Builder.recoverBuilder(mRow.getContext(), sbn.getNotification());
-            Context packageContext = sbn.getPackageContext(mRow.getContext());
-            inflateNotificationViews(reInflateFlags, recoveredBuilder, packageContext);
-
-        } catch (RuntimeException e) {
-            final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
-            Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
-            throw new InflationException("Couldn't inflate contentViews");
-        }
+        new AsyncInflationTask(mRow.getContext(), sbn, reInflateFlags).execute();
     }
 
     @VisibleForTesting
@@ -284,12 +267,13 @@ public class NotificationInflater {
                         && a.getLayoutId() == b.getLayoutId());
     }
 
-    public void setInflateExceptionHandler(InflationExceptionHandler inflateExceptionHandler) {
-        mInflateExceptionHandler = inflateExceptionHandler;
+    public void setInflationCallback(InflationCallback callback) {
+        mCallback = callback;
     }
 
-    public interface InflationExceptionHandler {
+    public interface InflationCallback {
         void handleInflationException(StatusBarNotification notification, InflationException e);
+        void onAsyncInflationFinished(NotificationData.Entry entry);
     }
 
     public void onDensityOrFontScaleChanged() {
@@ -299,12 +283,67 @@ public class NotificationInflater {
         entry.cachedContentView = null;
         entry.cachedHeadsUpContentView = null;
         entry.cachedPublicContentView = null;
+        inflateNotificationViews();
+    }
+
+    private class AsyncInflationTask extends AsyncTask<Void, Void, Notification.Builder> {
+
+        private final StatusBarNotification mSbn;
+        private final Context mContext;
+        private final int mReInflateFlags;
+        private Context mPackageContext = null;
+        private Exception mError;
+
+        private AsyncInflationTask(Context context, StatusBarNotification notification,
+                int reInflateFlags) {
+            mSbn = notification;
+            mContext = context;
+            mReInflateFlags = reInflateFlags;
+            mRow.getEntry().addInflationTask(this);
+        }
+
+        @Override
+        protected Notification.Builder doInBackground(Void... params) {
+            try {
+                final Notification.Builder recoveredBuilder
+                        = Notification.Builder.recoverBuilder(mContext,
+                        mSbn.getNotification());
+                mPackageContext = mSbn.getPackageContext(mContext);
+                return recoveredBuilder;
+            } catch (Exception e) {
+                mError = e;
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Notification.Builder builder) {
+            if (mError == null) {
+                finishInflation(mReInflateFlags, builder, mPackageContext);
+            } else {
+                handleError(mError);
+            }
+        }
+    }
+
+    private void finishInflation(int reinflationFlags, Notification.Builder builder,
+            Context context) {
         try {
-            inflateNotificationViews();
-        } catch (InflationException e) {
-            mInflateExceptionHandler.handleInflationException(
-                    mRow.getStatusBarNotification(), e);
+            inflateNotificationViews(reinflationFlags, builder, context);
+        } catch (RuntimeException e){
+            handleError(e);
+            return;
         }
+        mRow.onNotificationUpdated();
+        mCallback.onAsyncInflationFinished(mRow.getEntry());
     }
 
+    private void handleError(Exception e) {
+        StatusBarNotification sbn = mRow.getStatusBarNotification();
+        final String ident = sbn.getPackageName() + "/0x"
+                + Integer.toHexString(sbn.getId());
+        Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
+        mCallback.handleInflationException(sbn,
+                new InflationException("Couldn't inflate contentViews" + e));
+    }
 }
index 608d03c..7b7fa29 100644 (file)
@@ -22,7 +22,7 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 
-import static com.android.systemui.statusbar.notification.NotificationInflater.InflationExceptionHandler;
+import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
@@ -257,7 +257,7 @@ public class StatusBar extends SystemUI implements DemoMode,
         OnHeadsUpChangedListener, VisualStabilityManager.Callback, CommandQueue.Callbacks,
         ActivatableNotificationView.OnActivatedListener,
         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
-        ExpandableNotificationRow.OnExpandClickListener {
+        ExpandableNotificationRow.OnExpandClickListener, InflationCallback {
     public static final boolean MULTIUSER_DEBUG = false;
 
     public static final boolean ENABLE_REMOTE_INPUT =
@@ -713,8 +713,8 @@ public class StatusBar extends SystemUI implements DemoMode,
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private NotificationIconAreaController mNotificationIconAreaController;
     private ConfigurationListener mConfigurationListener;
-    private InflationExceptionHandler mInflationExceptionHandler = this::handleInflationException;
     private boolean mReinflateNotificationsOnUserSwitched;
+    private HashMap<String, Entry> mPendingNotifications = new HashMap<>();
     private boolean mClearAllEnabled;
 
     private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
@@ -1551,29 +1551,24 @@ public class StatusBar extends SystemUI implements DemoMode,
         return new UserHandle(mCurrentUserId);
     }
 
-    public void addNotification(StatusBarNotification notification, RankingMap ranking,
-            Entry oldEntry) throws InflationException {
-        if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
+    public void addNotification(StatusBarNotification notification, RankingMap ranking)
+            throws InflationException {
+        String key = notification.getKey();
+        if (DEBUG) Log.d(TAG, "addNotification key=" + key);
 
         mNotificationData.updateRanking(ranking);
         Entry shadeEntry = createNotificationViews(notification);
         boolean isHeadsUped = shouldPeek(shadeEntry);
-        if (isHeadsUped) {
-            mHeadsUpManager.showNotification(shadeEntry);
-            // Mark as seen immediately
-            setNotificationShown(notification);
-        }
-
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            if (shouldSuppressFullScreenIntent(notification.getKey())) {
+            if (shouldSuppressFullScreenIntent(key)) {
                 if (DEBUG) {
-                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
+                    Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
                 }
-            } else if (mNotificationData.getImportance(notification.getKey())
+            } else if (mNotificationData.getImportance(key)
                     < NotificationManager.IMPORTANCE_HIGH) {
                 if (DEBUG) {
                     Log.d(TAG, "No Fullscreen intent: not important enough: "
-                            + notification.getKey());
+                            + key);
                 }
             } else {
                 // Stop screensaver if the notification has a full-screen intent.
@@ -1585,7 +1580,7 @@ public class StatusBar extends SystemUI implements DemoMode,
                     Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
                 try {
                     EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
-                            notification.getKey());
+                            key);
                     notification.getNotification().fullScreenIntent.send();
                     shadeEntry.notifyFullScreenIntentLaunched();
                     mMetricsLogger.count("note_fullscreen", 1);
@@ -1593,15 +1588,47 @@ public class StatusBar extends SystemUI implements DemoMode,
                 }
             }
         }
-        addNotificationViews(shadeEntry, ranking);
+        abortExistingInflation(key);
+        mPendingNotifications.put(key, shadeEntry);
+    }
+
+    private void abortExistingInflation(String key) {
+        if (mPendingNotifications.containsKey(key)) {
+            Entry entry = mPendingNotifications.get(key);
+            entry.abortInflation();
+            mPendingNotifications.remove(key);
+        }
+        Entry addedEntry = mNotificationData.get(key);
+        if (addedEntry != null) {
+            addedEntry.abortInflation();
+        }
+    }
+
+    private void addEntry(Entry shadeEntry) {
+        boolean isHeadsUped = shouldPeek(shadeEntry);
+        if (isHeadsUped) {
+            mHeadsUpManager.showNotification(shadeEntry);
+            // Mark as seen immediately
+            setNotificationShown(shadeEntry.notification);
+        }
+        addNotificationViews(shadeEntry);
         // Recalculate the position of the sliding windows and the titles.
         setAreThereNotifications();
     }
 
+    @Override
     public void handleInflationException(StatusBarNotification notification, InflationException e) {
         handleNotificationError(notification, e.getMessage());
     }
 
+    @Override
+    public void onAsyncInflationFinished(Entry entry) {
+        mPendingNotifications.remove(entry.key);
+        if (mNotificationData.get(entry.key) == null) {
+            addEntry(entry);
+        }
+    }
+
     private boolean shouldSuppressFullScreenIntent(String key) {
         if (isDeviceInVrMode()) {
             return true;
@@ -1621,6 +1648,7 @@ public class StatusBar extends SystemUI implements DemoMode,
 
     public void removeNotification(String key, RankingMap ranking) {
         boolean deferRemoval = false;
+        abortExistingInflation(key);
         if (mHeadsUpManager.isHeadsUp(key)) {
             // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
             // sending look longer than it takes.
@@ -3308,6 +3336,14 @@ public class StatusBar extends SystemUI implements DemoMode,
                     + " scroll " + mStackScroller.getScrollX()
                     + "," + mStackScroller.getScrollY());
         }
+        pw.print("  mPendingNotifications=");
+        if (mPendingNotifications.size() == 0) {
+            pw.println("null");
+        } else {
+            for (Entry entry : mPendingNotifications.values()) {
+                pw.println(entry.notification);
+            }
+        }
 
         pw.print("  mInteractingWindows="); pw.println(mInteractingWindows);
         pw.print("  mStatusBarWindowState=");
@@ -5548,7 +5584,7 @@ public class StatusBar extends SystemUI implements DemoMode,
                 public void run() {
                     for (StatusBarNotification sbn : notifications) {
                         try {
-                            addNotification(sbn, currentRanking, null /* oldEntry */);
+                            addNotification(sbn, currentRanking);
                         } catch (InflationException e) {
                             handleInflationException(sbn, e);
                         }
@@ -5591,7 +5627,7 @@ public class StatusBar extends SystemUI implements DemoMode,
                             if (isUpdate) {
                                 updateNotification(sbn, rankingMap);
                             } else {
-                                addNotification(sbn, rankingMap, null /* oldEntry */);
+                                addNotification(sbn, rankingMap);
                             }
                         } catch (InflationException e) {
                             handleInflationException(sbn, e);
@@ -6153,8 +6189,7 @@ public class StatusBar extends SystemUI implements DemoMode,
         }
     }
 
-    protected void inflateViews(Entry entry, ViewGroup parent) throws
-            InflationException {
+    protected void inflateViews(Entry entry, ViewGroup parent) {
         PackageManager pmUser = getPackageManagerForUser(mContext,
                 entry.notification.getUser().getIdentifier());
 
@@ -6175,7 +6210,7 @@ public class StatusBar extends SystemUI implements DemoMode,
             row.setRemoteInputController(mRemoteInputController);
             row.setOnExpandClickListener(this);
             row.setRemoteViewClickHandler(mOnClickHandler);
-            row.setInflateExceptionHandler(mInflationExceptionHandler);
+            row.setInflationCallback(this);
 
             // Get the app name.
             // Note that Notification.Builder#bindHeaderAppName has similar logic
@@ -6573,12 +6608,12 @@ public class StatusBar extends SystemUI implements DemoMode,
         return entry;
     }
 
-    protected void addNotificationViews(Entry entry, RankingMap ranking) {
+    protected void addNotificationViews(Entry entry) {
         if (entry == null) {
             return;
         }
         // Add the expanded view and icon.
-        mNotificationData.add(entry, ranking);
+        mNotificationData.add(entry);
         updateNotifications();
     }
 
@@ -6710,6 +6745,7 @@ public class StatusBar extends SystemUI implements DemoMode,
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
+        abortExistingInflation(key);
         Entry entry = mNotificationData.get(key);
         if (entry == null) {
             return;
index af447f3..0f7c9a4 100644 (file)
@@ -28,4 +28,10 @@ public class Assert {
             throw new IllegalStateException("should be called from the main thread.");
         }
     }
+
+    public static void isNotMainThread() {
+        if (Looper.getMainLooper().isCurrentThread()) {
+            throw new IllegalStateException("should not be called from the main thread.");
+        }
+    }
 }
index 1b5d4a4..aa84098 100644 (file)
@@ -43,12 +43,13 @@ public class ExpandHelperTest extends SysuiTestCase {
     private ExpandHelper.Callback mCallback;
 
     @Before
-    @UiThreadTest
-    public void setUp() {
+    public void setUp() throws Exception {
         Context context = getContext();
         mRow = new NotificationTestHelper(context).createRow();
         mCallback = mock(ExpandHelper.Callback.class);
-        mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(
+                () -> mExpandHelper = new ExpandHelper(context, mCallback, 10, 100));
+
     }
 
     @Test
index 3db2440..5cd092b 100644 (file)
@@ -39,8 +39,7 @@ public class ExpandableNotificationRowTest {
     private NotificationTestHelper mNotificationTestHelper;
 
     @Before
-    @UiThreadTest
-    public void setUp() {
+    public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
         mNotificationTestHelper = new NotificationTestHelper(mContext);
         mGroup = mNotificationTestHelper.createGroup();
index c91b269..cb238dd 100644 (file)
 package com.android.systemui.statusbar;
 
 import android.app.ActivityManager;
+import android.app.Instrumentation;
 import android.app.Notification;
 import android.content.Context;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.NotificationInflaterTest;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 
 /**
@@ -34,14 +37,18 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager;
 public class NotificationTestHelper {
 
     private final Context mContext;
+    private final Instrumentation mInstrumentation;
     private int mId;
     private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
+    private ExpandableNotificationRow mRow;
+    private InflationException mException;
 
     public NotificationTestHelper(Context context) {
         mContext = context;
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
     }
 
-    public ExpandableNotificationRow createRow() {
+    public ExpandableNotificationRow createRow() throws Exception {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
@@ -56,12 +63,15 @@ public class NotificationTestHelper {
         return createRow(notification);
     }
 
-    public ExpandableNotificationRow createRow(Notification notification) {
+    public ExpandableNotificationRow createRow(Notification notification) throws Exception {
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
-        ExpandableNotificationRow row = (ExpandableNotificationRow) inflater.inflate(
-                R.layout.status_bar_notification_row,
-                null, false);
+        mInstrumentation.runOnMainSync(() -> {
+            mRow = (ExpandableNotificationRow) inflater.inflate(
+                    R.layout.status_bar_notification_row,
+                    null, false);
+        });
+        ExpandableNotificationRow row = mRow;
         row.setGroupManager(mGroupManager);
         UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
         StatusBarNotification sbn = new StatusBarNotification("com.android.systemui",
@@ -69,16 +79,13 @@ public class NotificationTestHelper {
                 2000, notification, mUser, null, System.currentTimeMillis());
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         entry.row = row;
-        try {
-            entry.createIcons(mContext, sbn);
-            row.updateNotification(entry);
-        } catch (InflationException e) {
-            throw new RuntimeException(e.getMessage());
-        }
+        entry.createIcons(mContext, sbn);
+        NotificationInflaterTest.runThenWaitForInflation(() -> row.updateNotification(entry),
+                row.getNotificationInflater());
         return row;
     }
 
-    public ExpandableNotificationRow createGroup() {
+    public ExpandableNotificationRow createGroup() throws Exception {
         ExpandableNotificationRow row = createRow();
         row.addChildNotification(createRow());
         row.addChildNotification(createRow());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationInflaterTest.java
new file mode 100644 (file)
index 0000000..65e0568
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 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.systemui.statusbar.notification;
+
+import static com.android.systemui.statusbar.notification.NotificationInflater.FLAG_REINFLATE_ALL;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Notification;
+import android.content.Context;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.RemoteViews;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.NotificationTestHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Function;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationInflaterTest {
+
+    private Context mContext;
+    private NotificationInflater mNotificationInflater;
+    private Notification.Builder mBuilder;
+    private ExpandableNotificationRow mRow;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mBuilder = new Notification.Builder(mContext).setSmallIcon(
+                R.drawable.ic_person)
+                .setContentTitle("Title")
+                .setContentText("Text")
+                .setStyle(new Notification.BigTextStyle().bigText("big text"));
+        ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
+                mBuilder.build());
+        mRow = spy(row);
+        mNotificationInflater = new NotificationInflater(mRow);
+        mNotificationInflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+            @Override
+            public void handleInflationException(StatusBarNotification notification,
+                    InflationException e) {
+            }
+
+            @Override
+            public void onAsyncInflationFinished(NotificationData.Entry entry) {
+            }
+        });
+    }
+
+    @Test
+    public void testIncreasedHeadsUpBeingUsed() {
+        mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
+        Notification.Builder builder = spy(mBuilder);
+        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        verify(builder).createHeadsUpContentView(true);
+    }
+
+    @Test
+    public void testIncreasedHeightBeingUsed() {
+        mNotificationInflater.setUsesIncreasedHeight(true);
+        Notification.Builder builder = spy(mBuilder);
+        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        verify(builder).createContentView(true);
+    }
+
+    @Test
+    public void testInflationCallsUpdated() throws Exception {
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+                mNotificationInflater);
+        verify(mRow).onNotificationUpdated();
+    }
+
+    @Test
+    public void testInflationCallsOnlyRightMethod() throws Exception {
+        mRow.getPrivateLayout().removeAllViews();
+        mRow.getEntry().cachedBigContentView = null;
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
+                NotificationInflater.FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
+        Assert.assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
+        Assert.assertTrue(mRow.getPrivateLayout().getChildAt(0)
+                == mRow.getPrivateLayout().getExpandedChild());
+        verify(mRow).onNotificationUpdated();
+    }
+
+    @Test
+    public void testInflationThrowsErrorDoesntCallUpdated() throws Exception {
+        mRow.getPrivateLayout().removeAllViews();
+        mRow.getStatusBarNotification().getNotification().contentView
+                = new RemoteViews(mContext.getPackageName(), R.layout.status_bar);;
+        runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+                true /* expectingException */, mNotificationInflater);
+        Assert.assertTrue(mRow.getPrivateLayout().getChildCount() == 0);
+        verify(mRow, times(0)).onNotificationUpdated();
+    }
+
+    public static void runThenWaitForInflation(Runnable block,
+            NotificationInflater inflater) throws Exception {
+        runThenWaitForInflation(block, false /* expectingException */, inflater);
+    }
+
+    private static void runThenWaitForInflation(Runnable block, boolean expectingException,
+            NotificationInflater inflater) throws Exception {
+        com.android.systemui.util.Assert.isNotMainThread();
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        final ExceptionHolder exceptionHolder = new ExceptionHolder();
+        inflater.setInflationCallback(new NotificationInflater.InflationCallback() {
+            @Override
+            public void handleInflationException(StatusBarNotification notification,
+                    InflationException e) {
+                if (!expectingException) {
+                    exceptionHolder.setException(e);
+                }
+                countDownLatch.countDown();
+            }
+
+            @Override
+            public void onAsyncInflationFinished(NotificationData.Entry entry) {
+                if (expectingException) {
+                    exceptionHolder.setException(new RuntimeException(
+                            "Inflation finished even though there should be an error"));
+                }
+                countDownLatch.countDown();
+            }
+        });
+        block.run();
+        countDownLatch.await(5, java.util.concurrent.TimeUnit.SECONDS);
+        if (exceptionHolder.mException != null) {
+            throw exceptionHolder.mException;
+        }
+    }
+
+    private static class ExceptionHolder {
+        private Exception mException;
+
+        public void setException(Exception exception) {
+            mException = exception;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationinflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationinflaterTest.java
deleted file mode 100644 (file)
index 0ec9c10..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2017 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.systemui.statusbar.notification;
-
-import static com.android.systemui.statusbar.notification.NotificationInflater.FLAG_REINFLATE_ALL;
-
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-import android.app.Notification;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationTestHelper;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationinflaterTest {
-
-    private Context mContext;
-    private NotificationInflater mNotificationInflater;
-    private Notification.Builder mBuilder;
-
-    @Before
-    @UiThreadTest
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mBuilder = new Notification.Builder(mContext).setSmallIcon(
-                R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text");
-        ExpandableNotificationRow row = new NotificationTestHelper(mContext).createRow(
-                mBuilder.build());
-        mNotificationInflater = new NotificationInflater(row);
-    }
-
-    @Test
-    public void testIncreasedHeadsUpBeingUsed() {
-        mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
-        Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
-        verify(builder).createHeadsUpContentView(true);
-    }
-
-    @Test
-    public void testIncreasedHeightBeingUsed() {
-        mNotificationInflater.setUsesIncreasedHeight(true);
-        Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
-        verify(builder).createContentView(true);
-    }
-}
index dbe0de4..f051f30 100644 (file)
@@ -42,8 +42,7 @@ public class NotificationChildrenContainerTest {
     private NotificationTestHelper mNotificationTestHelper;
 
     @Before
-    @UiThreadTest
-    public void setUp() {
+    public void setUp() throws Exception {
         mContext = InstrumentationRegistry.getTargetContext();
         mNotificationTestHelper = new NotificationTestHelper(mContext);
         mGroup = mNotificationTestHelper.createGroup();