OSDN Git Service

Require screen to be unlocked for smart replies
authorMilo Sredkov <miloslav@google.com>
Wed, 4 Apr 2018 15:13:28 +0000 (16:13 +0100)
committerMilo Sredkov <miloslav@google.com>
Thu, 5 Apr 2018 12:41:48 +0000 (13:41 +0100)
Currently tapping on a smart reply works even when the screen is
locked. To make it consistent with inline replies, require the screen
to be unlocked before sending the reply.

Bug: 76217177
Test: atest SmartReplyViewTest
Change-Id: I3af8a0411537503650fb9f83cd99f65900e700e0

packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java [new file with mode: 0644]
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java

index 039e7b5..0ba26e9 100644 (file)
@@ -44,6 +44,7 @@ import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -142,5 +143,6 @@ public class SystemUIFactory {
         providers.put(NotificationViewHierarchyManager.class,
                 () -> new NotificationViewHierarchyManager(context));
         providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
+        providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
new file mode 100644 (file)
index 0000000..759a0d1
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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.phone;
+
+import android.annotation.Nullable;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+
+
+/** Executes actions that require the screen to be unlocked. */
+public interface KeyguardDismissHandler {
+    /** Executes an action that requres the screen to be unlocked. */
+    void dismissKeyguardThenExecute(
+            OnDismissAction action, @Nullable Runnable cancelAction, boolean afterKeyguardGone);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
new file mode 100644 (file)
index 0000000..c38b0b6
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 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.phone;
+
+import android.util.Log;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+
+/**
+ * Executes actions that require the screen to be unlocked. Delegates the actual handling to an
+ * implementation passed via {@link #setDismissHandler}.
+ */
+public class KeyguardDismissUtil implements KeyguardDismissHandler {
+    private static final String TAG = "KeyguardDismissUtil";
+
+    private volatile KeyguardDismissHandler mDismissHandler;
+
+    /** Sets the actual {@link DismissHandler} implementation. */
+    public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
+        mDismissHandler = dismissHandler;
+    }
+
+    /**
+     * Executes an action that requres the screen to be unlocked.
+     *
+     * <p>Must be called after {@link #setDismissHandler}.
+     */
+    @Override
+    public void dismissKeyguardThenExecute(
+            OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) {
+        KeyguardDismissHandler dismissHandler = mDismissHandler;
+        if (dismissHandler == null) {
+            Log.wtf(TAG, "KeyguardDismissHandler not set.");
+            action.onDismiss();
+            return;
+        }
+        dismissHandler.dismissKeyguardThenExecute(action, cancelAction, afterKeyguardGone);
+    }
+}
index 340b462..7987bfd 100644 (file)
@@ -213,6 +213,7 @@ import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -1300,6 +1301,8 @@ public class StatusBar extends SystemUI implements DemoMode,
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
         mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+        Dependency.get(KeyguardDismissUtil.class).setDismissHandler(
+                this::dismissKeyguardThenExecute);
         Trace.endSection();
     }
 
index 790135f..74b3926 100644 (file)
@@ -20,8 +20,10 @@ import android.view.ViewGroup;
 import android.widget.Button;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
 import java.util.Comparator;
@@ -42,6 +44,7 @@ public class SmartReplyView extends ViewGroup {
     private static final int SQUEEZE_FAILED = -1;
 
     private final SmartReplyConstants mConstants;
+    private final KeyguardDismissUtil mKeyguardDismissUtil;
 
     /** Spacing to be applied between views. */
     private final int mSpacing;
@@ -62,6 +65,7 @@ public class SmartReplyView extends ViewGroup {
     public SmartReplyView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mConstants = Dependency.get(SmartReplyConstants.class);
+        mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class);
 
         int spacing = 0;
         int singleLineButtonPaddingHorizontal = 0;
@@ -126,12 +130,13 @@ public class SmartReplyView extends ViewGroup {
     }
 
     @VisibleForTesting
-    static Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
+    Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
             RemoteInput remoteInput, PendingIntent pendingIntent) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
         b.setText(choice);
-        b.setOnClickListener(view -> {
+
+        OnDismissAction action = () -> {
             Bundle results = new Bundle();
             results.putString(remoteInput.getResultKey(), choice.toString());
             Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -142,6 +147,12 @@ public class SmartReplyView extends ViewGroup {
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Unable to send smart reply", e);
             }
+            return false; // do not defer
+        };
+
+        b.setOnClickListener(view -> {
+            mKeyguardDismissUtil.dismissKeyguardThenExecute(
+                    action, null /* cancelAction */, false /* afterKeyguardGone */);
         });
         return b;
     }
index a9b2c96..56882c6 100644 (file)
@@ -18,6 +18,7 @@ import static android.view.View.MeasureSpec;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
@@ -34,8 +35,12 @@ import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
 
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.junit.After;
 import org.junit.Before;
@@ -65,6 +70,8 @@ public class SmartReplyViewTest extends SysuiTestCase {
     public void setUp() {
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
+        mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
+            (action, cancelAction, afterKeyguardGone) -> action.onDismiss());
 
         mView = SmartReplyView.inflate(mContext, null);
 
@@ -95,6 +102,42 @@ public class SmartReplyViewTest extends SysuiTestCase {
     }
 
     @Test
+    public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
+        mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
+            (action, cancelAction, afterKeyguardGone) -> {
+                if (cancelAction != null) {
+                    cancelAction.run();
+                }
+            });
+        setRepliesFromRemoteInput(TEST_CHOICES);
+
+        mView.getChildAt(2).performClick();
+
+        assertNull(mReceiver.waitForIntent());
+    }
+
+    @Test
+    public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
+        AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
+        mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
+            (action, cancelAction, afterKeyguardGone) -> actionRef.set(action));
+        setRepliesFromRemoteInput(TEST_CHOICES);
+
+        mView.getChildAt(2).performClick();
+
+        // No intent until the screen is unlocked.
+        assertNull(mReceiver.waitForIntent());
+
+        actionRef.get().onDismiss();
+
+        // Now the intent should arrive.
+        Intent resultIntent = mReceiver.waitForIntent();
+        assertEquals(TEST_CHOICES[2],
+                RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
+        assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
+    }
+
+    @Test
     public void testMeasure_empty() {
         mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
         assertEquals(500, mView.getMeasuredWidthAndState());
@@ -301,7 +344,7 @@ public class SmartReplyViewTest extends SysuiTestCase {
 
         Button previous = null;
         for (CharSequence choice : choices) {
-            Button current = SmartReplyView.inflateReplyButton(mContext, mView, choice, null, null);
+            Button current = mView.inflateReplyButton(mContext, mView, choice, null, null);
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {