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;
providers.put(NotificationViewHierarchyManager.class,
() -> new NotificationViewHierarchyManager(context));
providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
+ providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
}
}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+ Dependency.get(KeyguardDismissUtil.class).setDismissHandler(
+ this::dismissKeyguardThenExecute);
Trace.endSection();
}
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;
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;
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;
}
@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);
} 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;
}
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;
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;
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);
}
@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());
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) {