1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.accessibility.magnification; 18 19 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION; 20 import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK; 21 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK; 22 23 import static com.android.server.accessibility.AccessibilityManagerService.INVALID_SERVICE_ID; 24 import static com.android.server.accessibility.AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID; 25 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.graphics.PointF; 34 import android.graphics.Rect; 35 import android.graphics.Region; 36 import android.os.Binder; 37 import android.os.IBinder; 38 import android.os.RemoteException; 39 import android.os.SystemClock; 40 import android.util.MathUtils; 41 import android.util.Slog; 42 import android.util.SparseArray; 43 import android.util.SparseBooleanArray; 44 import android.view.MotionEvent; 45 import android.view.accessibility.IWindowMagnificationConnection; 46 import android.view.accessibility.IWindowMagnificationConnectionCallback; 47 import android.view.accessibility.MagnificationAnimationCallback; 48 49 import com.android.internal.accessibility.common.MagnificationConstants; 50 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils; 51 import com.android.internal.annotations.GuardedBy; 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.server.LocalServices; 54 import com.android.server.accessibility.AccessibilityTraceManager; 55 import com.android.server.statusbar.StatusBarManagerInternal; 56 import com.android.server.wm.WindowManagerInternal; 57 58 import java.lang.annotation.Retention; 59 import java.lang.annotation.RetentionPolicy; 60 import java.util.concurrent.atomic.AtomicLongFieldUpdater; 61 62 /** 63 * A class to manipulate window magnification through {@link WindowMagnificationConnectionWrapper} 64 * create by {@link #setConnection(IWindowMagnificationConnection)}. To set the connection with 65 * SysUI, call {@code StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)}. 66 * The applied magnification scale is constrained by 67 * {@link MagnificationScaleProvider#constrainScale(float)} 68 */ 69 public class WindowMagnificationManager implements 70 PanningScalingHandler.MagnificationDelegate, 71 WindowManagerInternal.AccessibilityControllerInternal.UiChangesForAccessibilityCallbacks { 72 73 private static final boolean DBG = false; 74 75 private static final String TAG = "WindowMagnificationMgr"; 76 77 /** 78 * Indicate that the magnification window is at the magnification center. 79 */ 80 public static final int WINDOW_POSITION_AT_CENTER = 0; 81 82 /** 83 * Indicate that the magnification window is at the top-left side of the magnification 84 * center. The offset is equal to a half of MirrorSurfaceView. So, the bottom-right corner 85 * of the window is at the magnification center. 86 */ 87 public static final int WINDOW_POSITION_AT_TOP_LEFT = 1; 88 89 @Retention(RetentionPolicy.SOURCE) 90 @IntDef(prefix = { "WINDOW_POSITION_AT_" }, value = { 91 WINDOW_POSITION_AT_CENTER, 92 WINDOW_POSITION_AT_TOP_LEFT 93 }) 94 public @interface WindowPosition {} 95 96 /** Window magnification connection is connecting. */ 97 private static final int CONNECTING = 0; 98 /** Window magnification connection is connected. */ 99 private static final int CONNECTED = 1; 100 /** Window magnification connection is disconnecting. */ 101 private static final int DISCONNECTING = 2; 102 /** Window magnification connection is disconnected. */ 103 private static final int DISCONNECTED = 3; 104 105 @Retention(RetentionPolicy.SOURCE) 106 @IntDef(prefix = {"CONNECTION_STATE"}, value = { 107 CONNECTING, 108 CONNECTED, 109 DISCONNECTING, 110 DISCONNECTED 111 }) 112 private @interface ConnectionState { 113 } 114 connectionStateToString(@onnectionState int state)115 private static String connectionStateToString(@ConnectionState int state) { 116 switch (state) { 117 case CONNECTING: return "CONNECTING"; 118 case CONNECTED: return "CONNECTED"; 119 case DISCONNECTING: return "DISCONNECTING"; 120 case DISCONNECTED: return "DISCONNECTED"; 121 default: 122 return "UNKNOWN:" + state; 123 } 124 } 125 126 @ConnectionState 127 private int mConnectionState = DISCONNECTED; 128 129 private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 100; 130 131 private final Object mLock; 132 private final Context mContext; 133 @VisibleForTesting 134 @GuardedBy("mLock") 135 @Nullable 136 WindowMagnificationConnectionWrapper mConnectionWrapper; 137 @GuardedBy("mLock") 138 private ConnectionCallback mConnectionCallback; 139 @GuardedBy("mLock") 140 private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>(); 141 // Whether the following typing focus feature for magnification is enabled. 142 private boolean mMagnificationFollowTypingEnabled = true; 143 @GuardedBy("mLock") 144 private final SparseBooleanArray mIsImeVisibleArray = new SparseBooleanArray(); 145 @GuardedBy("mLock") 146 private final SparseArray<Float> mLastActivatedScale = new SparseArray<>(); 147 148 private boolean mReceiverRegistered = false; 149 @VisibleForTesting 150 protected final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() { 151 @Override 152 public void onReceive(Context context, Intent intent) { 153 final int displayId = context.getDisplayId(); 154 removeMagnificationButton(displayId); 155 disableWindowMagnification(displayId, false, null); 156 } 157 }; 158 159 /** 160 * Callback to handle magnification actions from system UI. 161 */ 162 public interface Callback { 163 164 /** 165 * Called when the accessibility action of scale requests to be performed. 166 * It is invoked from System UI. And the action is provided by the mirror window. 167 * 168 * @param displayId The logical display id. 169 * @param scale the target scale, or {@link Float#NaN} to leave unchanged 170 * @param updatePersistence whether the scale should be persisted 171 */ onPerformScaleAction(int displayId, float scale, boolean updatePersistence)172 void onPerformScaleAction(int displayId, float scale, boolean updatePersistence); 173 174 /** 175 * Called when the accessibility action is performed. 176 * 177 * @param displayId The logical display id. 178 */ onAccessibilityActionPerformed(int displayId)179 void onAccessibilityActionPerformed(int displayId); 180 181 /** 182 * Called when the state of the magnification activation is changed. 183 * 184 * @param displayId The logical display id. 185 * @param activated {@code true} if the magnification is activated, otherwise {@code false}. 186 */ onWindowMagnificationActivationState(int displayId, boolean activated)187 void onWindowMagnificationActivationState(int displayId, boolean activated); 188 189 /** 190 * Called when the magnification source bounds are changed. 191 * 192 * @param displayId The logical display id. 193 * @param bounds The magnified source bounds on the display. 194 */ onSourceBoundsChanged(int displayId, Rect bounds)195 void onSourceBoundsChanged(int displayId, Rect bounds); 196 197 /** 198 * Called from {@link IWindowMagnificationConnection} to request changing the magnification 199 * mode on the given display. 200 * 201 * @param displayId the logical display id 202 * @param magnificationMode the target magnification mode 203 */ onChangeMagnificationMode(int displayId, int magnificationMode)204 void onChangeMagnificationMode(int displayId, int magnificationMode); 205 } 206 207 private final Callback mCallback; 208 private final AccessibilityTraceManager mTrace; 209 private final MagnificationScaleProvider mScaleProvider; 210 WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback, AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider)211 public WindowMagnificationManager(Context context, Object lock, @NonNull Callback callback, 212 AccessibilityTraceManager trace, MagnificationScaleProvider scaleProvider) { 213 mContext = context; 214 mLock = lock; 215 mCallback = callback; 216 mTrace = trace; 217 mScaleProvider = scaleProvider; 218 } 219 220 /** 221 * Sets {@link IWindowMagnificationConnection}. 222 * 223 * @param connection {@link IWindowMagnificationConnection} 224 */ setConnection(@ullable IWindowMagnificationConnection connection)225 public void setConnection(@Nullable IWindowMagnificationConnection connection) { 226 if (DBG) { 227 Slog.d(TAG, "setConnection :" + connection + ", mConnectionState=" 228 + connectionStateToString(mConnectionState)); 229 } 230 synchronized (mLock) { 231 // Reset connectionWrapper. 232 if (mConnectionWrapper != null) { 233 mConnectionWrapper.setConnectionCallback(null); 234 if (mConnectionCallback != null) { 235 mConnectionCallback.mExpiredDeathRecipient = true; 236 } 237 mConnectionWrapper.unlinkToDeath(mConnectionCallback); 238 mConnectionWrapper = null; 239 // The connection is still connecting so it is no need to reset the 240 // connection state to disconnected. 241 // TODO b/220086369 will reset the connection immediately when requestConnection 242 // is called 243 if (mConnectionState != CONNECTING) { 244 setConnectionState(DISCONNECTED); 245 } 246 } 247 if (connection != null) { 248 mConnectionWrapper = new WindowMagnificationConnectionWrapper(connection, mTrace); 249 } 250 251 if (mConnectionWrapper != null) { 252 try { 253 mConnectionCallback = new ConnectionCallback(); 254 mConnectionWrapper.linkToDeath(mConnectionCallback); 255 mConnectionWrapper.setConnectionCallback(mConnectionCallback); 256 setConnectionState(CONNECTED); 257 } catch (RemoteException e) { 258 Slog.e(TAG, "setConnection failed", e); 259 mConnectionWrapper = null; 260 setConnectionState(DISCONNECTED); 261 } finally { 262 mLock.notify(); 263 } 264 } 265 } 266 } 267 268 /** 269 * @return {@code true} if {@link IWindowMagnificationConnection} is available 270 */ isConnected()271 public boolean isConnected() { 272 synchronized (mLock) { 273 return mConnectionWrapper != null; 274 } 275 } 276 277 /** 278 * Requests {@link IWindowMagnificationConnection} through 279 * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and 280 * destroys all window magnifications if necessary. 281 * 282 * @param connect {@code true} if needs connection, otherwise set the connection to null and 283 * destroy all window magnifications. 284 * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change. 285 */ requestConnection(boolean connect)286 public boolean requestConnection(boolean connect) { 287 if (DBG) { 288 Slog.d(TAG, "requestConnection :" + connect); 289 } 290 if (mTrace.isA11yTracingEnabledForTypes(FLAGS_WINDOW_MAGNIFICATION_CONNECTION)) { 291 mTrace.logTrace(TAG + ".requestWindowMagnificationConnection", 292 FLAGS_WINDOW_MAGNIFICATION_CONNECTION, "connect=" + connect); 293 } 294 synchronized (mLock) { 295 if ((connect && (mConnectionState == CONNECTED || mConnectionState == CONNECTING)) 296 || (!connect && (mConnectionState == DISCONNECTED 297 || mConnectionState == DISCONNECTING))) { 298 Slog.w(TAG, "requestConnection duplicated request: connect=" + connect 299 + ", mConnectionState=" + connectionStateToString(mConnectionState)); 300 return false; 301 } 302 303 if (connect) { 304 final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 305 if (!mReceiverRegistered) { 306 mContext.registerReceiver(mScreenStateReceiver, intentFilter); 307 mReceiverRegistered = true; 308 } 309 } else { 310 disableAllWindowMagnifiers(); 311 if (mReceiverRegistered) { 312 mContext.unregisterReceiver(mScreenStateReceiver); 313 mReceiverRegistered = false; 314 } 315 } 316 } 317 if (requestConnectionInternal(connect)) { 318 setConnectionState(connect ? CONNECTING : DISCONNECTING); 319 return true; 320 } else { 321 setConnectionState(DISCONNECTED); 322 return false; 323 } 324 } 325 requestConnectionInternal(boolean connect)326 private boolean requestConnectionInternal(boolean connect) { 327 final long identity = Binder.clearCallingIdentity(); 328 try { 329 final StatusBarManagerInternal service = LocalServices.getService( 330 StatusBarManagerInternal.class); 331 if (service != null) { 332 return service.requestWindowMagnificationConnection(connect); 333 } 334 } finally { 335 Binder.restoreCallingIdentity(identity); 336 } 337 return false; 338 } 339 340 /** 341 * Returns window magnification connection state. 342 */ getConnectionState()343 public String getConnectionState() { 344 return connectionStateToString(mConnectionState); 345 } 346 setConnectionState(@onnectionState int state)347 private void setConnectionState(@ConnectionState int state) { 348 if (DBG) { 349 Slog.d(TAG, "setConnectionState : state=" + state + ", mConnectionState=" 350 + connectionStateToString(mConnectionState)); 351 } 352 mConnectionState = state; 353 } 354 355 /** 356 * Disables window magnifier on all displays without animation. 357 */ disableAllWindowMagnifiers()358 void disableAllWindowMagnifiers() { 359 synchronized (mLock) { 360 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 361 final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 362 magnifier.disableWindowMagnificationInternal(null); 363 } 364 mWindowMagnifiers.clear(); 365 } 366 } 367 368 /** 369 * Resets the window magnifier on all displays that had been controlled by the 370 * specified service connection. Called when the service connection is unbound 371 * or binder died. 372 * 373 * @param connectionId The connection id 374 */ resetAllIfNeeded(int connectionId)375 public void resetAllIfNeeded(int connectionId) { 376 synchronized (mLock) { 377 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 378 final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 379 if (magnifier != null 380 && magnifier.mEnabled 381 && connectionId == magnifier.getIdOfLastServiceToControl()) { 382 magnifier.disableWindowMagnificationInternal(null); 383 } 384 } 385 } 386 } 387 resetWindowMagnifiers()388 private void resetWindowMagnifiers() { 389 synchronized (mLock) { 390 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 391 WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 392 magnifier.reset(); 393 } 394 } 395 } 396 397 @Override onRectangleOnScreenRequested(int displayId, int left, int top, int right, int bottom)398 public void onRectangleOnScreenRequested(int displayId, int left, int top, int right, 399 int bottom) { 400 if (!mMagnificationFollowTypingEnabled) { 401 return; 402 } 403 404 float toCenterX = (float) (left + right) / 2; 405 float toCenterY = (float) (top + bottom) / 2; 406 407 synchronized (mLock) { 408 if (mIsImeVisibleArray.get(displayId, false) 409 && !isPositionInSourceBounds(displayId, toCenterX, toCenterY) 410 && isTrackingTypingFocusEnabled(displayId)) { 411 moveWindowMagnifierToPositionInternal(displayId, toCenterX, toCenterY, 412 STUB_ANIMATION_CALLBACK); 413 } 414 } 415 } 416 setMagnificationFollowTypingEnabled(boolean enabled)417 void setMagnificationFollowTypingEnabled(boolean enabled) { 418 mMagnificationFollowTypingEnabled = enabled; 419 } 420 isMagnificationFollowTypingEnabled()421 boolean isMagnificationFollowTypingEnabled() { 422 return mMagnificationFollowTypingEnabled; 423 } 424 425 /** 426 * Get the ID of the last service that changed the magnification config. 427 * 428 * @param displayId The logical display id. 429 * @return The id 430 */ getIdOfLastServiceToMagnify(int displayId)431 public int getIdOfLastServiceToMagnify(int displayId) { 432 synchronized (mLock) { 433 final WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 434 if (magnifier != null) { 435 return magnifier.mIdOfLastServiceToControl; 436 } 437 } 438 return INVALID_SERVICE_ID; 439 } 440 441 /** 442 * Enable or disable tracking typing focus for the specific magnification window. 443 * 444 * The tracking typing focus should be set to enabled with the following conditions: 445 * 1. IME is shown. 446 * 447 * The tracking typing focus should be set to disabled with the following conditions: 448 * 1. A user drags the magnification window by 1 finger. 449 * 2. A user scroll the magnification window by 2 fingers. 450 * 451 * @param displayId The logical display id. 452 * @param trackingTypingFocusEnabled Enabled or disable the function of tracking typing focus. 453 */ setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled)454 void setTrackingTypingFocusEnabled(int displayId, boolean trackingTypingFocusEnabled) { 455 synchronized (mLock) { 456 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 457 if (magnifier == null) { 458 return; 459 } 460 magnifier.setTrackingTypingFocusEnabled(trackingTypingFocusEnabled); 461 } 462 } 463 464 /** 465 * Enable tracking typing focus function for all magnifications. 466 */ enableAllTrackingTypingFocus()467 private void enableAllTrackingTypingFocus() { 468 synchronized (mLock) { 469 for (int i = 0; i < mWindowMagnifiers.size(); i++) { 470 WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i); 471 magnifier.setTrackingTypingFocusEnabled(true); 472 } 473 } 474 } 475 pauseTrackingTypingFocusRecord(int displayId)476 private void pauseTrackingTypingFocusRecord(int displayId) { 477 WindowMagnifier magnifier; 478 synchronized (mLock) { 479 magnifier = mWindowMagnifiers.get(displayId); 480 if (magnifier == null) { 481 return; 482 } 483 } 484 magnifier.pauseTrackingTypingFocusRecord(); 485 } 486 487 /** 488 * Called when the IME window visibility changed. 489 * 490 * @param shown {@code true} means the IME window shows on the screen. Otherwise, it's hidden. 491 */ onImeWindowVisibilityChanged(int displayId, boolean shown)492 void onImeWindowVisibilityChanged(int displayId, boolean shown) { 493 synchronized (mLock) { 494 mIsImeVisibleArray.put(displayId, shown); 495 } 496 if (shown) { 497 enableAllTrackingTypingFocus(); 498 } else { 499 pauseTrackingTypingFocusRecord(displayId); 500 } 501 } 502 isImeVisible(int displayId)503 boolean isImeVisible(int displayId) { 504 synchronized (mLock) { 505 return mIsImeVisibleArray.get(displayId); 506 } 507 } 508 logTrackingTypingFocus(long duration)509 void logTrackingTypingFocus(long duration) { 510 AccessibilityStatsLogUtils.logMagnificationFollowTypingFocusSession(duration); 511 } 512 513 @Override processScroll(int displayId, float distanceX, float distanceY)514 public boolean processScroll(int displayId, float distanceX, float distanceY) { 515 moveWindowMagnification(displayId, -distanceX, -distanceY); 516 setTrackingTypingFocusEnabled(displayId, false); 517 return /* event consumed: */ true; 518 } 519 520 /** 521 * Scales the magnified region on the specified display if window magnification is initiated. 522 * 523 * @param displayId The logical display id. 524 * @param scale The target scale, must be >= 1 525 */ 526 @Override setScale(int displayId, float scale)527 public void setScale(int displayId, float scale) { 528 synchronized (mLock) { 529 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 530 if (magnifier == null) { 531 return; 532 } 533 magnifier.setScale(scale); 534 mLastActivatedScale.put(displayId, scale); 535 } 536 } 537 538 /** 539 * Enables window magnification with specified center and scale on the given display and 540 * animating the transition. 541 * 542 * @param displayId The logical display id. 543 * @param scale The target scale, must be >= 1. 544 * @param centerX The screen-relative X coordinate around which to center, 545 * or {@link Float#NaN} to leave unchanged. 546 * @param centerY The screen-relative Y coordinate around which to center, 547 * or {@link Float#NaN} to leave unchanged. 548 * @return {@code true} if the magnification is enabled successfully. 549 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY)550 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 551 float centerY) { 552 return enableWindowMagnification(displayId, scale, centerX, centerY, 553 STUB_ANIMATION_CALLBACK, MAGNIFICATION_GESTURE_HANDLER_ID); 554 } 555 556 /** 557 * Enables window magnification with specified center and scale on the given display and 558 * animating the transition. 559 * 560 * @param displayId The logical display id. 561 * @param scale The target scale, must be >= 1. 562 * @param centerX The screen-relative X coordinate around which to center for magnification, 563 * or {@link Float#NaN} to leave unchanged. 564 * @param centerY The screen-relative Y coordinate around which to center for magnification, 565 * or {@link Float#NaN} to leave unchanged. 566 * @param animationCallback Called when the animation result is valid. 567 * @param id The connection ID 568 * @return {@code true} if the magnification is enabled successfully. 569 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id)570 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 571 float centerY, @Nullable MagnificationAnimationCallback animationCallback, int id) { 572 return enableWindowMagnification(displayId, scale, centerX, centerY, animationCallback, 573 WINDOW_POSITION_AT_CENTER, id); 574 } 575 576 /** 577 * Enables window magnification with specified center and scale on the given display and 578 * animating the transition. 579 * 580 * @param displayId The logical display id. 581 * @param scale The target scale, must be >= 1. 582 * @param centerX The screen-relative X coordinate around which to center for magnification, 583 * or {@link Float#NaN} to leave unchanged. 584 * @param centerY The screen-relative Y coordinate around which to center for magnification, 585 * or {@link Float#NaN} to leave unchanged. 586 * @param windowPosition Indicate the offset between window position and (centerX, centerY). 587 * @return {@code true} if the magnification is enabled successfully. 588 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @WindowPosition int windowPosition)589 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 590 float centerY, @WindowPosition int windowPosition) { 591 return enableWindowMagnification(displayId, scale, centerX, centerY, 592 STUB_ANIMATION_CALLBACK, windowPosition, MAGNIFICATION_GESTURE_HANDLER_ID); 593 } 594 595 /** 596 * Enables window magnification with specified center and scale on the given display and 597 * animating the transition. 598 * 599 * @param displayId The logical display id. 600 * @param scale The target scale, must be >= 1. 601 * @param centerX The screen-relative X coordinate around which to center for 602 * magnification, or {@link Float#NaN} to leave unchanged. 603 * @param centerY The screen-relative Y coordinate around which to center for 604 * magnification, or {@link Float#NaN} to leave unchanged. 605 * @param animationCallback Called when the animation result is valid. 606 * @param windowPosition Indicate the offset between window position and (centerX, centerY). 607 * @return {@code true} if the magnification is enabled successfully. 608 */ enableWindowMagnification(int displayId, float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id)609 public boolean enableWindowMagnification(int displayId, float scale, float centerX, 610 float centerY, @Nullable MagnificationAnimationCallback animationCallback, 611 @WindowPosition int windowPosition, int id) { 612 final boolean enabled; 613 boolean previousEnabled; 614 synchronized (mLock) { 615 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 616 if (magnifier == null) { 617 magnifier = createWindowMagnifier(displayId); 618 } 619 previousEnabled = magnifier.mEnabled; 620 enabled = magnifier.enableWindowMagnificationInternal(scale, centerX, centerY, 621 animationCallback, windowPosition, id); 622 if (enabled) { 623 mLastActivatedScale.put(displayId, getScale(displayId)); 624 } 625 } 626 627 if (enabled) { 628 setTrackingTypingFocusEnabled(displayId, true); 629 if (!previousEnabled) { 630 mCallback.onWindowMagnificationActivationState(displayId, true); 631 } 632 } 633 return enabled; 634 } 635 636 /** 637 * Disables window magnification on the given display. 638 * 639 * @param displayId The logical display id. 640 * @param clear {@true} Clears the state of window magnification. 641 * @return {@code true} if the magnification is turned to be disabled successfully 642 */ disableWindowMagnification(int displayId, boolean clear)643 public boolean disableWindowMagnification(int displayId, boolean clear) { 644 return disableWindowMagnification(displayId, clear, STUB_ANIMATION_CALLBACK); 645 } 646 647 /** 648 * Disables window magnification on the specified display and animating the transition. 649 * 650 * @param displayId The logical display id. 651 * @param clear {@true} Clears the state of window magnification. 652 * @param animationCallback Called when the animation result is valid. 653 * @return {@code true} if the magnification is turned to be disabled successfully 654 */ disableWindowMagnification(int displayId, boolean clear, MagnificationAnimationCallback animationCallback)655 public boolean disableWindowMagnification(int displayId, boolean clear, 656 MagnificationAnimationCallback animationCallback) { 657 final boolean disabled; 658 synchronized (mLock) { 659 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 660 if (magnifier == null) { 661 return false; 662 } 663 664 disabled = magnifier.disableWindowMagnificationInternal(animationCallback); 665 if (clear) { 666 mWindowMagnifiers.delete(displayId); 667 } 668 } 669 670 if (disabled) { 671 mCallback.onWindowMagnificationActivationState(displayId, false); 672 } 673 return disabled; 674 } 675 676 /** 677 * Calculates the number of fingers in the window. 678 * 679 * @param displayId The logical display id. 680 * @param motionEvent The motion event 681 * @return the number of fingers in the window. 682 */ pointersInWindow(int displayId, MotionEvent motionEvent)683 int pointersInWindow(int displayId, MotionEvent motionEvent) { 684 synchronized (mLock) { 685 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 686 if (magnifier == null) { 687 return 0; 688 } 689 return magnifier.pointersInWindow(motionEvent); 690 } 691 } 692 693 @GuardedBy("mLock") isPositionInSourceBounds(int displayId, float x, float y)694 boolean isPositionInSourceBounds(int displayId, float x, float y) { 695 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 696 if (magnifier == null) { 697 return false; 698 } 699 return magnifier.isPositionInSourceBounds(x, y); 700 } 701 702 /** 703 * Indicates whether window magnification is enabled on specified display. 704 * 705 * @param displayId The logical display id. 706 * @return {@code true} if the window magnification is enabled. 707 */ isWindowMagnifierEnabled(int displayId)708 public boolean isWindowMagnifierEnabled(int displayId) { 709 synchronized (mLock) { 710 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 711 if (magnifier == null) { 712 return false; 713 } 714 return magnifier.isEnabled(); 715 } 716 } 717 718 /** 719 * Retrieves a previously magnification scale from the current 720 * user's settings. Only the value of the default display is persisted. 721 * 722 * @return the previously magnification scale, or the default 723 * scale if none is available 724 */ getPersistedScale(int displayId)725 float getPersistedScale(int displayId) { 726 return MathUtils.constrain(mScaleProvider.getScale(displayId), 727 MagnificationConstants.PERSISTED_SCALE_MIN_VALUE, 728 MagnificationScaleProvider.MAX_SCALE); 729 } 730 731 /** 732 * Persists the default display magnification scale to the current user's settings 733 * <strong>if scale is >= {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}</strong>. 734 * We assume if the scale is < {@link MagnificationConstants.PERSISTED_SCALE_MIN_VALUE}, there 735 * will be no obvious magnification effect. 736 * Only the value of the default display is persisted in user's settings. 737 */ persistScale(int displayId)738 void persistScale(int displayId) { 739 float scale = getScale(displayId); 740 if (scale < MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { 741 return; 742 } 743 mScaleProvider.putScale(scale, displayId); 744 } 745 746 /** 747 * Returns the magnification scale. 748 * 749 * @param displayId The logical display id. 750 * @return the scale 751 */ getScale(int displayId)752 public float getScale(int displayId) { 753 synchronized (mLock) { 754 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 755 if (magnifier == null || !magnifier.mEnabled) { 756 return 1.0f; 757 } 758 return magnifier.getScale(); 759 } 760 } 761 getLastActivatedScale(int displayId)762 protected float getLastActivatedScale(int displayId) { 763 synchronized (mLock) { 764 if (!mLastActivatedScale.contains(displayId)) { 765 return -1.0f; 766 } 767 return mLastActivatedScale.get(displayId); 768 } 769 } 770 771 /** 772 * Moves window magnification on the specified display with the specified offset. 773 * 774 * @param displayId The logical display id. 775 * @param offsetX the amount in pixels to offset the region in the X direction, in current 776 * screen pixels. 777 * @param offsetY the amount in pixels to offset the region in the Y direction, in current 778 * screen pixels. 779 */ moveWindowMagnification(int displayId, float offsetX, float offsetY)780 void moveWindowMagnification(int displayId, float offsetX, float offsetY) { 781 synchronized (mLock) { 782 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 783 if (magnifier == null) { 784 return; 785 } 786 magnifier.move(offsetX, offsetY); 787 } 788 } 789 790 /** 791 * Requests System UI show magnification mode button UI on the specified display. 792 * 793 * @param displayId The logical display id. 794 * @param magnificationMode the current magnification mode. 795 * @return {@code true} if the event was handled, {@code false} otherwise 796 */ showMagnificationButton(int displayId, int magnificationMode)797 public boolean showMagnificationButton(int displayId, int magnificationMode) { 798 synchronized (mLock) { 799 return mConnectionWrapper != null 800 && mConnectionWrapper.showMagnificationButton(displayId, magnificationMode); 801 } 802 } 803 804 /** 805 * Requests System UI remove magnification mode button UI on the specified display. 806 * 807 * @param displayId The logical display id. 808 * @return {@code true} if the event was handled, {@code false} otherwise 809 */ removeMagnificationButton(int displayId)810 public boolean removeMagnificationButton(int displayId) { 811 synchronized (mLock) { 812 return mConnectionWrapper != null 813 && mConnectionWrapper.removeMagnificationButton(displayId); 814 } 815 } 816 817 /** 818 * Requests System UI remove magnification settings panel on the specified display. 819 * 820 * @param displayId The logical display id. 821 * @return {@code true} if the event was handled, {@code false} otherwise 822 */ removeMagnificationSettingsPanel(int displayId)823 public boolean removeMagnificationSettingsPanel(int displayId) { 824 synchronized (mLock) { 825 return mConnectionWrapper != null 826 && mConnectionWrapper.removeMagnificationSettingsPanel(displayId); 827 } 828 } 829 830 /** 831 * Notify System UI the magnification scale on the specified display for userId is changed. 832 * 833 * @param userId the user id. 834 * @param displayId the logical display id. 835 * @param scale magnification scale. 836 */ onUserMagnificationScaleChanged(int userId, int displayId, float scale)837 public boolean onUserMagnificationScaleChanged(int userId, int displayId, float scale) { 838 synchronized (mLock) { 839 return mConnectionWrapper != null 840 && mConnectionWrapper.onUserMagnificationScaleChanged(userId, displayId, scale); 841 } 842 } 843 844 /** 845 * Returns the screen-relative X coordinate of the center of the magnified bounds. 846 * 847 * @param displayId The logical display id 848 * @return the X coordinate. {@link Float#NaN} if the window magnification is not enabled. 849 */ getCenterX(int displayId)850 public float getCenterX(int displayId) { 851 synchronized (mLock) { 852 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 853 if (magnifier == null || !magnifier.mEnabled) { 854 return Float.NaN; 855 } 856 return magnifier.getCenterX(); 857 } 858 } 859 860 /** 861 * Returns the screen-relative Y coordinate of the center of the magnified bounds. 862 * 863 * @param displayId The logical display id 864 * @return the Y coordinate. {@link Float#NaN} if the window magnification is not enabled. 865 */ getCenterY(int displayId)866 public float getCenterY(int displayId) { 867 synchronized (mLock) { 868 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 869 if (magnifier == null || !magnifier.mEnabled) { 870 return Float.NaN; 871 } 872 return magnifier.getCenterY(); 873 } 874 } 875 isTrackingTypingFocusEnabled(int displayId)876 boolean isTrackingTypingFocusEnabled(int displayId) { 877 synchronized (mLock) { 878 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 879 if (magnifier == null) { 880 return false; 881 } 882 return magnifier.isTrackingTypingFocusEnabled(); 883 } 884 } 885 886 /** 887 * Populates magnified bounds on the screen. And the populated magnified bounds would be 888 * empty If window magnifier is not activated. 889 * 890 * @param displayId The logical display id. 891 * @param outRegion the region to populate 892 */ getMagnificationSourceBounds(int displayId, @NonNull Region outRegion)893 public void getMagnificationSourceBounds(int displayId, @NonNull Region outRegion) { 894 synchronized (mLock) { 895 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 896 if (magnifier == null || !magnifier.mEnabled) { 897 outRegion.setEmpty(); 898 } else { 899 outRegion.set(magnifier.mSourceBounds); 900 } 901 } 902 } 903 904 /** 905 * Creates the windowMagnifier based on the specified display and stores it. 906 * 907 * @param displayId logical display id. 908 */ 909 @GuardedBy("mLock") createWindowMagnifier(int displayId)910 private WindowMagnifier createWindowMagnifier(int displayId) { 911 final WindowMagnifier magnifier = new WindowMagnifier(displayId, this); 912 mWindowMagnifiers.put(displayId, magnifier); 913 return magnifier; 914 } 915 916 /** 917 * Removes the window magnifier with given id. 918 * 919 * @param displayId The logical display id. 920 */ onDisplayRemoved(int displayId)921 public void onDisplayRemoved(int displayId) { 922 disableWindowMagnification(displayId, true); 923 } 924 925 private class ConnectionCallback extends IWindowMagnificationConnectionCallback.Stub implements 926 IBinder.DeathRecipient { 927 private boolean mExpiredDeathRecipient = false; 928 929 @Override onWindowMagnifierBoundsChanged(int displayId, Rect bounds)930 public void onWindowMagnifierBoundsChanged(int displayId, Rect bounds) { 931 if (mTrace.isA11yTracingEnabledForTypes( 932 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { 933 mTrace.logTrace(TAG + "ConnectionCallback.onWindowMagnifierBoundsChanged", 934 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, 935 "displayId=" + displayId + ";bounds=" + bounds); 936 } 937 synchronized (mLock) { 938 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 939 if (magnifier == null) { 940 magnifier = createWindowMagnifier(displayId); 941 } 942 if (DBG) { 943 Slog.i(TAG, 944 "onWindowMagnifierBoundsChanged -" + displayId + " bounds = " + bounds); 945 } 946 magnifier.setMagnifierLocation(bounds); 947 } 948 } 949 950 @Override onChangeMagnificationMode(int displayId, int magnificationMode)951 public void onChangeMagnificationMode(int displayId, int magnificationMode) 952 throws RemoteException { 953 if (mTrace.isA11yTracingEnabledForTypes( 954 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { 955 mTrace.logTrace(TAG + "ConnectionCallback.onChangeMagnificationMode", 956 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, 957 "displayId=" + displayId + ";mode=" + magnificationMode); 958 } 959 mCallback.onChangeMagnificationMode(displayId, magnificationMode); 960 } 961 962 @Override onSourceBoundsChanged(int displayId, Rect sourceBounds)963 public void onSourceBoundsChanged(int displayId, Rect sourceBounds) { 964 if (mTrace.isA11yTracingEnabledForTypes( 965 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { 966 mTrace.logTrace(TAG + "ConnectionCallback.onSourceBoundsChanged", 967 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, 968 "displayId=" + displayId + ";source=" + sourceBounds); 969 } 970 synchronized (mLock) { 971 WindowMagnifier magnifier = mWindowMagnifiers.get(displayId); 972 if (magnifier == null) { 973 magnifier = createWindowMagnifier(displayId); 974 } 975 magnifier.onSourceBoundsChanged(sourceBounds); 976 } 977 mCallback.onSourceBoundsChanged(displayId, sourceBounds); 978 } 979 980 @Override onPerformScaleAction(int displayId, float scale, boolean updatePersistence)981 public void onPerformScaleAction(int displayId, float scale, boolean updatePersistence) { 982 if (mTrace.isA11yTracingEnabledForTypes( 983 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { 984 mTrace.logTrace(TAG + "ConnectionCallback.onPerformScaleAction", 985 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, 986 "displayId=" + displayId + ";scale=" + scale 987 + ";updatePersistence=" + updatePersistence); 988 } 989 mCallback.onPerformScaleAction(displayId, scale, updatePersistence); 990 } 991 992 @Override onAccessibilityActionPerformed(int displayId)993 public void onAccessibilityActionPerformed(int displayId) { 994 if (mTrace.isA11yTracingEnabledForTypes( 995 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { 996 mTrace.logTrace(TAG + "ConnectionCallback.onAccessibilityActionPerformed", 997 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, 998 "displayId=" + displayId); 999 } 1000 mCallback.onAccessibilityActionPerformed(displayId); 1001 } 1002 1003 @Override onMove(int displayId)1004 public void onMove(int displayId) { 1005 if (mTrace.isA11yTracingEnabledForTypes( 1006 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK)) { 1007 mTrace.logTrace(TAG + "ConnectionCallback.onMove", 1008 FLAGS_WINDOW_MAGNIFICATION_CONNECTION_CALLBACK, 1009 "displayId=" + displayId); 1010 } 1011 setTrackingTypingFocusEnabled(displayId, false); 1012 } 1013 1014 @Override binderDied()1015 public void binderDied() { 1016 synchronized (mLock) { 1017 Slog.w(TAG, "binderDied DeathRecipient :" + mExpiredDeathRecipient); 1018 if (mExpiredDeathRecipient) { 1019 return; 1020 } 1021 mConnectionWrapper.unlinkToDeath(this); 1022 mConnectionWrapper = null; 1023 mConnectionCallback = null; 1024 setConnectionState(DISCONNECTED); 1025 resetWindowMagnifiers(); 1026 } 1027 } 1028 } 1029 1030 /** 1031 * A class manipulates window magnification per display and contains the magnification 1032 * information. 1033 * <p> 1034 * This class requires to hold the lock when controlling the magnifier. 1035 * </p> 1036 */ 1037 private static class WindowMagnifier { 1038 1039 private final int mDisplayId; 1040 private float mScale = MagnificationScaleProvider.MIN_SCALE; 1041 private boolean mEnabled; 1042 1043 private final WindowMagnificationManager mWindowMagnificationManager; 1044 // Records the bounds of window magnification. 1045 private final Rect mBounds = new Rect(); 1046 // The magnified bounds on the screen. 1047 private final Rect mSourceBounds = new Rect(); 1048 1049 private int mIdOfLastServiceToControl = INVALID_SERVICE_ID; 1050 1051 private final PointF mMagnificationFrameOffsetRatio = new PointF(0f, 0f); 1052 1053 private boolean mTrackingTypingFocusEnabled = true; 1054 1055 private volatile long mTrackingTypingFocusStartTime = 0; 1056 private static final AtomicLongFieldUpdater<WindowMagnifier> SUM_TIME_UPDATER = 1057 AtomicLongFieldUpdater.newUpdater(WindowMagnifier.class, 1058 "mTrackingTypingFocusSumTime"); 1059 private volatile long mTrackingTypingFocusSumTime = 0; 1060 WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager)1061 WindowMagnifier(int displayId, WindowMagnificationManager windowMagnificationManager) { 1062 mDisplayId = displayId; 1063 mWindowMagnificationManager = windowMagnificationManager; 1064 } 1065 enableWindowMagnificationInternal(float scale, float centerX, float centerY, @Nullable MagnificationAnimationCallback animationCallback, @WindowPosition int windowPosition, int id)1066 boolean enableWindowMagnificationInternal(float scale, float centerX, float centerY, 1067 @Nullable MagnificationAnimationCallback animationCallback, 1068 @WindowPosition int windowPosition, int id) { 1069 // Handle defaults. The scale may be NAN when just updating magnification center. 1070 if (Float.isNaN(scale)) { 1071 scale = getScale(); 1072 } 1073 final float normScale = MagnificationScaleProvider.constrainScale(scale); 1074 setMagnificationFrameOffsetRatioByWindowPosition(windowPosition); 1075 if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale, 1076 centerX, centerY, mMagnificationFrameOffsetRatio.x, 1077 mMagnificationFrameOffsetRatio.y, animationCallback)) { 1078 mScale = normScale; 1079 mEnabled = true; 1080 mIdOfLastServiceToControl = id; 1081 return true; 1082 } 1083 return false; 1084 } 1085 setMagnificationFrameOffsetRatioByWindowPosition(@indowPosition int windowPosition)1086 void setMagnificationFrameOffsetRatioByWindowPosition(@WindowPosition int windowPosition) { 1087 switch (windowPosition) { 1088 case WINDOW_POSITION_AT_CENTER: { 1089 mMagnificationFrameOffsetRatio.set(0f, 0f); 1090 } 1091 break; 1092 case WINDOW_POSITION_AT_TOP_LEFT: { 1093 mMagnificationFrameOffsetRatio.set(-1f, -1f); 1094 } 1095 break; 1096 } 1097 } 1098 disableWindowMagnificationInternal( @ullable MagnificationAnimationCallback animationResultCallback)1099 boolean disableWindowMagnificationInternal( 1100 @Nullable MagnificationAnimationCallback animationResultCallback) { 1101 if (!mEnabled) { 1102 return false; 1103 } 1104 if (mWindowMagnificationManager.disableWindowMagnificationInternal( 1105 mDisplayId, animationResultCallback)) { 1106 mEnabled = false; 1107 mIdOfLastServiceToControl = INVALID_SERVICE_ID; 1108 mTrackingTypingFocusEnabled = false; 1109 pauseTrackingTypingFocusRecord(); 1110 return true; 1111 } 1112 return false; 1113 } 1114 1115 @GuardedBy("mLock") setScale(float scale)1116 void setScale(float scale) { 1117 if (!mEnabled) { 1118 return; 1119 } 1120 final float normScale = MagnificationScaleProvider.constrainScale(scale); 1121 if (Float.compare(mScale, normScale) != 0 1122 && mWindowMagnificationManager.setScaleInternal(mDisplayId, scale)) { 1123 mScale = normScale; 1124 } 1125 } 1126 1127 @GuardedBy("mLock") getScale()1128 float getScale() { 1129 return mScale; 1130 } 1131 1132 @GuardedBy("mLock") setMagnifierLocation(Rect rect)1133 void setMagnifierLocation(Rect rect) { 1134 mBounds.set(rect); 1135 } 1136 1137 /** 1138 * Returns the ID of the last service that changed the magnification config. 1139 */ getIdOfLastServiceToControl()1140 int getIdOfLastServiceToControl() { 1141 return mIdOfLastServiceToControl; 1142 } 1143 pointersInWindow(MotionEvent motionEvent)1144 int pointersInWindow(MotionEvent motionEvent) { 1145 int count = 0; 1146 final int pointerCount = motionEvent.getPointerCount(); 1147 for (int i = 0; i < pointerCount; i++) { 1148 final float x = motionEvent.getX(i); 1149 final float y = motionEvent.getY(i); 1150 if (mBounds.contains((int) x, (int) y)) { 1151 count++; 1152 } 1153 } 1154 return count; 1155 } 1156 isPositionInSourceBounds(float x, float y)1157 boolean isPositionInSourceBounds(float x, float y) { 1158 return mSourceBounds.contains((int) x, (int) y); 1159 } 1160 setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled)1161 void setTrackingTypingFocusEnabled(boolean trackingTypingFocusEnabled) { 1162 if (mWindowMagnificationManager.isWindowMagnifierEnabled(mDisplayId) 1163 && mWindowMagnificationManager.isImeVisible(mDisplayId) 1164 && trackingTypingFocusEnabled) { 1165 startTrackingTypingFocusRecord(); 1166 } 1167 if (mTrackingTypingFocusEnabled && !trackingTypingFocusEnabled) { 1168 stopAndLogTrackingTypingFocusRecordIfNeeded(); 1169 } 1170 mTrackingTypingFocusEnabled = trackingTypingFocusEnabled; 1171 } 1172 isTrackingTypingFocusEnabled()1173 boolean isTrackingTypingFocusEnabled() { 1174 return mTrackingTypingFocusEnabled; 1175 } 1176 startTrackingTypingFocusRecord()1177 void startTrackingTypingFocusRecord() { 1178 if (mTrackingTypingFocusStartTime == 0) { 1179 mTrackingTypingFocusStartTime = SystemClock.uptimeMillis(); 1180 if (DBG) { 1181 Slog.d(TAG, "start: mTrackingTypingFocusStartTime = " 1182 + mTrackingTypingFocusStartTime); 1183 } 1184 } 1185 } 1186 pauseTrackingTypingFocusRecord()1187 void pauseTrackingTypingFocusRecord() { 1188 if (mTrackingTypingFocusStartTime != 0) { 1189 final long elapsed = (SystemClock.uptimeMillis() - mTrackingTypingFocusStartTime); 1190 // update mTrackingTypingFocusSumTime value in an atomic operation 1191 SUM_TIME_UPDATER.addAndGet(this, elapsed); 1192 mTrackingTypingFocusStartTime = 0; 1193 if (DBG) { 1194 Slog.d(TAG, "pause: mTrackingTypingFocusSumTime = " 1195 + mTrackingTypingFocusSumTime + ", elapsed = " + elapsed); 1196 } 1197 } 1198 } 1199 stopAndLogTrackingTypingFocusRecordIfNeeded()1200 void stopAndLogTrackingTypingFocusRecordIfNeeded() { 1201 if (mTrackingTypingFocusStartTime != 0 || mTrackingTypingFocusSumTime != 0) { 1202 final long elapsed = mTrackingTypingFocusStartTime != 0 1203 ? (SystemClock.uptimeMillis() - mTrackingTypingFocusStartTime) : 0; 1204 final long duration = mTrackingTypingFocusSumTime + elapsed; 1205 if (DBG) { 1206 Slog.d(TAG, "stop and log: session duration = " + duration 1207 + ", elapsed = " + elapsed); 1208 } 1209 mWindowMagnificationManager.logTrackingTypingFocus(duration); 1210 mTrackingTypingFocusStartTime = 0; 1211 mTrackingTypingFocusSumTime = 0; 1212 } 1213 } 1214 isEnabled()1215 boolean isEnabled() { 1216 return mEnabled; 1217 } 1218 1219 @GuardedBy("mLock") move(float offsetX, float offsetY)1220 void move(float offsetX, float offsetY) { 1221 mWindowMagnificationManager.moveWindowMagnifierInternal(mDisplayId, offsetX, offsetY); 1222 } 1223 1224 @GuardedBy("mLock") reset()1225 void reset() { 1226 mEnabled = false; 1227 mIdOfLastServiceToControl = INVALID_SERVICE_ID; 1228 mSourceBounds.setEmpty(); 1229 } 1230 1231 @GuardedBy("mLock") onSourceBoundsChanged(Rect sourceBounds)1232 public void onSourceBoundsChanged(Rect sourceBounds) { 1233 mSourceBounds.set(sourceBounds); 1234 } 1235 1236 @GuardedBy("mLock") getCenterX()1237 float getCenterX() { 1238 return mSourceBounds.exactCenterX(); 1239 } 1240 1241 @GuardedBy("mLock") getCenterY()1242 float getCenterY() { 1243 return mSourceBounds.exactCenterY(); 1244 } 1245 } 1246 1247 @GuardedBy("mLock") enableWindowMagnificationInternal(int displayId, float scale, float centerX, float centerY, float magnificationFrameOffsetRatioX, float magnificationFrameOffsetRatioY, MagnificationAnimationCallback animationCallback)1248 private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX, 1249 float centerY, float magnificationFrameOffsetRatioX, 1250 float magnificationFrameOffsetRatioY, 1251 MagnificationAnimationCallback animationCallback) { 1252 // Wait for the connection with a timeout. 1253 final long endMillis = SystemClock.uptimeMillis() + WAIT_CONNECTION_TIMEOUT_MILLIS; 1254 while (mConnectionState == CONNECTING && (SystemClock.uptimeMillis() < endMillis)) { 1255 try { 1256 mLock.wait(endMillis - SystemClock.uptimeMillis()); 1257 } catch (InterruptedException ie) { 1258 /* ignore */ 1259 } 1260 } 1261 if (mConnectionWrapper == null) { 1262 Slog.w(TAG, 1263 "enableWindowMagnificationInternal mConnectionWrapper is null. " 1264 + "mConnectionState=" + connectionStateToString(mConnectionState)); 1265 return false; 1266 } 1267 return mConnectionWrapper.enableWindowMagnification( 1268 displayId, scale, centerX, centerY, 1269 magnificationFrameOffsetRatioX, magnificationFrameOffsetRatioY, 1270 animationCallback); 1271 } 1272 setScaleInternal(int displayId, float scale)1273 private boolean setScaleInternal(int displayId, float scale) { 1274 return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale); 1275 } 1276 1277 @GuardedBy("mLock") disableWindowMagnificationInternal(int displayId, MagnificationAnimationCallback animationCallback)1278 private boolean disableWindowMagnificationInternal(int displayId, 1279 MagnificationAnimationCallback animationCallback) { 1280 if (mConnectionWrapper == null) { 1281 Slog.w(TAG, "mConnectionWrapper is null"); 1282 return false; 1283 } 1284 return mConnectionWrapper.disableWindowMagnification( 1285 displayId, animationCallback); 1286 } 1287 1288 @GuardedBy("mLock") moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY)1289 private boolean moveWindowMagnifierInternal(int displayId, float offsetX, float offsetY) { 1290 return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifier( 1291 displayId, offsetX, offsetY); 1292 } 1293 1294 @GuardedBy("mLock") moveWindowMagnifierToPositionInternal(int displayId, float positionX, float positionY, MagnificationAnimationCallback animationCallback)1295 private boolean moveWindowMagnifierToPositionInternal(int displayId, float positionX, 1296 float positionY, MagnificationAnimationCallback animationCallback) { 1297 return mConnectionWrapper != null && mConnectionWrapper.moveWindowMagnifierToPosition( 1298 displayId, positionX, positionY, animationCallback); 1299 } 1300 } 1301