blob: b4299fd760fd50b7fc5e679f1108697fab2ff406 [file] [log] [blame]
/*
* 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.recents;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
import android.annotation.FloatRange;
import android.app.ActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.systemui.Dumpable;
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.NavigationBarController;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarView;
import com.android.systemui.statusbar.phone.NavigationModeController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
/**
* Class to send information from overview to launcher with a binder.
*/
@Singleton
public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
NavigationModeController.ModeChangedListener, Dumpable {
private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
public static final String TAG_OPS = "OverviewProxyService";
private static final long BACKOFF_MILLIS = 1000;
private static final long DEFERRED_CALLBACK_MILLIS = 5000;
// Max backoff caps at 5 mins
private static final long MAX_BACKOFF_MILLIS = 10 * 60 * 1000;
private final Context mContext;
private final Handler mHandler;
private final NavigationBarController mNavBarController;
private final StatusBarWindowController mStatusBarWinController;
private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
private final ComponentName mRecentsComponentName;
private final DeviceProvisionedController mDeviceProvisionedController;
private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
private final Intent mQuickStepIntent;
private Region mActiveNavBarRegion;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
private @SystemUiStateFlags int mSysUiStateFlags;
private boolean mBound;
private boolean mIsEnabled;
private int mCurrentBoundedUserId = -1;
private float mBackButtonAlpha;
private MotionEvent mStatusBarGestureDownEvent;
private float mWindowCornerRadius;
private boolean mSupportsRoundedCornersOnWindows;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@Override
public void startScreenPinning(int taskId) {
if (!verifyCaller("startScreenPinning")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
StatusBar statusBar = SysUiServiceProvider.getComponent(mContext,
StatusBar.class);
if (statusBar != null) {
statusBar.showScreenPinningRequest(taskId, false /* allowCancel */);
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void stopScreenPinning() {
if (!verifyCaller("stopScreenPinning")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
try {
ActivityTaskManager.getService().stopSystemLockTaskMode();
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to stop screen pinning");
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
if (!verifyCaller("onStatusBarMotionEvent")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
// TODO move this logic to message queue
mHandler.post(()->{
StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
if (bar != null) {
bar.dispatchNotificationsPanelTouchEvent(event);
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
mStatusBarGestureDownEvent = MotionEvent.obtain(event);
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mStatusBarGestureDownEvent.recycle();
mStatusBarGestureDownEvent = null;
}
event.recycle();
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onSplitScreenInvoked() {
if (!verifyCaller("onSplitScreenInvoked")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
if (divider != null) {
divider.onDockedFirstAnimationFrame();
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onOverviewShown(boolean fromHome) {
if (!verifyCaller("onOverviewShown")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onOverviewShown(fromHome);
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
return null;
}
long token = Binder.clearCallingIdentity();
try {
Divider divider = SysUiServiceProvider.getComponent(mContext, Divider.class);
if (divider != null) {
return divider.getView().getNonMinimizedSplitScreenSecondaryBounds();
}
return null;
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void setBackButtonAlpha(float alpha, boolean animate) {
if (!verifyCaller("setBackButtonAlpha")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mBackButtonAlpha = alpha;
mHandler.post(() -> {
notifyBackButtonAlphaChanged(alpha, animate);
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
if (!verifyCaller("onAssistantProgress")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> notifyAssistantProgress(progress));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void onAssistantGestureCompletion(float velocity) {
if (!verifyCaller("onAssistantGestureCompletion")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void startAssistant(Bundle bundle) {
if (!verifyCaller("startAssistant")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
mHandler.post(() -> notifyStartAssistant(bundle));
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public Bundle monitorGestureInput(String name, int displayId) {
if (!verifyCaller("monitorGestureInput")) {
return null;
}
long token = Binder.clearCallingIdentity();
try {
InputMonitor monitor =
InputManager.getInstance().monitorGestureInput(name, displayId);
Bundle result = new Bundle();
result.putParcelable(KEY_EXTRA_INPUT_MONITOR, monitor);
return result;
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void notifyAccessibilityButtonClicked(int displayId) {
if (!verifyCaller("notifyAccessibilityButtonClicked")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
AccessibilityManager.getInstance(mContext)
.notifyAccessibilityButtonClicked(displayId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public void notifyAccessibilityButtonLongClicked() {
if (!verifyCaller("notifyAccessibilityButtonLongClicked")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
} finally {
Binder.restoreCallingIdentity(token);
}
}
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
Log.w(TAG_OPS, "Launcher called sysui with invalid user: " + callerId + ", reason: "
+ reason);
return false;
}
return true;
}
};
private final Runnable mDeferredConnectionCallback = () -> {
Log.w(TAG_OPS, "Binder supposed established connection but actual connection to service "
+ "timed out, trying again");
retryConnectionWithBackoff();
};
private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateEnabledState();
// Reconnect immediately, instead of waiting for resume to arrive.
startConnectionToCurrentUser();
}
};
private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mConnectionBackoffAttempts = 0;
mHandler.removeCallbacks(mDeferredConnectionCallback);
try {
service.linkToDeath(mOverviewServiceDeathRcpt, 0);
} catch (RemoteException e) {
// Failed to link to death (process may have died between binding and connecting),
// just unbind the service for now and retry again
Log.e(TAG_OPS, "Lost connection to launcher service", e);
disconnectFromLauncherService();
retryConnectionWithBackoff();
return;
}
mCurrentBoundedUserId = mDeviceProvisionedController.getCurrentUser();
mOverviewProxy = IOverviewProxy.Stub.asInterface(service);
Bundle params = new Bundle();
params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());
params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius);
params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows);
try {
mOverviewProxy.onInitialize(params);
} catch (RemoteException e) {
mCurrentBoundedUserId = -1;
Log.e(TAG_OPS, "Failed to call onInitialize()", e);
}
dispatchNavButtonBounds();
// Update the systemui state flags
updateSystemUiStateFlags();
notifyConnectionChanged();
}
@Override
public void onNullBinding(ComponentName name) {
Log.w(TAG_OPS, "Null binding of '" + name + "', try reconnecting");
mCurrentBoundedUserId = -1;
retryConnectionWithBackoff();
}
@Override
public void onBindingDied(ComponentName name) {
Log.w(TAG_OPS, "Binding died of '" + name + "', try reconnecting");
mCurrentBoundedUserId = -1;
retryConnectionWithBackoff();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// Do nothing
mCurrentBoundedUserId = -1;
}
};
private final DeviceProvisionedListener mDeviceProvisionedCallback =
new DeviceProvisionedListener() {
@Override
public void onUserSetupChanged() {
if (mDeviceProvisionedController.isCurrentUserSetup()) {
internalConnectToCurrentUser();
}
}
@Override
public void onUserSwitched() {
mConnectionBackoffAttempts = 0;
internalConnectToCurrentUser();
}
};
private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
// This is the death handler for the binder from the launcher service
private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
= this::cleanupAfterDeath;
@Inject
public OverviewProxyService(Context context, DeviceProvisionedController provisionController,
NavigationBarController navBarController, NavigationModeController navModeController,
StatusBarWindowController statusBarWinController) {
mContext = context;
mHandler = new Handler();
mNavBarController = navBarController;
mStatusBarWinController = statusBarWinController;
mDeviceProvisionedController = provisionController;
mConnectionBackoffAttempts = 0;
mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
com.android.internal.R.string.config_recentsComponentName));
mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils
.supportsRoundedCornersOnWindows(mContext.getResources());
// Assumes device always starts with back button until launcher tells it that it does not
mBackButtonAlpha = 1.0f;
// Listen for nav bar mode changes
mNavBarMode = navModeController.addListener(this);
// Listen for device provisioned/user setup
updateEnabledState();
mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
// Listen for launcher package changes
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addDataScheme("package");
filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
PatternMatcher.PATTERN_LITERAL);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
mContext.registerReceiver(mLauncherStateChangedReceiver, filter);
// Listen for status bar state changes
statusBarWinController.registerCallback(mStatusBarWindowCallback);
}
public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
boolean gestureSwipeLeft) {
try {
if (mOverviewProxy != null) {
mOverviewProxy.onBackAction(completed, downX, downY, isButton, gestureSwipeLeft);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to notify back action", e);
}
}
public void setSystemUiStateFlag(int flag, boolean enabled, int displayId) {
if (displayId != DEFAULT_DISPLAY) {
// Ignore non-default displays for now
return;
}
int newState = mSysUiStateFlags;
if (enabled) {
newState |= flag;
} else {
newState &= ~flag;
}
if (mSysUiStateFlags != newState) {
mSysUiStateFlags = newState;
notifySystemUiStateChanged(mSysUiStateFlags);
notifySystemUiStateFlags(mSysUiStateFlags);
}
}
public int getSystemUiStateFlags() {
return mSysUiStateFlags;
}
private void updateSystemUiStateFlags() {
final NavigationBarFragment navBarFragment =
mNavBarController.getDefaultNavigationBarFragment();
final NavigationBarView navBarView =
mNavBarController.getNavigationBarView(mContext.getDisplayId());
mSysUiStateFlags = 0;
if (navBarFragment != null) {
navBarFragment.updateSystemUiStateFlags(-1);
}
if (navBarView != null) {
navBarView.updateSystemUiStateFlags();
}
if (mStatusBarWinController != null) {
mStatusBarWinController.notifyStateChangedCallbacks();
}
notifySystemUiStateFlags(mSysUiStateFlags);
}
private void notifySystemUiStateFlags(int flags) {
try {
if (mOverviewProxy != null) {
mOverviewProxy.onSystemUiStateChanged(flags);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to notify sysui state change", e);
}
}
private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded,
boolean bouncerShowing) {
int displayId = mContext.getDisplayId();
setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING,
keyguardShowing && !keyguardOccluded, displayId);
setSystemUiStateFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED,
keyguardShowing && keyguardOccluded, displayId);
setSystemUiStateFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing, displayId);
}
/**
* Sets the navbar region which can receive touch inputs
*/
public void onActiveNavBarRegionChanges(Region activeRegion) {
mActiveNavBarRegion = activeRegion;
dispatchNavButtonBounds();
}
private void dispatchNavButtonBounds() {
if (mOverviewProxy != null && mActiveNavBarRegion != null) {
try {
mOverviewProxy.onActiveNavBarRegionChanges(mActiveNavBarRegion);
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onActiveNavBarRegionChanges()", e);
}
}
}
public float getBackButtonAlpha() {
return mBackButtonAlpha;
}
public void cleanupAfterDeath() {
if (mStatusBarGestureDownEvent != null) {
mHandler.post(()-> {
StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
if (bar != null) {
mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
mStatusBarGestureDownEvent.recycle();
mStatusBarGestureDownEvent = null;
}
});
}
startConnectionToCurrentUser();
}
public void startConnectionToCurrentUser() {
if (mHandler.getLooper() != Looper.myLooper()) {
mHandler.post(mConnectionRunnable);
} else {
internalConnectToCurrentUser();
}
}
private void internalConnectToCurrentUser() {
disconnectFromLauncherService();
// If user has not setup yet or already connected, do not try to connect
if (!mDeviceProvisionedController.isCurrentUserSetup() || !isEnabled()) {
Log.v(TAG_OPS, "Cannot attempt connection, is setup "
+ mDeviceProvisionedController.isCurrentUserSetup() + ", is enabled "
+ isEnabled());
return;
}
mHandler.removeCallbacks(mConnectionRunnable);
Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
.setPackage(mRecentsComponentName.getPackageName());
try {
mBound = mContext.bindServiceAsUser(launcherServiceIntent,
mOverviewServiceConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
} catch (SecurityException e) {
Log.e(TAG_OPS, "Unable to bind because of security error", e);
}
if (mBound) {
// Ensure that connection has been established even if it thinks it is bound
mHandler.postDelayed(mDeferredConnectionCallback, DEFERRED_CALLBACK_MILLIS);
} else {
// Retry after exponential backoff timeout
retryConnectionWithBackoff();
}
}
private void retryConnectionWithBackoff() {
if (mHandler.hasCallbacks(mConnectionRunnable)) {
return;
}
final long timeoutMs = (long) Math.min(
Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts), MAX_BACKOFF_MILLIS);
mHandler.postDelayed(mConnectionRunnable, timeoutMs);
mConnectionBackoffAttempts++;
Log.w(TAG_OPS, "Failed to connect on attempt " + mConnectionBackoffAttempts
+ " will try again in " + timeoutMs + "ms");
}
@Override
public void addCallback(OverviewProxyListener listener) {
mConnectionCallbacks.add(listener);
listener.onConnectionChanged(mOverviewProxy != null);
listener.onBackButtonAlphaChanged(mBackButtonAlpha, false);
listener.onSystemUiStateChanged(mSysUiStateFlags);
}
@Override
public void removeCallback(OverviewProxyListener listener) {
mConnectionCallbacks.remove(listener);
}
public boolean shouldShowSwipeUpUI() {
return isEnabled() && !QuickStepContract.isLegacyMode(mNavBarMode);
}
public boolean isEnabled() {
return mIsEnabled;
}
public IOverviewProxy getProxy() {
return mOverviewProxy;
}
private void disconnectFromLauncherService() {
if (mBound) {
// Always unbind the service (ie. if called through onNullBinding or onBindingDied)
mContext.unbindService(mOverviewServiceConnection);
mBound = false;
}
if (mOverviewProxy != null) {
mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
mOverviewProxy = null;
notifyBackButtonAlphaChanged(1f, false /* animate */);
notifyConnectionChanged();
}
}
private void notifyBackButtonAlphaChanged(float alpha, boolean animate) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onBackButtonAlphaChanged(alpha, animate);
}
}
private void notifyConnectionChanged() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
}
}
public void notifyQuickStepStarted() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onQuickStepStarted();
}
}
public void notifyQuickScrubStarted() {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onQuickScrubStarted();
}
}
private void notifyAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onAssistantProgress(progress);
}
}
private void notifyAssistantGestureCompletion(float velocity) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onAssistantGestureCompletion(velocity);
}
}
private void notifySystemUiStateChanged(int sysuiStateFlags) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onSystemUiStateChanged(sysuiStateFlags);
}
}
private void notifyStartAssistant(Bundle bundle) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).startAssistant(bundle);
}
}
public void notifyAssistantVisibilityChanged(float visibility) {
try {
if (mOverviewProxy != null) {
mOverviewProxy.onAssistantVisibilityChanged(visibility);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to call onAssistantVisibilityChanged()", e);
}
}
private void updateEnabledState() {
mIsEnabled = mContext.getPackageManager().resolveServiceAsUser(mQuickStepIntent,
MATCH_SYSTEM_ONLY,
ActivityManagerWrapper.getInstance().getCurrentUserId()) != null;
}
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(TAG_OPS + " state:");
pw.print(" recentsComponentName="); pw.println(mRecentsComponentName);
pw.print(" isConnected="); pw.println(mOverviewProxy != null);
pw.print(" isCurrentUserSetup="); pw.println(mDeviceProvisionedController
.isCurrentUserSetup());
pw.print(" connectionBackoffAttempts="); pw.println(mConnectionBackoffAttempts);
pw.print(" quickStepIntent="); pw.println(mQuickStepIntent);
pw.print(" quickStepIntentResolved="); pw.println(isEnabled());
pw.print(" mSysUiStateFlags="); pw.println(mSysUiStateFlags);
pw.println(" " + QuickStepContract.getSystemUiStateString(mSysUiStateFlags));
pw.print(" backGestureDisabled=");
pw.println(QuickStepContract.isBackGestureDisabled(mSysUiStateFlags));
pw.print(" assistantGestureDisabled=");
pw.println(QuickStepContract.isAssistantGestureDisabled(mSysUiStateFlags));
}
public interface OverviewProxyListener {
default void onConnectionChanged(boolean isConnected) {}
default void onQuickStepStarted() {}
default void onOverviewShown(boolean fromHome) {}
default void onQuickScrubStarted() {}
default void onBackButtonAlphaChanged(float alpha, boolean animate) {}
default void onSystemUiStateChanged(int sysuiStateFlags) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
}
}