Adds an Assistant handle controller to the AssistManager

This will control when the Assistant handles show or hide.

Change mode using the following command:

$ adb shell am broadcast \
  -a "com.google.systemui.SET_ASSIST_HANDLE_BEHAVIOR" \
  --es behavior "${BEHAVIOR}"

BEHAVIOR can be one of: OFF, LIKE_HOME, or REMINDER_EXP

Change the minimum time between handle appearance in REMINDER_EXP using
the following command:

$ adb shell setprop ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS \
  ${DESIRED_MILLISECONDS}

Change the duration the handles appear in REMINDER_EXP using the
following command:

$ adb shell setprop ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS \
  ${DESIRED_MILLISECONDS}

BUG:132983599
BUG:131115187
Test: atest AssistHandleBehaviorControllerTest
Test: Tested locally.
Change-Id: I44bb59367159226b67a2aeb49aae2a5b34584d70
This commit is contained in:
Govinda Wasserman
2019-05-20 14:43:28 -04:00
parent 3fce56e79c
commit c7495cd607
10 changed files with 820 additions and 1 deletions

View File

@@ -156,6 +156,11 @@ public class ScreenDecorations extends SystemUI implements Tunable {
* @param visible whether the handles should be shown
*/
public void setAssistHintVisible(boolean visible) {
if (!mHandler.getLooper().isCurrentThread()) {
mHandler.post(() -> setAssistHintVisible(visible));
return;
}
if (mAssistHintVisible != visible) {
mAssistHintVisible = visible;

View File

@@ -0,0 +1,46 @@
/*
* 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.systemui.assist;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
public enum AssistHandleBehavior {
TEST(new AssistHandleOffBehavior()),
OFF(new AssistHandleOffBehavior()),
LIKE_HOME(new AssistHandleLikeHomeBehavior()),
REMINDER_EXP(new AssistHandleReminderExpBehavior());
private BehaviorController mController;
AssistHandleBehavior(BehaviorController controller) {
mController = controller;
}
BehaviorController getController() {
return mController;
}
@VisibleForTesting
void setTestController(BehaviorController controller) {
if (this.equals(TEST)) {
mController = controller;
}
}
}

View File

@@ -0,0 +1,204 @@
/*
* 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.systemui.assist;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.NavigationModeController;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* A class for managing Assistant handle logic.
*
* Controls when visual handles for Assistant gesture affordance should be shown or hidden using an
* {@link AssistHandleBehavior}.
*/
public final class AssistHandleBehaviorController implements AssistHandleCallbacks {
private static final String TAG = "AssistHandleBehavior";
private static final boolean IS_DEBUG_DEVICE =
Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
|| Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
private static final String SHOWN_FREQUENCY_THRESHOLD_KEY =
"ASSIST_HANDLES_SHOWN_FREQUENCY_THRESHOLD_MS";
private static final long DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(10);
private static final String SHOW_AND_GO_DURATION_KEY = "ASSIST_HANDLES_SHOW_AND_GO_DURATION_MS";
private static final long DEFAULT_SHOW_AND_GO_DURATION_MS = TimeUnit.SECONDS.toMillis(3);
private static final String BEHAVIOR_KEY = "behavior";
private static final String SET_BEHAVIOR_ACTION =
"com.android.systemui.SET_ASSIST_HANDLE_BEHAVIOR";
private final Context mContext;
private final Handler mHandler;
private final Runnable mHideHandles = this::hideHandles;
private final Supplier<ScreenDecorations> mScreenDecorationsSupplier;
private boolean mHandlesShowing = false;
private long mHandlesLastHiddenAt;
private AssistHandleBehavior mCurrentBehavior = AssistHandleBehavior.OFF;
private boolean mInGesturalMode;
AssistHandleBehaviorController(Context context, Handler handler) {
this(context, handler, () ->
SysUiServiceProvider.getComponent(context, ScreenDecorations.class));
}
@VisibleForTesting
AssistHandleBehaviorController(
Context context,
Handler handler,
Supplier<ScreenDecorations> screenDecorationsSupplier) {
mContext = context;
mHandler = handler;
mScreenDecorationsSupplier = screenDecorationsSupplier;
mInGesturalMode = QuickStepContract.isGesturalMode(
Dependency.get(NavigationModeController.class)
.addListener(this::handleNavigationModeChange));
if (IS_DEBUG_DEVICE) {
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String behaviorString = intent.getExtras().getString(BEHAVIOR_KEY);
try {
setBehavior(AssistHandleBehavior.valueOf(behaviorString));
} catch (IllegalArgumentException e) {
Log.e(TAG, "Invalid behavior identifier: " + behaviorString);
}
}
}, new IntentFilter(SET_BEHAVIOR_ACTION));
}
}
@Override
public void hide() {
mHandler.removeCallbacks(mHideHandles);
mHandler.post(mHideHandles);
}
@Override
public void showAndGo() {
mHandler.removeCallbacks(mHideHandles);
mHandler.post(() -> {
maybeShowHandles(/* ignoreThreshold = */ false);
mHandler.postDelayed(mHideHandles, getShowAndGoDuration());
});
}
@Override
public void showAndStay() {
mHandler.removeCallbacks(mHideHandles);
mHandler.post(() -> maybeShowHandles(/* ignoreThreshold = */ true));
}
void setBehavior(AssistHandleBehavior behavior) {
if (mCurrentBehavior == behavior) {
return;
}
if (mInGesturalMode) {
mCurrentBehavior.getController().onModeDeactivated();
behavior.getController().onModeActivated(mContext, this);
}
mCurrentBehavior = behavior;
}
private static long getShownFrequencyThreshold() {
return SystemProperties.getLong(
SHOWN_FREQUENCY_THRESHOLD_KEY, DEFAULT_SHOWN_FREQUENCY_THRESHOLD_MS);
}
private static long getShowAndGoDuration() {
return SystemProperties.getLong(SHOW_AND_GO_DURATION_KEY, DEFAULT_SHOW_AND_GO_DURATION_MS);
}
private void maybeShowHandles(boolean ignoreThreshold) {
if (mHandlesShowing) {
return;
}
long timeSinceHidden = SystemClock.elapsedRealtime() - mHandlesLastHiddenAt;
if (ignoreThreshold || timeSinceHidden > getShownFrequencyThreshold()) {
mHandlesShowing = true;
ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
if (screenDecorations == null) {
Log.w(TAG, "Couldn't show handles, ScreenDecorations unavailable");
} else {
screenDecorations.setAssistHintVisible(true);
}
}
}
private void hideHandles() {
if (!mHandlesShowing) {
return;
}
mHandlesShowing = false;
mHandlesLastHiddenAt = SystemClock.elapsedRealtime();
ScreenDecorations screenDecorations = mScreenDecorationsSupplier.get();
if (screenDecorations == null) {
Log.w(TAG, "Couldn't hide handles, ScreenDecorations unavailable");
} else {
screenDecorations.setAssistHintVisible(false);
}
}
private void handleNavigationModeChange(int navigationMode) {
boolean inGesturalMode = QuickStepContract.isGesturalMode(navigationMode);
if (mInGesturalMode == inGesturalMode) {
return;
}
mInGesturalMode = inGesturalMode;
if (mInGesturalMode) {
mCurrentBehavior.getController().onModeActivated(mContext, this);
} else {
mCurrentBehavior.getController().onModeDeactivated();
hide();
}
}
@VisibleForTesting
void setInGesturalModeForTest(boolean inGesturalMode) {
mInGesturalMode = inGesturalMode;
}
interface BehaviorController {
void onModeActivated(Context context, AssistHandleCallbacks callbacks);
void onModeDeactivated();
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.systemui.assist;
/** Callback for controlling Assistant handle behavior. */
public interface AssistHandleCallbacks {
/** Hide the Assistant handles. */
void hide();
/**
* Show the Assistant handles for the configured duration and then hide them.
*
* Won't show if the handles have been shown within the configured timeout.
*/
void showAndGo();
/** Show the Assistant handles. */
void showAndStay();
}

View File

@@ -0,0 +1,92 @@
/*
* 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.systemui.assist;
import android.app.StatusBarManager;
import android.content.Context;
import androidx.annotation.Nullable;
import com.android.systemui.Dependency;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
/**
* Assistant Handle behavior that makes Assistant handles show/hide when the home handle is
* shown/hidden, respectively.
*/
final class AssistHandleLikeHomeBehavior implements BehaviorController {
private final CommandQueue.Callbacks mCallbacks = new CommandQueue.Callbacks() {
@Override
public void setWindowState(int displayId, int window, int state) {
if (mNavBarDisplayId == displayId
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR) {
handleWindowStateChanged(state);
}
}
};
private CommandQueue mCommandQueue;
private int mNavBarDisplayId;
private boolean mIsNavBarWindowVisible;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
NavigationBarFragment navigationBarFragment =
Dependency.get(NavigationBarController.class).getDefaultNavigationBarFragment();
mNavBarDisplayId = navigationBarFragment.mDisplayId;
mIsNavBarWindowVisible = navigationBarFragment.isNavBarWindowVisible();
mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
mCommandQueue.addCallback(mCallbacks);
callbackForCurrentState();
}
@Override
public void onModeDeactivated() {
mAssistHandleCallbacks = null;
mCommandQueue.removeCallback(mCallbacks);
}
private void handleWindowStateChanged(int state) {
boolean newVisibility = state == StatusBarManager.WINDOW_STATE_SHOWING;
if (mIsNavBarWindowVisible == newVisibility) {
return;
}
mIsNavBarWindowVisible = newVisibility;
callbackForCurrentState();
}
private void callbackForCurrentState() {
if (mAssistHandleCallbacks == null) {
return;
}
if (mIsNavBarWindowVisible) {
mAssistHandleCallbacks.showAndStay();
} else {
mAssistHandleCallbacks.hide();
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.systemui.assist;
import android.content.Context;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
/** Assistant handle behavior that hides the Assistant handles. */
final class AssistHandleOffBehavior implements BehaviorController {
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
callbacks.hide();
}
@Override
public void onModeDeactivated() {
// Do nothing
}
}

View File

@@ -0,0 +1,193 @@
/*
* 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.systemui.assist;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.android.systemui.Dependency;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistHandleBehaviorController.BehaviorController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
/**
* Assistant handle behavior that hides the handles when the phone is dozing or in immersive mode,
* shows the handles when on lockscreen, and shows the handles temporarily when changing tasks or
* entering overview.
*/
final class AssistHandleReminderExpBehavior implements BehaviorController {
private final StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
public void onStateChanged(int newState) {
handleStatusBarStateChanged(newState);
}
@Override
public void onDozingChanged(boolean isDozing) {
handleDozingChanged(isDozing);
}
};
private final TaskStackChangeListener mTaskStackChangeListener =
new TaskStackChangeListener() {
@Override
public void onTaskMovedToFront(int taskId) {
handleTaskStackTopChanged(taskId);
}
@Override
public void onTaskCreated(int taskId, ComponentName componentName) {
handleTaskStackTopChanged(taskId);
}
};
private final CommandQueue.Callbacks mCallbacks = new CommandQueue.Callbacks() {
@Override
public void setSystemUiVisibility(int displayId, int vis,
int fullscreenStackVis, int dockedStackVis, int mask,
Rect fullscreenStackBounds, Rect dockedStackBounds,
boolean navbarColorManagedByIme) {
if (mStatusBarDisplayId == displayId) {
handleSystemUiVisibilityChange(vis, mask);
}
}
};
private final OverviewProxyService.OverviewProxyListener mOverviewProxyListener =
new OverviewProxyService.OverviewProxyListener() {
@Override
public void onOverviewShown(boolean fromHome) {
handleOverviewShown();
}
};
private StatusBarStateController mStatusBarStateController;
private ActivityManagerWrapper mActivityManagerWrapper;
private OverviewProxyService mOverviewProxyService;
private int mStatusBarDisplayId;
private CommandQueue mCommandQueue;
private boolean mOnLockscreen;
private boolean mIsDozing;
private int mRunningTaskId;
private boolean mIsImmersive;
@Nullable private AssistHandleCallbacks mAssistHandleCallbacks;
@Override
public void onModeActivated(Context context, AssistHandleCallbacks callbacks) {
mAssistHandleCallbacks = callbacks;
mStatusBarStateController = Dependency.get(StatusBarStateController.class);
mOnLockscreen = onLockscreen(mStatusBarStateController.getState());
mIsDozing = mStatusBarStateController.isDozing();
mStatusBarStateController.addCallback(mStatusBarStateListener);
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
mRunningTaskId = mActivityManagerWrapper.getRunningTask().taskId;
mActivityManagerWrapper.registerTaskStackListener(mTaskStackChangeListener);
mStatusBarDisplayId =
((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay().getDisplayId();
mCommandQueue = SysUiServiceProvider.getComponent(context, CommandQueue.class);
mCommandQueue.addCallback(mCallbacks);
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
mOverviewProxyService.addCallback(mOverviewProxyListener);
callbackForCurrentState();
}
@Override
public void onModeDeactivated() {
mAssistHandleCallbacks = null;
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackChangeListener);
mCommandQueue.removeCallback(mCallbacks);
mOverviewProxyService.removeCallback(mOverviewProxyListener);
}
private void handleStatusBarStateChanged(int newState) {
boolean onLockscreen = onLockscreen(newState);
if (mOnLockscreen == onLockscreen) {
return;
}
mOnLockscreen = onLockscreen;
callbackForCurrentState();
}
private void handleDozingChanged(boolean isDozing) {
if (mIsDozing == isDozing) {
return;
}
mIsDozing = isDozing;
callbackForCurrentState();
}
private void handleTaskStackTopChanged(int taskId) {
if (mRunningTaskId == taskId) {
return;
}
mRunningTaskId = taskId;
callbackForCurrentState();
}
private void handleSystemUiVisibilityChange(int vis, int mask) {
boolean isImmersive = isImmersive(vis, mask);
if (mIsImmersive == isImmersive) {
return;
}
mIsImmersive = isImmersive;
callbackForCurrentState();
}
private void handleOverviewShown() {
callbackForCurrentState();
}
private boolean onLockscreen(int statusBarState) {
return statusBarState == StatusBarState.KEYGUARD
|| statusBarState == StatusBarState.SHADE_LOCKED;
}
private boolean isImmersive(int vis, int mask) {
return ((vis & mask)
& (View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)) != 0;
}
private void callbackForCurrentState() {
if (mAssistHandleCallbacks == null) {
return;
}
if (mIsDozing || mIsImmersive) {
mAssistHandleCallbacks.hide();
} else if (mOnLockscreen) {
mAssistHandleCallbacks.showAndStay();
} else {
mAssistHandleCallbacks.showAndGo();
}
}
}

View File

@@ -75,6 +75,7 @@ public class AssistManager implements ConfigurationChangedReceiver {
private final AssistDisclosure mAssistDisclosure;
private final InterestingConfigChanges mInterestingConfigChanges;
private final PhoneStateMonitor mPhoneStateMonitor;
private final AssistHandleBehaviorController mHandleController;
private AssistOrbContainer mView;
private final DeviceProvisionedController mDeviceProvisionedController;
@@ -110,6 +111,7 @@ public class AssistManager implements ConfigurationChangedReceiver {
mAssistUtils = new AssistUtils(context);
mAssistDisclosure = new AssistDisclosure(context, new Handler());
mPhoneStateMonitor = new PhoneStateMonitor(context);
mHandleController = new AssistHandleBehaviorController(context, new Handler());
registerVoiceInteractionSessionListener();
mInterestingConfigChanges = new InterestingConfigChanges(ActivityInfo.CONFIG_ORIENTATION
@@ -352,6 +354,10 @@ public class AssistManager implements ConfigurationChangedReceiver {
v.setImageDrawable(null);
}
protected AssistHandleBehaviorController getHandleBehaviorController() {
return mHandleController;
}
@Nullable
public ComponentName getAssistInfoForUser(int userId) {
return mAssistUtils.getAssistComponentForUser(userId);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* 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.

View File

@@ -0,0 +1,204 @@
/*
* 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.systemui.assist;
import static org.mockito.AdditionalAnswers.answerVoid;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.ScreenDecorations;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class AssistHandleBehaviorControllerTest extends SysuiTestCase {
private final AssistHandleBehavior mTestBehavior = AssistHandleBehavior.TEST;
private AssistHandleBehaviorController mAssistHandleBehaviorController;
@Mock private ScreenDecorations mMockScreenDecorations;
@Mock private Handler mMockHandler;
@Mock private AssistHandleBehaviorController.BehaviorController mMockBehaviorController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
doAnswer(answerVoid(Runnable::run)).when(mMockHandler).post(any(Runnable.class));
doAnswer(answerVoid(Runnable::run)).when(mMockHandler)
.postDelayed(any(Runnable.class), anyLong());
mTestBehavior.setTestController(mMockBehaviorController);
mAssistHandleBehaviorController =
new AssistHandleBehaviorController(
mContext, mMockHandler, () -> mMockScreenDecorations);
}
@Test
public void hide_hidesHandlesWhenShowing() {
// Arrange
mAssistHandleBehaviorController.showAndStay();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.hide();
// Assert
verify(mMockScreenDecorations).setAssistHintVisible(false);
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void hide_doesNothingWhenHiding() {
// Arrange
mAssistHandleBehaviorController.hide();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.hide();
// Assert
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void showAndStay_showsHandlesWhenHiding() {
// Arrange
mAssistHandleBehaviorController.hide();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndStay();
// Assert
verify(mMockScreenDecorations).setAssistHintVisible(true);
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void showAndStay_doesNothingWhenShowing() {
// Arrange
mAssistHandleBehaviorController.showAndStay();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndStay();
// Assert
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void showAndGo_showsThenHidesHandlesWhenHiding() {
// Arrange
mAssistHandleBehaviorController.hide();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
InOrder inOrder = inOrder(mMockScreenDecorations);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(true);
inOrder.verify(mMockScreenDecorations).setAssistHintVisible(false);
inOrder.verifyNoMoreInteractions();
}
@Test
public void showAndGo_hidesHandlesAfterTimeoutWhenShowing() {
// Arrange
mAssistHandleBehaviorController.showAndStay();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
verify(mMockScreenDecorations).setAssistHintVisible(false);
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void showAndGo_doesNothingIfRecentlyHidden() {
// Arrange
mAssistHandleBehaviorController.showAndGo();
reset(mMockScreenDecorations);
// Act
mAssistHandleBehaviorController.showAndGo();
// Assert
verifyNoMoreInteractions(mMockScreenDecorations);
}
@Test
public void setBehavior_activatesTheBehaviorWhenInGesturalMode() {
// Arrange
mAssistHandleBehaviorController.setInGesturalModeForTest(true);
// Act
mAssistHandleBehaviorController.setBehavior(mTestBehavior);
// Assert
verify(mMockBehaviorController).onModeActivated(mContext, mAssistHandleBehaviorController);
verifyNoMoreInteractions(mMockBehaviorController);
}
@Test
public void setBehavior_deactivatesThePreviousBehaviorWhenInGesturalMode() {
// Arrange
mAssistHandleBehaviorController.setBehavior(mTestBehavior);
mAssistHandleBehaviorController.setInGesturalModeForTest(true);
// Act
mAssistHandleBehaviorController.setBehavior(AssistHandleBehavior.OFF);
// Assert
verify(mMockBehaviorController).onModeDeactivated();
verifyNoMoreInteractions(mMockBehaviorController);
}
@Test
public void setBehavior_doesNothingWhenNotInGesturalMode() {
// Arrange
mAssistHandleBehaviorController.setInGesturalModeForTest(false);
// Act
mAssistHandleBehaviorController.setBehavior(mTestBehavior);
// Assert
verifyNoMoreInteractions(mMockBehaviorController);
}
}