1 /* 2 * Copyright (C) 2015 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; 18 19 import android.animation.ValueAnimator; 20 import android.annotation.NonNull; 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.graphics.Rect; 26 import android.graphics.Region; 27 import android.os.AsyncTask; 28 import android.os.Handler; 29 import android.os.Message; 30 import android.provider.Settings; 31 import android.text.TextUtils; 32 import android.util.MathUtils; 33 import android.util.Slog; 34 import android.util.SparseArray; 35 import android.view.Display; 36 import android.view.MagnificationSpec; 37 import android.view.View; 38 import android.view.animation.DecelerateInterpolator; 39 40 import com.android.internal.R; 41 import com.android.internal.annotations.GuardedBy; 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.util.function.pooled.PooledLambda; 44 import com.android.server.LocalServices; 45 import com.android.server.wm.WindowManagerInternal; 46 47 import java.util.Locale; 48 49 /** 50 * This class is used to control and query the state of display magnification 51 * from the accessibility manager and related classes. It is responsible for 52 * holding the current state of magnification and animation, and it handles 53 * communication between the accessibility manager and window manager. 54 * 55 * Magnification is limited to the range [MIN_SCALE, MAX_SCALE], and can only occur inside the 56 * magnification region. If a value is out of bounds, it will be adjusted to guarantee these 57 * constraints. 58 */ 59 public class MagnificationController { 60 private static final boolean DEBUG = false; 61 private static final String LOG_TAG = "MagnificationController"; 62 63 public static final float MIN_SCALE = 1.0f; 64 public static final float MAX_SCALE = 8.0f; 65 66 private static final boolean DEBUG_SET_MAGNIFICATION_SPEC = false; 67 68 private static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; 69 70 private final Object mLock; 71 72 private final ControllerContext mControllerCtx; 73 74 private final ScreenStateObserver mScreenStateObserver; 75 76 private int mUserId; 77 78 private final long mMainThreadId; 79 80 /** List of display Magnification, mapping from displayId -> DisplayMagnification. */ 81 @GuardedBy("mLock") 82 private final SparseArray<DisplayMagnification> mDisplays = new SparseArray<>(0); 83 84 /** 85 * This class implements {@link WindowManagerInternal.MagnificationCallbacks} and holds 86 * magnification information per display. 87 */ 88 private final class DisplayMagnification implements 89 WindowManagerInternal.MagnificationCallbacks { 90 /** 91 * The current magnification spec. If an animation is running, this 92 * reflects the end state. 93 */ 94 private final MagnificationSpec mCurrentMagnificationSpec = MagnificationSpec.obtain(); 95 96 private final Region mMagnificationRegion = Region.obtain(); 97 private final Rect mMagnificationBounds = new Rect(); 98 99 private final Rect mTempRect = new Rect(); 100 private final Rect mTempRect1 = new Rect(); 101 102 private final SpecAnimationBridge mSpecAnimationBridge; 103 104 // Flag indicating that we are registered with window manager. 105 private boolean mRegistered; 106 private boolean mUnregisterPending; 107 private boolean mDeleteAfterUnregister; 108 109 private final int mDisplayId; 110 111 private static final int INVALID_ID = -1; 112 private int mIdOfLastServiceToMagnify = INVALID_ID; 113 DisplayMagnification(int displayId)114 DisplayMagnification(int displayId) { 115 mDisplayId = displayId; 116 mSpecAnimationBridge = new SpecAnimationBridge(mControllerCtx, mLock, mDisplayId); 117 } 118 119 /** 120 * Registers magnification callback and get current magnification region from 121 * window manager. 122 * 123 * @return true if callback registers successful. 124 */ 125 @GuardedBy("mLock") register()126 boolean register() { 127 mRegistered = mControllerCtx.getWindowManager().setMagnificationCallbacks( 128 mDisplayId, this); 129 if (!mRegistered) { 130 Slog.w(LOG_TAG, "set magnification callbacks fail, displayId:" + mDisplayId); 131 return false; 132 } 133 mSpecAnimationBridge.setEnabled(true); 134 // Obtain initial state. 135 mControllerCtx.getWindowManager().getMagnificationRegion( 136 mDisplayId, mMagnificationRegion); 137 mMagnificationRegion.getBounds(mMagnificationBounds); 138 return true; 139 } 140 141 /** 142 * Unregisters magnification callback from window manager. Callbacks to 143 * {@link MagnificationController#unregisterCallbackLocked(int, boolean)} after 144 * unregistered. 145 * 146 * @param delete true if this instance should be removed from the SparseArray in 147 * MagnificationController after unregistered, for example, display removed. 148 */ 149 @GuardedBy("mLock") unregister(boolean delete)150 void unregister(boolean delete) { 151 if (mRegistered) { 152 mSpecAnimationBridge.setEnabled(false); 153 mControllerCtx.getWindowManager().setMagnificationCallbacks( 154 mDisplayId, null); 155 mMagnificationRegion.setEmpty(); 156 mRegistered = false; 157 unregisterCallbackLocked(mDisplayId, delete); 158 } 159 mUnregisterPending = false; 160 } 161 162 /** 163 * Reset magnification status with animation enabled. {@link #unregister(boolean)} will be 164 * called after animation finished. 165 * 166 * @param delete true if this instance should be removed from the SparseArray in 167 * MagnificationController after unregistered, for example, display removed. 168 */ 169 @GuardedBy("mLock") unregisterPending(boolean delete)170 void unregisterPending(boolean delete) { 171 mDeleteAfterUnregister = delete; 172 mUnregisterPending = true; 173 reset(true); 174 } 175 isRegistered()176 boolean isRegistered() { 177 return mRegistered; 178 } 179 isMagnifying()180 boolean isMagnifying() { 181 return mCurrentMagnificationSpec.scale > 1.0f; 182 } 183 getScale()184 float getScale() { 185 return mCurrentMagnificationSpec.scale; 186 } 187 getOffsetX()188 float getOffsetX() { 189 return mCurrentMagnificationSpec.offsetX; 190 } 191 getOffsetY()192 float getOffsetY() { 193 return mCurrentMagnificationSpec.offsetY; 194 } 195 196 @GuardedBy("mLock") getCenterX()197 float getCenterX() { 198 return (mMagnificationBounds.width() / 2.0f 199 + mMagnificationBounds.left - getOffsetX()) / getScale(); 200 } 201 202 @GuardedBy("mLock") getCenterY()203 float getCenterY() { 204 return (mMagnificationBounds.height() / 2.0f 205 + mMagnificationBounds.top - getOffsetY()) / getScale(); 206 } 207 208 /** 209 * Returns the scale currently used by the window manager. If an 210 * animation is in progress, this reflects the current state of the 211 * animation. 212 * 213 * @return the scale currently used by the window manager 214 */ getSentScale()215 float getSentScale() { 216 return mSpecAnimationBridge.mSentMagnificationSpec.scale; 217 } 218 219 /** 220 * Returns the X offset currently used by the window manager. If an 221 * animation is in progress, this reflects the current state of the 222 * animation. 223 * 224 * @return the X offset currently used by the window manager 225 */ getSentOffsetX()226 float getSentOffsetX() { 227 return mSpecAnimationBridge.mSentMagnificationSpec.offsetX; 228 } 229 230 /** 231 * Returns the Y offset currently used by the window manager. If an 232 * animation is in progress, this reflects the current state of the 233 * animation. 234 * 235 * @return the Y offset currently used by the window manager 236 */ getSentOffsetY()237 float getSentOffsetY() { 238 return mSpecAnimationBridge.mSentMagnificationSpec.offsetY; 239 } 240 241 @Override onMagnificationRegionChanged(Region magnificationRegion)242 public void onMagnificationRegionChanged(Region magnificationRegion) { 243 final Message m = PooledLambda.obtainMessage( 244 DisplayMagnification::updateMagnificationRegion, this, 245 Region.obtain(magnificationRegion)); 246 mControllerCtx.getHandler().sendMessage(m); 247 } 248 249 @Override onRectangleOnScreenRequested(int left, int top, int right, int bottom)250 public void onRectangleOnScreenRequested(int left, int top, int right, int bottom) { 251 final Message m = PooledLambda.obtainMessage( 252 DisplayMagnification::requestRectangleOnScreen, this, 253 left, top, right, bottom); 254 mControllerCtx.getHandler().sendMessage(m); 255 } 256 257 @Override onRotationChanged(int rotation)258 public void onRotationChanged(int rotation) { 259 // Treat as context change and reset 260 final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded, 261 MagnificationController.this, mDisplayId, true); 262 mControllerCtx.getHandler().sendMessage(m); 263 } 264 265 @Override onUserContextChanged()266 public void onUserContextChanged() { 267 final Message m = PooledLambda.obtainMessage(MagnificationController::resetIfNeeded, 268 MagnificationController.this, mDisplayId, true); 269 mControllerCtx.getHandler().sendMessage(m); 270 } 271 272 /** 273 * Update our copy of the current magnification region 274 * 275 * @param magnified the magnified region 276 */ updateMagnificationRegion(Region magnified)277 void updateMagnificationRegion(Region magnified) { 278 synchronized (mLock) { 279 if (!mRegistered) { 280 // Don't update if we've unregistered 281 return; 282 } 283 if (!mMagnificationRegion.equals(magnified)) { 284 mMagnificationRegion.set(magnified); 285 mMagnificationRegion.getBounds(mMagnificationBounds); 286 // It's possible that our magnification spec is invalid with the new bounds. 287 // Adjust the current spec's offsets if necessary. 288 if (updateCurrentSpecWithOffsetsLocked( 289 mCurrentMagnificationSpec.offsetX, mCurrentMagnificationSpec.offsetY)) { 290 sendSpecToAnimation(mCurrentMagnificationSpec, false); 291 } 292 onMagnificationChangedLocked(); 293 } 294 magnified.recycle(); 295 } 296 } 297 sendSpecToAnimation(MagnificationSpec spec, boolean animate)298 void sendSpecToAnimation(MagnificationSpec spec, boolean animate) { 299 if (DEBUG) { 300 Slog.i(LOG_TAG, 301 "sendSpecToAnimation(spec = " + spec + ", animate = " + animate + ")"); 302 } 303 if (Thread.currentThread().getId() == mMainThreadId) { 304 mSpecAnimationBridge.updateSentSpecMainThread(spec, animate); 305 } else { 306 final Message m = PooledLambda.obtainMessage( 307 SpecAnimationBridge::updateSentSpecMainThread, 308 mSpecAnimationBridge, spec, animate); 309 mControllerCtx.getHandler().sendMessage(m); 310 } 311 } 312 313 /** 314 * Get the ID of the last service that changed the magnification spec. 315 * 316 * @return The id 317 */ getIdOfLastServiceToMagnify()318 int getIdOfLastServiceToMagnify() { 319 return mIdOfLastServiceToMagnify; 320 } 321 onMagnificationChangedLocked()322 void onMagnificationChangedLocked() { 323 mControllerCtx.getAms().notifyMagnificationChanged(mDisplayId, mMagnificationRegion, 324 getScale(), getCenterX(), getCenterY()); 325 if (mUnregisterPending && !isMagnifying()) { 326 unregister(mDeleteAfterUnregister); 327 } 328 } 329 330 @GuardedBy("mLock") magnificationRegionContains(float x, float y)331 boolean magnificationRegionContains(float x, float y) { 332 return mMagnificationRegion.contains((int) x, (int) y); 333 } 334 335 @GuardedBy("mLock") getMagnificationBounds(@onNull Rect outBounds)336 void getMagnificationBounds(@NonNull Rect outBounds) { 337 outBounds.set(mMagnificationBounds); 338 } 339 340 @GuardedBy("mLock") getMagnificationRegion(@onNull Region outRegion)341 void getMagnificationRegion(@NonNull Region outRegion) { 342 outRegion.set(mMagnificationRegion); 343 } 344 requestRectangleOnScreen(int left, int top, int right, int bottom)345 void requestRectangleOnScreen(int left, int top, int right, int bottom) { 346 synchronized (mLock) { 347 final Rect magnifiedFrame = mTempRect; 348 getMagnificationBounds(magnifiedFrame); 349 if (!magnifiedFrame.intersects(left, top, right, bottom)) { 350 return; 351 } 352 353 final Rect magnifFrameInScreenCoords = mTempRect1; 354 getMagnifiedFrameInContentCoordsLocked(magnifFrameInScreenCoords); 355 356 final float scrollX; 357 final float scrollY; 358 if (right - left > magnifFrameInScreenCoords.width()) { 359 final int direction = TextUtils 360 .getLayoutDirectionFromLocale(Locale.getDefault()); 361 if (direction == View.LAYOUT_DIRECTION_LTR) { 362 scrollX = left - magnifFrameInScreenCoords.left; 363 } else { 364 scrollX = right - magnifFrameInScreenCoords.right; 365 } 366 } else if (left < magnifFrameInScreenCoords.left) { 367 scrollX = left - magnifFrameInScreenCoords.left; 368 } else if (right > magnifFrameInScreenCoords.right) { 369 scrollX = right - magnifFrameInScreenCoords.right; 370 } else { 371 scrollX = 0; 372 } 373 374 if (bottom - top > magnifFrameInScreenCoords.height()) { 375 scrollY = top - magnifFrameInScreenCoords.top; 376 } else if (top < magnifFrameInScreenCoords.top) { 377 scrollY = top - magnifFrameInScreenCoords.top; 378 } else if (bottom > magnifFrameInScreenCoords.bottom) { 379 scrollY = bottom - magnifFrameInScreenCoords.bottom; 380 } else { 381 scrollY = 0; 382 } 383 384 final float scale = getScale(); 385 offsetMagnifiedRegion(scrollX * scale, scrollY * scale, INVALID_ID); 386 } 387 } 388 getMagnifiedFrameInContentCoordsLocked(Rect outFrame)389 void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) { 390 final float scale = getSentScale(); 391 final float offsetX = getSentOffsetX(); 392 final float offsetY = getSentOffsetY(); 393 getMagnificationBounds(outFrame); 394 outFrame.offset((int) -offsetX, (int) -offsetY); 395 outFrame.scale(1.0f / scale); 396 } 397 398 @GuardedBy("mLock") setForceShowMagnifiableBounds(boolean show)399 void setForceShowMagnifiableBounds(boolean show) { 400 if (mRegistered) { 401 mControllerCtx.getWindowManager().setForceShowMagnifiableBounds( 402 mDisplayId, show); 403 } 404 } 405 406 @GuardedBy("mLock") reset(boolean animate)407 boolean reset(boolean animate) { 408 if (!mRegistered) { 409 return false; 410 } 411 final MagnificationSpec spec = mCurrentMagnificationSpec; 412 final boolean changed = !spec.isNop(); 413 if (changed) { 414 spec.clear(); 415 onMagnificationChangedLocked(); 416 } 417 mIdOfLastServiceToMagnify = INVALID_ID; 418 sendSpecToAnimation(spec, animate); 419 return changed; 420 } 421 422 @GuardedBy("mLock") setScale(float scale, float pivotX, float pivotY, boolean animate, int id)423 boolean setScale(float scale, float pivotX, float pivotY, 424 boolean animate, int id) { 425 if (!mRegistered) { 426 return false; 427 } 428 // Constrain scale immediately for use in the pivot calculations. 429 scale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); 430 431 final Rect viewport = mTempRect; 432 mMagnificationRegion.getBounds(viewport); 433 final MagnificationSpec spec = mCurrentMagnificationSpec; 434 final float oldScale = spec.scale; 435 final float oldCenterX = 436 (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale; 437 final float oldCenterY = 438 (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale; 439 final float normPivotX = (pivotX - spec.offsetX) / oldScale; 440 final float normPivotY = (pivotY - spec.offsetY) / oldScale; 441 final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale); 442 final float offsetY = (oldCenterY - normPivotY) * (oldScale / scale); 443 final float centerX = normPivotX + offsetX; 444 final float centerY = normPivotY + offsetY; 445 mIdOfLastServiceToMagnify = id; 446 return setScaleAndCenter(scale, centerX, centerY, animate, id); 447 } 448 449 @GuardedBy("mLock") setScaleAndCenter(float scale, float centerX, float centerY, boolean animate, int id)450 boolean setScaleAndCenter(float scale, float centerX, float centerY, 451 boolean animate, int id) { 452 if (!mRegistered) { 453 return false; 454 } 455 if (DEBUG) { 456 Slog.i(LOG_TAG, 457 "setScaleAndCenterLocked(scale = " + scale + ", centerX = " + centerX 458 + ", centerY = " + centerY + ", animate = " + animate 459 + ", id = " + id 460 + ")"); 461 } 462 final boolean changed = updateMagnificationSpecLocked(scale, centerX, centerY); 463 sendSpecToAnimation(mCurrentMagnificationSpec, animate); 464 if (isMagnifying() && (id != INVALID_ID)) { 465 mIdOfLastServiceToMagnify = id; 466 } 467 return changed; 468 } 469 470 /** 471 * Updates the current magnification spec. 472 * 473 * @param scale the magnification scale 474 * @param centerX the unscaled, screen-relative X coordinate of the center 475 * of the viewport, or {@link Float#NaN} to leave unchanged 476 * @param centerY the unscaled, screen-relative Y coordinate of the center 477 * of the viewport, or {@link Float#NaN} to leave unchanged 478 * @return {@code true} if the magnification spec changed or {@code false} 479 * otherwise 480 */ updateMagnificationSpecLocked(float scale, float centerX, float centerY)481 boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) { 482 // Handle defaults. 483 if (Float.isNaN(centerX)) { 484 centerX = getCenterX(); 485 } 486 if (Float.isNaN(centerY)) { 487 centerY = getCenterY(); 488 } 489 if (Float.isNaN(scale)) { 490 scale = getScale(); 491 } 492 493 // Compute changes. 494 boolean changed = false; 495 496 final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); 497 if (Float.compare(mCurrentMagnificationSpec.scale, normScale) != 0) { 498 mCurrentMagnificationSpec.scale = normScale; 499 changed = true; 500 } 501 502 final float nonNormOffsetX = mMagnificationBounds.width() / 2.0f 503 + mMagnificationBounds.left - centerX * normScale; 504 final float nonNormOffsetY = mMagnificationBounds.height() / 2.0f 505 + mMagnificationBounds.top - centerY * normScale; 506 changed |= updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY); 507 508 if (changed) { 509 onMagnificationChangedLocked(); 510 } 511 512 return changed; 513 } 514 515 @GuardedBy("mLock") offsetMagnifiedRegion(float offsetX, float offsetY, int id)516 void offsetMagnifiedRegion(float offsetX, float offsetY, int id) { 517 if (!mRegistered) { 518 return; 519 } 520 521 final float nonNormOffsetX = mCurrentMagnificationSpec.offsetX - offsetX; 522 final float nonNormOffsetY = mCurrentMagnificationSpec.offsetY - offsetY; 523 if (updateCurrentSpecWithOffsetsLocked(nonNormOffsetX, nonNormOffsetY)) { 524 onMagnificationChangedLocked(); 525 } 526 if (id != INVALID_ID) { 527 mIdOfLastServiceToMagnify = id; 528 } 529 sendSpecToAnimation(mCurrentMagnificationSpec, false); 530 } 531 updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY)532 boolean updateCurrentSpecWithOffsetsLocked(float nonNormOffsetX, float nonNormOffsetY) { 533 if (DEBUG) { 534 Slog.i(LOG_TAG, 535 "updateCurrentSpecWithOffsetsLocked(nonNormOffsetX = " + nonNormOffsetX 536 + ", nonNormOffsetY = " + nonNormOffsetY + ")"); 537 } 538 boolean changed = false; 539 final float offsetX = MathUtils.constrain( 540 nonNormOffsetX, getMinOffsetXLocked(), getMaxOffsetXLocked()); 541 if (Float.compare(mCurrentMagnificationSpec.offsetX, offsetX) != 0) { 542 mCurrentMagnificationSpec.offsetX = offsetX; 543 changed = true; 544 } 545 final float offsetY = MathUtils.constrain( 546 nonNormOffsetY, getMinOffsetYLocked(), getMaxOffsetYLocked()); 547 if (Float.compare(mCurrentMagnificationSpec.offsetY, offsetY) != 0) { 548 mCurrentMagnificationSpec.offsetY = offsetY; 549 changed = true; 550 } 551 return changed; 552 } 553 getMinOffsetXLocked()554 float getMinOffsetXLocked() { 555 final float viewportWidth = mMagnificationBounds.width(); 556 final float viewportLeft = mMagnificationBounds.left; 557 return (viewportLeft + viewportWidth) - 558 (viewportLeft + viewportWidth) * mCurrentMagnificationSpec.scale; 559 } 560 getMaxOffsetXLocked()561 float getMaxOffsetXLocked() { 562 return mMagnificationBounds.left - 563 mMagnificationBounds.left * mCurrentMagnificationSpec.scale; 564 } 565 getMinOffsetYLocked()566 float getMinOffsetYLocked() { 567 final float viewportHeight = mMagnificationBounds.height(); 568 final float viewportTop = mMagnificationBounds.top; 569 return (viewportTop + viewportHeight) - 570 (viewportTop + viewportHeight) * mCurrentMagnificationSpec.scale; 571 } 572 getMaxOffsetYLocked()573 float getMaxOffsetYLocked() { 574 return mMagnificationBounds.top - 575 mMagnificationBounds.top * mCurrentMagnificationSpec.scale; 576 } 577 578 @Override toString()579 public String toString() { 580 return "DisplayMagnification[" 581 + "mCurrentMagnificationSpec=" + mCurrentMagnificationSpec 582 + ", mMagnificationRegion=" + mMagnificationRegion 583 + ", mMagnificationBounds=" + mMagnificationBounds 584 + ", mDisplayId=" + mDisplayId 585 + ", mIdOfLastServiceToMagnify=" + mIdOfLastServiceToMagnify 586 + ", mRegistered=" + mRegistered 587 + ", mUnregisterPending=" + mUnregisterPending 588 + ']'; 589 } 590 } 591 592 /** 593 * MagnificationController Constructor 594 */ MagnificationController(@onNull Context context, @NonNull AccessibilityManagerService ams, @NonNull Object lock)595 public MagnificationController(@NonNull Context context, 596 @NonNull AccessibilityManagerService ams, @NonNull Object lock) { 597 this(new ControllerContext(context, ams, 598 LocalServices.getService(WindowManagerInternal.class), 599 new Handler(context.getMainLooper()), 600 context.getResources().getInteger(R.integer.config_longAnimTime)), lock); 601 } 602 603 /** 604 * Constructor for tests 605 */ 606 @VisibleForTesting MagnificationController(@onNull ControllerContext ctx, @NonNull Object lock)607 public MagnificationController(@NonNull ControllerContext ctx, @NonNull Object lock) { 608 mControllerCtx = ctx; 609 mLock = lock; 610 mMainThreadId = mControllerCtx.getContext().getMainLooper().getThread().getId(); 611 mScreenStateObserver = new ScreenStateObserver(mControllerCtx.getContext(), this); 612 } 613 614 /** 615 * Start tracking the magnification region for services that control magnification and the 616 * magnification gesture handler. 617 * 618 * This tracking imposes a cost on the system, so we avoid tracking this data unless it's 619 * required. 620 * 621 * @param displayId The logical display id. 622 */ register(int displayId)623 public void register(int displayId) { 624 synchronized (mLock) { 625 DisplayMagnification display = mDisplays.get(displayId); 626 if (display == null) { 627 display = new DisplayMagnification(displayId); 628 } 629 if (display.isRegistered()) { 630 return; 631 } 632 if (display.register()) { 633 mDisplays.put(displayId, display); 634 mScreenStateObserver.registerIfNecessary(); 635 } 636 } 637 } 638 639 /** 640 * Stop requiring tracking the magnification region. We may remain registered while we 641 * reset magnification. 642 * 643 * @param displayId The logical display id. 644 */ unregister(int displayId)645 public void unregister(int displayId) { 646 synchronized (mLock) { 647 unregisterLocked(displayId, false); 648 } 649 } 650 651 /** 652 * Stop tracking all displays' magnification region. 653 */ unregisterAll()654 public void unregisterAll() { 655 synchronized (mLock) { 656 // display will be removed from array after unregister, we need to clone it to 657 // prevent error. 658 final SparseArray<DisplayMagnification> displays = mDisplays.clone(); 659 for (int i = 0; i < displays.size(); i++) { 660 unregisterLocked(displays.keyAt(i), false); 661 } 662 } 663 } 664 665 /** 666 * Remove the display magnification with given id. 667 * 668 * @param displayId The logical display id. 669 */ onDisplayRemoved(int displayId)670 public void onDisplayRemoved(int displayId) { 671 synchronized (mLock) { 672 unregisterLocked(displayId, true); 673 } 674 } 675 676 /** 677 * Check if we are registered on specified display. Note that we may be planning to unregister 678 * at any moment. 679 * 680 * @return {@code true} if the controller is registered on specified display. 681 * {@code false} otherwise. 682 * 683 * @param displayId The logical display id. 684 */ isRegistered(int displayId)685 public boolean isRegistered(int displayId) { 686 synchronized (mLock) { 687 final DisplayMagnification display = mDisplays.get(displayId); 688 if (display == null) { 689 return false; 690 } 691 return display.isRegistered(); 692 } 693 } 694 695 /** 696 * @param displayId The logical display id. 697 * @return {@code true} if magnification is active, e.g. the scale 698 * is > 1, {@code false} otherwise 699 */ isMagnifying(int displayId)700 public boolean isMagnifying(int displayId) { 701 synchronized (mLock) { 702 final DisplayMagnification display = mDisplays.get(displayId); 703 if (display == null) { 704 return false; 705 } 706 return display.isMagnifying(); 707 } 708 } 709 710 /** 711 * Returns whether the magnification region contains the specified 712 * screen-relative coordinates. 713 * 714 * @param displayId The logical display id. 715 * @param x the screen-relative X coordinate to check 716 * @param y the screen-relative Y coordinate to check 717 * @return {@code true} if the coordinate is contained within the 718 * magnified region, or {@code false} otherwise 719 */ magnificationRegionContains(int displayId, float x, float y)720 public boolean magnificationRegionContains(int displayId, float x, float y) { 721 synchronized (mLock) { 722 final DisplayMagnification display = mDisplays.get(displayId); 723 if (display == null) { 724 return false; 725 } 726 return display.magnificationRegionContains(x, y); 727 } 728 } 729 730 /** 731 * Populates the specified rect with the screen-relative bounds of the 732 * magnification region. If magnification is not enabled, the returned 733 * bounds will be empty. 734 * 735 * @param displayId The logical display id. 736 * @param outBounds rect to populate with the bounds of the magnified 737 * region 738 */ getMagnificationBounds(int displayId, @NonNull Rect outBounds)739 public void getMagnificationBounds(int displayId, @NonNull Rect outBounds) { 740 synchronized (mLock) { 741 final DisplayMagnification display = mDisplays.get(displayId); 742 if (display == null) { 743 return; 744 } 745 display.getMagnificationBounds(outBounds); 746 } 747 } 748 749 /** 750 * Populates the specified region with the screen-relative magnification 751 * region. If magnification is not enabled, then the returned region 752 * will be empty. 753 * 754 * @param displayId The logical display id. 755 * @param outRegion the region to populate 756 */ getMagnificationRegion(int displayId, @NonNull Region outRegion)757 public void getMagnificationRegion(int displayId, @NonNull Region outRegion) { 758 synchronized (mLock) { 759 final DisplayMagnification display = mDisplays.get(displayId); 760 if (display == null) { 761 return; 762 } 763 display.getMagnificationRegion(outRegion); 764 } 765 } 766 767 /** 768 * Returns the magnification scale. If an animation is in progress, 769 * this reflects the end state of the animation. 770 * 771 * @param displayId The logical display id. 772 * @return the scale 773 */ getScale(int displayId)774 public float getScale(int displayId) { 775 synchronized (mLock) { 776 final DisplayMagnification display = mDisplays.get(displayId); 777 if (display == null) { 778 return 1.0f; 779 } 780 return display.getScale(); 781 } 782 } 783 784 /** 785 * Returns the X offset of the magnification viewport. If an animation 786 * is in progress, this reflects the end state of the animation. 787 * 788 * @param displayId The logical display id. 789 * @return the X offset 790 */ getOffsetX(int displayId)791 public float getOffsetX(int displayId) { 792 synchronized (mLock) { 793 final DisplayMagnification display = mDisplays.get(displayId); 794 if (display == null) { 795 return 0.0f; 796 } 797 return display.getOffsetX(); 798 } 799 } 800 801 /** 802 * Returns the screen-relative X coordinate of the center of the 803 * magnification viewport. 804 * 805 * @param displayId The logical display id. 806 * @return the X coordinate 807 */ getCenterX(int displayId)808 public float getCenterX(int displayId) { 809 synchronized (mLock) { 810 final DisplayMagnification display = mDisplays.get(displayId); 811 if (display == null) { 812 return 0.0f; 813 } 814 return display.getCenterX(); 815 } 816 } 817 818 /** 819 * Returns the Y offset of the magnification viewport. If an animation 820 * is in progress, this reflects the end state of the animation. 821 * 822 * @param displayId The logical display id. 823 * @return the Y offset 824 */ getOffsetY(int displayId)825 public float getOffsetY(int displayId) { 826 synchronized (mLock) { 827 final DisplayMagnification display = mDisplays.get(displayId); 828 if (display == null) { 829 return 0.0f; 830 } 831 return display.getOffsetY(); 832 } 833 } 834 835 /** 836 * Returns the screen-relative Y coordinate of the center of the 837 * magnification viewport. 838 * 839 * @param displayId The logical display id. 840 * @return the Y coordinate 841 */ getCenterY(int displayId)842 public float getCenterY(int displayId) { 843 synchronized (mLock) { 844 final DisplayMagnification display = mDisplays.get(displayId); 845 if (display == null) { 846 return 0.0f; 847 } 848 return display.getCenterY(); 849 } 850 } 851 852 /** 853 * Resets the magnification scale and center, optionally animating the 854 * transition. 855 * 856 * @param displayId The logical display id. 857 * @param animate {@code true} to animate the transition, {@code false} 858 * to transition immediately 859 * @return {@code true} if the magnification spec changed, {@code false} if 860 * the spec did not change 861 */ reset(int displayId, boolean animate)862 public boolean reset(int displayId, boolean animate) { 863 synchronized (mLock) { 864 final DisplayMagnification display = mDisplays.get(displayId); 865 if (display == null) { 866 return false; 867 } 868 return display.reset(animate); 869 } 870 } 871 872 /** 873 * Scales the magnified region around the specified pivot point, 874 * optionally animating the transition. If animation is disabled, the 875 * transition is immediate. 876 * 877 * @param displayId The logical display id. 878 * @param scale the target scale, must be >= 1 879 * @param pivotX the screen-relative X coordinate around which to scale 880 * @param pivotY the screen-relative Y coordinate around which to scale 881 * @param animate {@code true} to animate the transition, {@code false} 882 * to transition immediately 883 * @param id the ID of the service requesting the change 884 * @return {@code true} if the magnification spec changed, {@code false} if 885 * the spec did not change 886 */ setScale(int displayId, float scale, float pivotX, float pivotY, boolean animate, int id)887 public boolean setScale(int displayId, float scale, float pivotX, float pivotY, 888 boolean animate, int id) { 889 synchronized (mLock) { 890 final DisplayMagnification display = mDisplays.get(displayId); 891 if (display == null) { 892 return false; 893 } 894 return display.setScale(scale, pivotX, pivotY, animate, id); 895 } 896 } 897 898 /** 899 * Sets the center of the magnified region, optionally animating the 900 * transition. If animation is disabled, the transition is immediate. 901 * 902 * @param displayId The logical display id. 903 * @param centerX the screen-relative X coordinate around which to 904 * center 905 * @param centerY the screen-relative Y coordinate around which to 906 * center 907 * @param animate {@code true} to animate the transition, {@code false} 908 * to transition immediately 909 * @param id the ID of the service requesting the change 910 * @return {@code true} if the magnification spec changed, {@code false} if 911 * the spec did not change 912 */ setCenter(int displayId, float centerX, float centerY, boolean animate, int id)913 public boolean setCenter(int displayId, float centerX, float centerY, boolean animate, int id) { 914 synchronized (mLock) { 915 final DisplayMagnification display = mDisplays.get(displayId); 916 if (display == null) { 917 return false; 918 } 919 return display.setScaleAndCenter(Float.NaN, centerX, centerY, animate, id); 920 } 921 } 922 923 /** 924 * Sets the scale and center of the magnified region, optionally 925 * animating the transition. If animation is disabled, the transition 926 * is immediate. 927 * 928 * @param displayId The logical display id. 929 * @param scale the target scale, or {@link Float#NaN} to leave unchanged 930 * @param centerX the screen-relative X coordinate around which to 931 * center and scale, or {@link Float#NaN} to leave unchanged 932 * @param centerY the screen-relative Y coordinate around which to 933 * center and scale, or {@link Float#NaN} to leave unchanged 934 * @param animate {@code true} to animate the transition, {@code false} 935 * to transition immediately 936 * @param id the ID of the service requesting the change 937 * @return {@code true} if the magnification spec changed, {@code false} if 938 * the spec did not change 939 */ setScaleAndCenter(int displayId, float scale, float centerX, float centerY, boolean animate, int id)940 public boolean setScaleAndCenter(int displayId, float scale, float centerX, float centerY, 941 boolean animate, int id) { 942 synchronized (mLock) { 943 final DisplayMagnification display = mDisplays.get(displayId); 944 if (display == null) { 945 return false; 946 } 947 return display.setScaleAndCenter(scale, centerX, centerY, animate, id); 948 } 949 } 950 951 /** 952 * Offsets the magnified region. Note that the offsetX and offsetY values actually move in the 953 * opposite direction as the offsets passed in here. 954 * 955 * @param displayId The logical display id. 956 * @param offsetX the amount in pixels to offset the region in the X direction, in current 957 * screen pixels. 958 * @param offsetY the amount in pixels to offset the region in the Y direction, in current 959 * screen pixels. 960 * @param id the ID of the service requesting the change 961 */ offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id)962 public void offsetMagnifiedRegion(int displayId, float offsetX, float offsetY, int id) { 963 synchronized (mLock) { 964 final DisplayMagnification display = mDisplays.get(displayId); 965 if (display == null) { 966 return; 967 } 968 display.offsetMagnifiedRegion(offsetX, offsetY, id); 969 } 970 } 971 972 /** 973 * Get the ID of the last service that changed the magnification spec. 974 * 975 * @param displayId The logical display id. 976 * @return The id 977 */ getIdOfLastServiceToMagnify(int displayId)978 public int getIdOfLastServiceToMagnify(int displayId) { 979 synchronized (mLock) { 980 final DisplayMagnification display = mDisplays.get(displayId); 981 if (display == null) { 982 return -1; 983 } 984 return display.getIdOfLastServiceToMagnify(); 985 } 986 } 987 988 /** 989 * Persists the default display magnification scale to the current user's settings. 990 */ persistScale()991 public void persistScale() { 992 // TODO: b/123047354, Need support multi-display? 993 final float scale = getScale(Display.DEFAULT_DISPLAY); 994 final int userId = mUserId; 995 996 new AsyncTask<Void, Void, Void>() { 997 @Override 998 protected Void doInBackground(Void... params) { 999 mControllerCtx.putMagnificationScale(scale, userId); 1000 return null; 1001 } 1002 }.execute(); 1003 } 1004 1005 /** 1006 * Retrieves a previously persisted magnification scale from the current 1007 * user's settings. 1008 * 1009 * @return the previously persisted magnification scale, or the default 1010 * scale if none is available 1011 */ getPersistedScale()1012 public float getPersistedScale() { 1013 return mControllerCtx.getMagnificationScale(mUserId); 1014 } 1015 1016 /** 1017 * Sets the currently active user ID. 1018 * 1019 * @param userId the currently active user ID 1020 */ setUserId(int userId)1021 public void setUserId(int userId) { 1022 if (mUserId == userId) { 1023 return; 1024 } 1025 mUserId = userId; 1026 resetAllIfNeeded(false); 1027 } 1028 1029 /** 1030 * Resets all displays' magnification if last magnifying service is disabled. 1031 * 1032 * @param connectionId 1033 */ resetAllIfNeeded(int connectionId)1034 public void resetAllIfNeeded(int connectionId) { 1035 synchronized (mLock) { 1036 for (int i = 0; i < mDisplays.size(); i++) { 1037 resetIfNeeded(mDisplays.keyAt(i), connectionId); 1038 } 1039 } 1040 } 1041 1042 /** 1043 * Resets magnification if magnification and auto-update are both enabled. 1044 * 1045 * @param displayId The logical display id. 1046 * @param animate whether the animate the transition 1047 * @return whether was {@link #isMagnifying(int) magnifying} 1048 */ resetIfNeeded(int displayId, boolean animate)1049 boolean resetIfNeeded(int displayId, boolean animate) { 1050 synchronized (mLock) { 1051 final DisplayMagnification display = mDisplays.get(displayId); 1052 if (display == null || !display.isMagnifying()) { 1053 return false; 1054 } 1055 display.reset(animate); 1056 return true; 1057 } 1058 } 1059 1060 /** 1061 * Resets magnification if last magnifying service is disabled. 1062 * 1063 * @param displayId The logical display id. 1064 * @param connectionId the connection ID be disabled. 1065 * @return {@code true} on success, {@code false} on failure 1066 */ resetIfNeeded(int displayId, int connectionId)1067 boolean resetIfNeeded(int displayId, int connectionId) { 1068 synchronized (mLock) { 1069 final DisplayMagnification display = mDisplays.get(displayId); 1070 if (display == null || !display.isMagnifying() 1071 || connectionId != display.getIdOfLastServiceToMagnify()) { 1072 return false; 1073 } 1074 display.reset(true); 1075 return true; 1076 } 1077 } 1078 setForceShowMagnifiableBounds(int displayId, boolean show)1079 void setForceShowMagnifiableBounds(int displayId, boolean show) { 1080 synchronized (mLock) { 1081 final DisplayMagnification display = mDisplays.get(displayId); 1082 if (display == null) { 1083 return; 1084 } 1085 display.setForceShowMagnifiableBounds(show); 1086 } 1087 } 1088 onScreenTurnedOff()1089 private void onScreenTurnedOff() { 1090 final Message m = PooledLambda.obtainMessage( 1091 MagnificationController::resetAllIfNeeded, this, false); 1092 mControllerCtx.getHandler().sendMessage(m); 1093 } 1094 resetAllIfNeeded(boolean animate)1095 private void resetAllIfNeeded(boolean animate) { 1096 synchronized (mLock) { 1097 for (int i = 0; i < mDisplays.size(); i++) { 1098 resetIfNeeded(mDisplays.keyAt(i), animate); 1099 } 1100 } 1101 } 1102 unregisterLocked(int displayId, boolean delete)1103 private void unregisterLocked(int displayId, boolean delete) { 1104 final DisplayMagnification display = mDisplays.get(displayId); 1105 if (display == null) { 1106 return; 1107 } 1108 if (!display.isRegistered()) { 1109 if (delete) { 1110 mDisplays.remove(displayId); 1111 } 1112 return; 1113 } 1114 if (!display.isMagnifying()) { 1115 display.unregister(delete); 1116 } else { 1117 display.unregisterPending(delete); 1118 } 1119 } 1120 1121 /** 1122 * Callbacks from DisplayMagnification after display magnification unregistered. It will remove 1123 * DisplayMagnification instance if delete is true, and unregister screen state if 1124 * there is no registered display magnification. 1125 */ unregisterCallbackLocked(int displayId, boolean delete)1126 private void unregisterCallbackLocked(int displayId, boolean delete) { 1127 if (delete) { 1128 mDisplays.remove(displayId); 1129 } 1130 // unregister screen state if necessary 1131 boolean hasRegister = false; 1132 for (int i = 0; i < mDisplays.size(); i++) { 1133 final DisplayMagnification display = mDisplays.valueAt(i); 1134 hasRegister = display.isRegistered(); 1135 if (hasRegister) { 1136 break; 1137 } 1138 } 1139 if (!hasRegister) { 1140 mScreenStateObserver.unregister(); 1141 } 1142 } 1143 1144 @Override toString()1145 public String toString() { 1146 StringBuilder builder = new StringBuilder(); 1147 builder.append("MagnificationController["); 1148 builder.append("mUserId=").append(mUserId); 1149 builder.append(", mDisplays=").append(mDisplays); 1150 builder.append("]"); 1151 return builder.toString(); 1152 } 1153 1154 /** 1155 * Class responsible for animating spec on the main thread and sending spec 1156 * updates to the window manager. 1157 */ 1158 private static class SpecAnimationBridge implements ValueAnimator.AnimatorUpdateListener { 1159 private final ControllerContext mControllerCtx; 1160 1161 /** 1162 * The magnification spec that was sent to the window manager. This should 1163 * only be accessed with the lock held. 1164 */ 1165 private final MagnificationSpec mSentMagnificationSpec = MagnificationSpec.obtain(); 1166 1167 private final MagnificationSpec mStartMagnificationSpec = MagnificationSpec.obtain(); 1168 1169 private final MagnificationSpec mEndMagnificationSpec = MagnificationSpec.obtain(); 1170 1171 private final MagnificationSpec mTmpMagnificationSpec = MagnificationSpec.obtain(); 1172 1173 /** 1174 * The animator should only be accessed and modified on the main (e.g. animation) thread. 1175 */ 1176 private final ValueAnimator mValueAnimator; 1177 1178 private final Object mLock; 1179 1180 private final int mDisplayId; 1181 1182 @GuardedBy("mLock") 1183 private boolean mEnabled = false; 1184 SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId)1185 private SpecAnimationBridge(ControllerContext ctx, Object lock, int displayId) { 1186 mControllerCtx = ctx; 1187 mLock = lock; 1188 mDisplayId = displayId; 1189 final long animationDuration = mControllerCtx.getAnimationDuration(); 1190 mValueAnimator = mControllerCtx.newValueAnimator(); 1191 mValueAnimator.setDuration(animationDuration); 1192 mValueAnimator.setInterpolator(new DecelerateInterpolator(2.5f)); 1193 mValueAnimator.setFloatValues(0.0f, 1.0f); 1194 mValueAnimator.addUpdateListener(this); 1195 } 1196 1197 /** 1198 * Enabled means the bridge will accept input. When not enabled, the output of the animator 1199 * will be ignored 1200 */ setEnabled(boolean enabled)1201 public void setEnabled(boolean enabled) { 1202 synchronized (mLock) { 1203 if (enabled != mEnabled) { 1204 mEnabled = enabled; 1205 if (!mEnabled) { 1206 mSentMagnificationSpec.clear(); 1207 mControllerCtx.getWindowManager().setMagnificationSpec( 1208 mDisplayId, mSentMagnificationSpec); 1209 } 1210 } 1211 } 1212 } 1213 updateSentSpecMainThread(MagnificationSpec spec, boolean animate)1214 public void updateSentSpecMainThread(MagnificationSpec spec, boolean animate) { 1215 if (mValueAnimator.isRunning()) { 1216 mValueAnimator.cancel(); 1217 } 1218 1219 // If the current and sent specs don't match, update the sent spec. 1220 synchronized (mLock) { 1221 final boolean changed = !mSentMagnificationSpec.equals(spec); 1222 if (changed) { 1223 if (animate) { 1224 animateMagnificationSpecLocked(spec); 1225 } else { 1226 setMagnificationSpecLocked(spec); 1227 } 1228 } 1229 } 1230 } 1231 1232 @GuardedBy("mLock") setMagnificationSpecLocked(MagnificationSpec spec)1233 private void setMagnificationSpecLocked(MagnificationSpec spec) { 1234 if (mEnabled) { 1235 if (DEBUG_SET_MAGNIFICATION_SPEC) { 1236 Slog.i(LOG_TAG, "Sending: " + spec); 1237 } 1238 1239 mSentMagnificationSpec.setTo(spec); 1240 mControllerCtx.getWindowManager().setMagnificationSpec( 1241 mDisplayId, mSentMagnificationSpec); 1242 } 1243 } 1244 animateMagnificationSpecLocked(MagnificationSpec toSpec)1245 private void animateMagnificationSpecLocked(MagnificationSpec toSpec) { 1246 mEndMagnificationSpec.setTo(toSpec); 1247 mStartMagnificationSpec.setTo(mSentMagnificationSpec); 1248 mValueAnimator.start(); 1249 } 1250 1251 @Override onAnimationUpdate(ValueAnimator animation)1252 public void onAnimationUpdate(ValueAnimator animation) { 1253 synchronized (mLock) { 1254 if (mEnabled) { 1255 float fract = animation.getAnimatedFraction(); 1256 mTmpMagnificationSpec.scale = mStartMagnificationSpec.scale + 1257 (mEndMagnificationSpec.scale - mStartMagnificationSpec.scale) * fract; 1258 mTmpMagnificationSpec.offsetX = mStartMagnificationSpec.offsetX + 1259 (mEndMagnificationSpec.offsetX - mStartMagnificationSpec.offsetX) 1260 * fract; 1261 mTmpMagnificationSpec.offsetY = mStartMagnificationSpec.offsetY + 1262 (mEndMagnificationSpec.offsetY - mStartMagnificationSpec.offsetY) 1263 * fract; 1264 setMagnificationSpecLocked(mTmpMagnificationSpec); 1265 } 1266 } 1267 } 1268 } 1269 1270 private static class ScreenStateObserver extends BroadcastReceiver { 1271 private final Context mContext; 1272 private final MagnificationController mController; 1273 private boolean mRegistered = false; 1274 ScreenStateObserver(Context context, MagnificationController controller)1275 public ScreenStateObserver(Context context, MagnificationController controller) { 1276 mContext = context; 1277 mController = controller; 1278 } 1279 registerIfNecessary()1280 public void registerIfNecessary() { 1281 if (!mRegistered) { 1282 mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF)); 1283 mRegistered = true; 1284 } 1285 } 1286 unregister()1287 public void unregister() { 1288 if (mRegistered) { 1289 mContext.unregisterReceiver(this); 1290 mRegistered = false; 1291 } 1292 } 1293 1294 @Override onReceive(Context context, Intent intent)1295 public void onReceive(Context context, Intent intent) { 1296 mController.onScreenTurnedOff(); 1297 } 1298 } 1299 1300 /** 1301 * This class holds resources used between the classes in MagnificationController, and 1302 * functions for tests to mock it. 1303 */ 1304 @VisibleForTesting 1305 public static class ControllerContext { 1306 private final Context mContext; 1307 private final AccessibilityManagerService mAms; 1308 private final WindowManagerInternal mWindowManager; 1309 private final Handler mHandler; 1310 private final Long mAnimationDuration; 1311 1312 /** 1313 * Constructor for ControllerContext. 1314 */ ControllerContext(@onNull Context context, @NonNull AccessibilityManagerService ams, @NonNull WindowManagerInternal windowManager, @NonNull Handler handler, long animationDuration)1315 public ControllerContext(@NonNull Context context, 1316 @NonNull AccessibilityManagerService ams, 1317 @NonNull WindowManagerInternal windowManager, 1318 @NonNull Handler handler, 1319 long animationDuration) { 1320 mContext = context; 1321 mAms = ams; 1322 mWindowManager = windowManager; 1323 mHandler = handler; 1324 mAnimationDuration = animationDuration; 1325 } 1326 1327 /** 1328 * @return A context. 1329 */ 1330 @NonNull getContext()1331 public Context getContext() { 1332 return mContext; 1333 } 1334 1335 /** 1336 * @return AccessibilityManagerService 1337 */ 1338 @NonNull getAms()1339 public AccessibilityManagerService getAms() { 1340 return mAms; 1341 } 1342 1343 /** 1344 * @return WindowManagerInternal 1345 */ 1346 @NonNull getWindowManager()1347 public WindowManagerInternal getWindowManager() { 1348 return mWindowManager; 1349 } 1350 1351 /** 1352 * @return Handler for main looper 1353 */ 1354 @NonNull getHandler()1355 public Handler getHandler() { 1356 return mHandler; 1357 } 1358 1359 /** 1360 * Create a new ValueAnimator. 1361 * 1362 * @return ValueAnimator 1363 */ 1364 @NonNull newValueAnimator()1365 public ValueAnimator newValueAnimator() { 1366 return new ValueAnimator(); 1367 } 1368 1369 /** 1370 * Write Settings of magnification scale. 1371 */ putMagnificationScale(float value, int userId)1372 public void putMagnificationScale(float value, int userId) { 1373 Settings.Secure.putFloatForUser(mContext.getContentResolver(), 1374 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, value, userId); 1375 } 1376 1377 /** 1378 * Get Settings of magnification scale. 1379 */ getMagnificationScale(int userId)1380 public float getMagnificationScale(int userId) { 1381 return Settings.Secure.getFloatForUser(mContext.getContentResolver(), 1382 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 1383 DEFAULT_MAGNIFICATION_SCALE, userId); 1384 } 1385 1386 /** 1387 * @return Configuration of animation duration. 1388 */ getAnimationDuration()1389 public long getAnimationDuration() { 1390 return mAnimationDuration; 1391 } 1392 } 1393 } 1394