diff options
9 files changed, 308 insertions, 57 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8a2a14c1f8a1..391ee835e92a 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -5605,10 +5605,10 @@ public final class Settings { "accessibility_web_content_key_bindings"; /** - * Setting that specifies whether the display magnification is enabled. - * Display magnifications allows the user to zoom in the display content - * and is targeted to low vision users. The current magnification scale - * is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. + * Setting that specifies whether the display magnification is enabled via a system-wide + * triple tap gesture. Display magnifications allows the user to zoom in the display content + * and is targeted to low vision users. The current magnification scale is controlled by + * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. * * @hide */ @@ -5616,11 +5616,23 @@ public final class Settings { "accessibility_display_magnification_enabled"; /** + * Setting that specifies whether the display magnification is enabled via a shortcut + * affordance within the system's navigation area. Display magnifications allows the user to + * zoom in the display content and is targeted to low vision users. The current + * magnification scale is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}. + * + * @hide + */ + public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED = + "accessibility_display_magnification_navbar_enabled"; + + /** * Setting that specifies what the display magnification scale is. * Display magnifications allows the user to zoom in the display * content and is targeted to low vision users. Whether a display * magnification is performed is controlled by - * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED} + * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED} and + * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED} * * @hide */ @@ -6950,6 +6962,7 @@ public final class Settings { ACCESSIBILITY_DISPLAY_DALTONIZER, ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, + ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, ACCESSIBILITY_SCRIPT_INJECTION, ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS, diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index a541a4cd3b1e..6dbc09ceef97 100644 --- a/core/java/android/view/WindowManagerInternal.java +++ b/core/java/android/view/WindowManagerInternal.java @@ -168,6 +168,14 @@ public abstract class WindowManagerInternal { public abstract void setMagnificationSpec(MagnificationSpec spec); /** + * Set by the accessibility framework to indicate whether the magnifiable regions of the display + * should be shown. + * + * @param show {@code true} to show magnifiable region bounds, {@code false} to hide + */ + public abstract void setForceShowMagnifiableBounds(boolean show); + + /** * Obtains the magnification regions. * * @param magnificationRegion the current magnification region diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index 8a3c4e3b7d76..2b52b48a6819 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -35,6 +35,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; +import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; import android.inputmethodservice.InputMethodService; @@ -46,6 +47,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; +import android.provider.Settings; import android.support.annotation.VisibleForTesting; import android.telecom.TelecomManager; import android.text.TextUtils; @@ -104,6 +106,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { private int mNavigationIconHints = 0; private int mNavigationBarMode; private AccessibilityManager mAccessibilityManager; + private MagnificationContentObserver mMagnificationObserver; private int mDisabledFlags1; private StatusBar mStatusBar; @@ -135,6 +138,12 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class); mAccessibilityManager.addAccessibilityServicesStateChangeListener( this::updateAccessibilityServicesState); + mMagnificationObserver = new MagnificationContentObserver( + getContext().getMainThreadHandler()); + getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false, + mMagnificationObserver); + if (savedInstanceState != null) { mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0); } @@ -154,6 +163,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mCommandQueue.removeCallbacks(this); mAccessibilityManager.removeAccessibilityServicesStateChangeListener( this::updateAccessibilityServicesState); + getContext().getContentResolver().unregisterContentObserver(mMagnificationObserver); try { WindowManagerGlobal.getWindowManagerService() .removeRotationWatcher(mRotationWatcher); @@ -387,6 +397,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton(); accessibilityButton.setOnClickListener(this::onAccessibilityClick); accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick); + updateAccessibilityServicesState(); } private boolean onHomeTouch(View v, MotionEvent event) { @@ -550,10 +561,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks { } private void updateAccessibilityServicesState() { + int requestingServices = 0; + try { + if (Settings.Secure.getInt(getContext().getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED) == 1) { + requestingServices++; + } + } catch (Settings.SettingNotFoundException e) { + } + final List<AccessibilityServiceInfo> services = mAccessibilityManager.getEnabledAccessibilityServiceList( AccessibilityServiceInfo.FEEDBACK_ALL_MASK); - int requestingServices = 0; for (int i = services.size() - 1; i >= 0; --i) { AccessibilityServiceInfo info = services.get(i); if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) { @@ -600,6 +619,18 @@ public class NavigationBarFragment extends Fragment implements Callbacks { mNavigationBarView.getBarTransitions().finishAnimations(); } + private class MagnificationContentObserver extends ContentObserver { + + public MagnificationContentObserver(Handler handler) { + super(handler); + } + + @Override + public void onChange(boolean selfChange) { + NavigationBarFragment.this.updateAccessibilityServicesState(); + } + } + private final Stub mRotationWatcher = new Stub() { @Override public void onRotationChanged(int rotation) throws RemoteException { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index fd93865ea97e..9e4d89cbc9c5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -77,9 +77,6 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010; - static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS - | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION - | FLAG_FEATURE_SCREEN_MAGNIFIER; /** * Flag for enabling the feature to control the screen magnifier. If * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored @@ -90,6 +87,16 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo */ static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020; + /** + * Flag for enabling the feature to trigger the screen magnifier + * from another on-device interaction. + */ + static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040; + + static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS + | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION + | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; + private final Runnable mProcessBatchedEventsRunnable = new Runnable() { @Override public void run() { @@ -379,6 +386,12 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } } + void notifyAccessibilityButtonClicked() { + if (mMagnificationGestureHandler != null) { + mMagnificationGestureHandler.notifyShortcutTriggered(); + } + } + private void enableFeatures() { resetStreamState(); @@ -393,11 +406,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0 - || (mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) { + || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) + || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) { final boolean detectControlGestures = (mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0; + final boolean triggerable = (mEnabledFeatures + & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0; mMagnificationGestureHandler = new MagnificationGestureHandler( - mContext, mAms, detectControlGestures); + mContext, mAms, detectControlGestures, triggerable); addFirstEventHandler(mMagnificationGestureHandler); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 98ce00e05914..397938ac3160 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -781,6 +781,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsTouchExplorationEnabled = false; userState.mIsEnhancedWebAccessibilityEnabled = false; userState.mIsDisplayMagnificationEnabled = false; + userState.mIsNavBarMagnificationEnabled = false; userState.mIsAutoclickEnabled = false; userState.mEnabledServices.clear(); } @@ -831,6 +832,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { userState.mIsTouchExplorationEnabled = touchExplorationEnabled; userState.mIsEnhancedWebAccessibilityEnabled = false; userState.mIsDisplayMagnificationEnabled = false; + userState.mIsNavBarMagnificationEnabled = false; userState.mIsAutoclickEnabled = false; userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); @@ -1152,11 +1154,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private void notifyAccessibilityButtonClickedLocked() { final UserState state = getCurrentUserStateLocked(); - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final Service service = state.mBoundServices.get(i); - // TODO(b/34720082): Only notify a single user-defined service - if (service.mRequestAccessibilityButton) { - service.notifyAccessibilityButtonClickedLocked(); + if (state.mIsNavBarMagnificationEnabled) { + mMainHandler.obtainMessage( + MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget(); + } else { + for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { + final Service service = state.mBoundServices.get(i); + // TODO(b/34720082): Only notify a single user-defined service + if (service.mRequestAccessibilityButton) { + service.notifyAccessibilityButtonClickedLocked(); + } } } } @@ -1548,6 +1555,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (userState.mIsDisplayMagnificationEnabled) { flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER; } + if (userState.mIsNavBarMagnificationEnabled) { + flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; + } if (userHasMagnificationServicesLocked(userState)) { flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER; } @@ -1781,7 +1791,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { somethingChanged |= readTouchExplorationEnabledSettingLocked(userState); somethingChanged |= readHighTextContrastEnabledSettingLocked(userState); somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState); - somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState); + somethingChanged |= readMagnificationEnabledSettingsLocked(userState); somethingChanged |= readAutoclickEnabledSettingLocked(userState); somethingChanged |= readAccessibilityShortcutSettingLocked(userState); return somethingChanged; @@ -1810,13 +1820,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } - private boolean readDisplayMagnificationEnabledSettingLocked(UserState userState) { + private boolean readMagnificationEnabledSettingsLocked(UserState userState) { final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userState.mUserId) == 1; - if (displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) { + final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, + 0, userState.mUserId) == 1; + if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) + || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) { userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled; + userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled; return true; } return false; @@ -2018,8 +2034,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return; } - if (userState.mIsDisplayMagnificationEnabled || - userHasListeningMagnificationServicesLocked(userState)) { + if (userState.mIsDisplayMagnificationEnabled || userState.mIsNavBarMagnificationEnabled + || userHasListeningMagnificationServicesLocked(userState)) { // Initialize the magnification controller if necessary getMagnificationController(); mMagnificationController.register(); @@ -2241,6 +2257,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled); pw.append(", displayMagnificationEnabled=" + userState.mIsDisplayMagnificationEnabled); + pw.append(", navBarMagnificationEnabled=" + + userState.mIsNavBarMagnificationEnabled); pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled); if (userState.mUiAutomationService != null) { pw.append(", "); @@ -2320,6 +2338,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10; public static final int MSG_UPDATE_FINGERPRINT = 11; public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12; + public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13; public MainHandler(Looper looper) { super(looper); @@ -2406,6 +2425,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } }); } break; + + case MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER: { + synchronized (mLock) { + if (mHasInputFilter && mInputFilter != null) { + mInputFilter.notifyAccessibilityButtonClicked(); + } + } + } } } @@ -4788,6 +4815,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public boolean mIsTextHighContrastEnabled; public boolean mIsEnhancedWebAccessibilityEnabled; public boolean mIsDisplayMagnificationEnabled; + public boolean mIsNavBarMagnificationEnabled; public boolean mIsAutoclickEnabled; public boolean mIsPerformGesturesEnabled; public boolean mIsFilterKeyEventsEnabled; @@ -4856,6 +4884,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mIsTouchExplorationEnabled = false; mIsEnhancedWebAccessibilityEnabled = false; mIsDisplayMagnificationEnabled = false; + mIsNavBarMagnificationEnabled = false; mIsAutoclickEnabled = false; mSoftKeyboardShowMode = 0; @@ -4888,6 +4917,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); + private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); + private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED); @@ -4927,6 +4959,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri, false, this, UserHandle.USER_ALL); + contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri, + false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mAutoclickEnabledUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri, @@ -4966,8 +5000,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { if (readTouchExplorationEnabledSettingLocked(userState)) { onUserStateChangedLocked(userState); } - } else if (mDisplayMagnificationEnabledUri.equals(uri)) { - if (readDisplayMagnificationEnabledSettingLocked(userState)) { + } else if (mDisplayMagnificationEnabledUri.equals(uri) + || mNavBarMagnificationEnabledUri.equals(uri)) { + if (readMagnificationEnabledSettingsLocked(userState)) { onUserStateChangedLocked(userState); } } else if (mAutoclickEnabledUri.equals(uri)) { diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java index f65046ce4350..caa74b9512d1 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java @@ -685,6 +685,12 @@ class MagnificationController implements Handler.Callback { } } + void setForceShowMagnifiableBounds(boolean show) { + if (mRegistered) { + mWindowManager.setForceShowMagnifiableBounds(show); + } + } + private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) { final float scale = getSentScale(); final float offsetX = getSentOffsetX(); diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java index f6e5340ed7ee..7e82edaae3e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java +++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java @@ -16,7 +16,10 @@ package com.android.server.accessibility; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.os.Handler; import android.os.Message; import android.util.MathUtils; @@ -57,22 +60,30 @@ import android.view.accessibility.AccessibilityEvent; * be the same but when the finger goes up the screen will stay magnified. * In other words, the initial magnified state is sticky. * - * 3. Pinching with any number of additional fingers when viewport dragging + * 3. Magnification can optionally be "triggered" by some external shortcut + * affordance. When this occurs via {@link #notifyShortcutTriggered()} a + * subsequent tap in a magnifiable region will engage permanent screen + * magnification as described in #1. Alternatively, a subsequent long-press + * or drag will engage magnification with viewport dragging as described in + * #2. Once magnified, all following behaviors apply whether magnification + * was engaged via a triple-tap or by a triggered shortcut. + * + * 4. Pinching with any number of additional fingers when viewport dragging * is enabled, i.e. the user triple tapped and holds, would adjust the * magnification scale which will become the current default magnification * scale. The next time the user magnifies the same magnification scale * would be used. * - * 4. When in a permanent magnified state the user can use two or more fingers + * 5. When in a permanent magnified state the user can use two or more fingers * to pan the viewport. Note that in this mode the content is panned as * opposed to the viewport dragging mode in which the viewport is moved. * - * 5. When in a permanent magnified state the user can use two or more + * 6. When in a permanent magnified state the user can use two or more * fingers to change the magnification scale which will become the current * default magnification scale. The next time the user magnifies the same * magnification scale would be used. * - * 6. The magnification scale will be persisted in settings and in the cloud. + * 7. The magnification scale will be persisted in settings and in the cloud. */ class MagnificationGestureHandler implements EventStreamTransformation { private static final String LOG_TAG = "MagnificationEventHandler"; @@ -94,8 +105,10 @@ class MagnificationGestureHandler implements EventStreamTransformation { private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler; private final StateViewportDraggingHandler mStateViewportDraggingHandler; + private final ScreenStateReceiver mScreenStateReceiver; - private final boolean mDetectControlGestures; + private final boolean mDetectTripleTap; + private final boolean mTriggerable; private EventStreamTransformation mNext; @@ -104,19 +117,39 @@ class MagnificationGestureHandler implements EventStreamTransformation { private boolean mTranslationEnabledBeforePan; + private boolean mShortcutTriggered; + private PointerCoords[] mTempPointerCoords; private PointerProperties[] mTempPointerProperties; private long mDelegatingStateDownTime; + /** + * @param context Context for resolving various magnification-related resources + * @param ams AccessibilityManagerService used to obtain a {@link MagnificationController} + * @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap + * gestures for engaging and disengaging magnification, + * {@code false} if it should ignore such gestures + * @param triggerable {@code true} if this detector should be "triggerable" by some external + * shortcut invoking {@link #notifyShortcutTriggered}, {@code + * false} if it should ignore such triggers. + */ public MagnificationGestureHandler(Context context, AccessibilityManagerService ams, - boolean detectControlGestures) { + boolean detectTripleTap, boolean triggerable) { mMagnificationController = ams.getMagnificationController(); mDetectingStateHandler = new DetectingStateHandler(context); mStateViewportDraggingHandler = new StateViewportDraggingHandler(); mMagnifiedContentInteractionStateHandler = new MagnifiedContentInteractionStateHandler(context); - mDetectControlGestures = detectControlGestures; + mDetectTripleTap = detectTripleTap; + mTriggerable = triggerable; + + if (triggerable) { + mScreenStateReceiver = new ScreenStateReceiver(context, this); + mScreenStateReceiver.register(); + } else { + mScreenStateReceiver = null; + } transitionToState(STATE_DETECTING); } @@ -129,7 +162,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { } return; } - if (!mDetectControlGestures) { + if (!mDetectTripleTap && !mTriggerable) { if (mNext != null) { dispatchTransformedEvent(event, rawEvent, policyFlags); } @@ -151,7 +184,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { break; case STATE_MAGNIFIED_INTERACTION: { // mMagnifiedContentInteractionStateHandler handles events only - // if this is the current state since it uses ScaleGestureDetecotr + // if this is the current state since it uses ScaleGestureDetector // and a GestureDetector which need well formed event stream. } break; @@ -193,11 +226,34 @@ class MagnificationGestureHandler implements EventStreamTransformation { @Override public void onDestroy() { + if (mScreenStateReceiver != null) { + mScreenStateReceiver.unregister(); + } clear(); } + void notifyShortcutTriggered() { + if (mTriggerable) { + if (mMagnificationController.resetIfNeeded(true)) { + clear(); + } else { + setMagnificationShortcutTriggered(!mShortcutTriggered); + } + } + } + + private void setMagnificationShortcutTriggered(boolean state) { + if (mShortcutTriggered == state) { + return; + } + + mShortcutTriggered = state; + mMagnificationController.setForceShowMagnifiableBounds(state); + } + private void clear() { mCurrentState = STATE_DETECTING; + setMagnificationShortcutTriggered(false); mDetectingStateHandler.clear(); mStateViewportDraggingHandler.clear(); mMagnifiedContentInteractionStateHandler.clear(); @@ -575,31 +631,51 @@ class MagnificationGestureHandler implements EventStreamTransformation { mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(!mShortcutTriggered); return; } - if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null - && GestureUtils.isMultiTap(mLastDownEvent, event, - mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) { + if (mShortcutTriggered) { Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD, policyFlags, 0, event); mHandler.sendMessageDelayed(message, ViewConfiguration.getLongPressTimeout()); - } else if (mTapCount < ACTION_TAP_COUNT) { + return; + } + if (mDetectTripleTap) { + if ((mTapCount == ACTION_TAP_COUNT - 1) && (mLastDownEvent != null) + && GestureUtils.isMultiTap(mLastDownEvent, event, mMultiTapTimeSlop, + mMultiTapDistanceSlop, 0)) { + Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD, + policyFlags, 0, event); + mHandler.sendMessageDelayed(message, + ViewConfiguration.getLongPressTimeout()); + } else if (mTapCount < ACTION_TAP_COUNT) { + Message message = mHandler.obtainMessage( + MESSAGE_TRANSITION_TO_DELEGATING_STATE); + mHandler.sendMessageDelayed(message, mMultiTapTimeSlop); + } + clearLastDownEvent(); + mLastDownEvent = MotionEvent.obtain(event); + } else if (mMagnificationController.isMagnifying()) { + // If magnified, consume an ACTION_DOWN until mMultiTapTimeSlop or + // mTapDistanceSlop is reached to ensure MAGNIFIED_INTERACTION is reachable. Message message = mHandler.obtainMessage( MESSAGE_TRANSITION_TO_DELEGATING_STATE); mHandler.sendMessageDelayed(message, mMultiTapTimeSlop); + return; + } else { + transitionToDelegatingState(true); + return; } - clearLastDownEvent(); - mLastDownEvent = MotionEvent.obtain(event); } break; case MotionEvent.ACTION_POINTER_DOWN: { if (mMagnificationController.isMagnifying()) { + mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); transitionToState(STATE_MAGNIFIED_INTERACTION); clear(); } else { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(true); } } break; @@ -608,29 +684,34 @@ class MagnificationGestureHandler implements EventStreamTransformation { final double distance = GestureUtils.computeDistance(mLastDownEvent, event, 0); if (Math.abs(distance) > mTapDistanceSlop) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(true); } } } break; case MotionEvent.ACTION_UP: { - if (mLastDownEvent == null) { - return; - } - mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); if (!mMagnificationController.magnificationRegionContains( event.getX(), event.getY())) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(!mShortcutTriggered); return; } + if (mShortcutTriggered) { + clear(); + onActionTap(event, policyFlags); + return; + } + if (mLastDownEvent == null) { + return; + } + mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop, mTapDistanceSlop, 0)) { - transitionToDelegatingStateAndClear(); + transitionToDelegatingState(true); return; } - if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(mLastTapUpEvent, - event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) { - transitionToDelegatingStateAndClear(); + if (mLastTapUpEvent != null && !GestureUtils.isMultiTap( + mLastTapUpEvent, event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) { + transitionToDelegatingState(true); return; } mTapCount++; @@ -655,6 +736,7 @@ class MagnificationGestureHandler implements EventStreamTransformation { @Override public void clear() { + setMagnificationShortcutTriggered(false); mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD); mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE); clearTapDetectionState(); @@ -714,10 +796,12 @@ class MagnificationGestureHandler implements EventStreamTransformation { } } - private void transitionToDelegatingStateAndClear() { + private void transitionToDelegatingState(boolean andClear) { transitionToState(STATE_DELEGATING); sendDelayedMotionEvents(); - clear(); + if (andClear) { + clear(); + } } private void onActionTap(MotionEvent up, int policyFlags) { @@ -820,4 +904,30 @@ class MagnificationGestureHandler implements EventStreamTransformation { mPolicyFlags = 0; } } + + /** + * BroadcastReceiver used to cancel the magnification shortcut when the screen turns off + */ + private static class ScreenStateReceiver extends BroadcastReceiver { + private final Context mContext; + private final MagnificationGestureHandler mGestureHandler; + + public ScreenStateReceiver(Context context, MagnificationGestureHandler gestureHandler) { + mContext = context; + mGestureHandler = gestureHandler; + } + + public void register() { + mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); + } + + public void unregister() { + mContext.unregisterReceiver(this); + } + + @Override + public void onReceive(Context context, Intent intent) { + mGestureHandler.setMagnificationShortcutTriggered(false); + } + } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 1510dd126c74..5abc4e4d2e56 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -222,6 +222,14 @@ final class AccessibilityController { || mWindowsForAccessibilityObserver != null); } + /** NOTE: This has to be called within a surface transaction. */ + public void setForceShowMagnifiableBoundsLocked(boolean show) { + if (mDisplayMagnifier != null) { + mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show); + mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); + } + } + private static void populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix) { sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; @@ -266,6 +274,8 @@ final class AccessibilityController { private final long mLongAnimationDuration; + private boolean mForceShowMagnifiableBounds = false; + public DisplayMagnifier(WindowManagerService windowManagerService, MagnificationCallbacks callbacks) { mContext = windowManagerService.mContext; @@ -283,6 +293,15 @@ final class AccessibilityController { mWindowManagerService.scheduleAnimationLocked(); } + public void setForceShowMagnifiableBoundsLocked(boolean show) { + mForceShowMagnifiableBounds = show; + mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); + } + + public boolean isForceShowingMagnifiableBoundsLocked() { + return mForceShowMagnifiableBounds; + } + public void onRectangleOnScreenRequestedLocked(Rect rectangle) { if (DEBUG_RECTANGLE_REQUESTED) { Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); @@ -488,7 +507,8 @@ final class AccessibilityController { // to show the border. We will do so when the pending message is handled. if (!mHandler.hasMessages( MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { - setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true); + setMagnifiedRegionBorderShownLocked( + isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); } } @@ -600,11 +620,11 @@ final class AccessibilityController { } public void onRotationChangedLocked() { - // If we are magnifying, hide the magnified border window immediately so + // If we are showing the magnification border, hide it immediately so // the user does not see strange artifacts during rotation. The screenshot - // used for rotation has already the border. After the rotation is complete + // used for rotation already has the border. After the rotation is complete // we will show the border. - if (isMagnifyingLocked()) { + if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) { setMagnifiedRegionBorderShownLocked(false, false); final long delay = (long) (mLongAnimationDuration * mWindowManagerService.getWindowAnimationScaleLocked()); @@ -926,7 +946,8 @@ final class AccessibilityController { case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { synchronized (mWindowManagerService.mWindowMap) { - if (mMagnifedViewport.isMagnifyingLocked()) { + if (mMagnifedViewport.isMagnifyingLocked() + || isForceShowingMagnifiableBoundsLocked()) { mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); mWindowManagerService.scheduleAnimationLocked(); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b063e01f3a2a..014a89dd25d7 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7158,6 +7158,17 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setForceShowMagnifiableBounds(boolean show) { + synchronized (mWindowMap) { + if (mAccessibilityController != null) { + mAccessibilityController.setForceShowMagnifiableBoundsLocked(show); + } else { + throw new IllegalStateException("Magnification callbacks not set!"); + } + } + } + + @Override public void getMagnificationRegion(@NonNull Region magnificationRegion) { synchronized (mWindowMap) { if (mAccessibilityController != null) { |