• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.MagnificationConfig.MAGNIFICATION_MODE_DEFAULT;
20 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
21 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
22 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
23 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
24 import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;
25 
26 import android.accessibilityservice.MagnificationConfig;
27 import android.annotation.NonNull;
28 import android.graphics.Region;
29 import android.util.Slog;
30 import android.view.Display;
31 
32 import java.io.PrintWriter;
33 import java.util.ArrayList;
34 
35 /**
36  * Processor class for AccessibilityService connection to control magnification on the specified
37  * display. This wraps the function of magnification controller.
38  *
39  * <p>
40  * If the magnification config uses {@link DEFAULT_MODE}. This processor will control the current
41  * activated magnifier on the display. If there is no magnifier activated, it controls
42  * full-screen magnifier by default.
43  * </p>
44  *
45  * <p>
46  * If the magnification config uses {@link FULLSCREEN_MODE}. This processor will control
47  * full-screen magnifier on the display.
48  * </p>
49  *
50  * <p>
51  * If the magnification config uses {@link WINDOW_MODE}. This processor will control
52  * the activated window magnifier on the display.
53  * </p>
54  *
55  * @see MagnificationController
56  * @see FullScreenMagnificationController
57  */
58 public class MagnificationProcessor {
59 
60     private static final String TAG = "MagnificationProcessor";
61     private static final boolean DEBUG = false;
62 
63     private final MagnificationController mController;
64 
MagnificationProcessor(MagnificationController controller)65     public MagnificationProcessor(MagnificationController controller) {
66         mController = controller;
67     }
68 
69     /**
70      * Gets the magnification config of the display.
71      *
72      * @param displayId The logical display id
73      * @return the magnification config
74      */
getMagnificationConfig(int displayId)75     public @NonNull MagnificationConfig getMagnificationConfig(int displayId) {
76         final int mode = getControllingMode(displayId);
77         MagnificationConfig.Builder builder = new MagnificationConfig.Builder();
78         if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
79             final FullScreenMagnificationController fullScreenMagnificationController =
80                     mController.getFullScreenMagnificationController();
81             builder.setMode(mode)
82                     .setScale(fullScreenMagnificationController.getScale(displayId))
83                     .setCenterX(fullScreenMagnificationController.getCenterX(displayId))
84                     .setCenterY(fullScreenMagnificationController.getCenterY(displayId));
85         } else if (mode == MAGNIFICATION_MODE_WINDOW) {
86             final WindowMagnificationManager windowMagnificationManager =
87                     mController.getWindowMagnificationMgr();
88             builder.setMode(mode)
89                     .setScale(windowMagnificationManager.getScale(displayId))
90                     .setCenterX(windowMagnificationManager.getCenterX(displayId))
91                     .setCenterY(windowMagnificationManager.getCenterY(displayId));
92         }
93         return builder.build();
94     }
95 
96     /**
97      * Sets the magnification config of the display. If animation is disabled, the transition
98      * is immediate.
99      *
100      * @param displayId The logical display id
101      * @param config    The magnification config
102      * @param animate   {@code true} to animate from the current config or
103      *                  {@code false} to set the config immediately
104      * @param id        The ID of the service requesting the change
105      * @return {@code true} if the magnification spec changed, {@code false} if the spec did not
106      * change
107      */
setMagnificationConfig(int displayId, @NonNull MagnificationConfig config, boolean animate, int id)108     public boolean setMagnificationConfig(int displayId, @NonNull MagnificationConfig config,
109             boolean animate, int id) {
110         if (DEBUG) {
111             Slog.d(TAG, "setMagnificationConfig config=" + config);
112         }
113         if (transitionModeIfNeeded(displayId, config, animate, id)) {
114             return true;
115         }
116 
117         int configMode = config.getMode();
118         if (configMode == MAGNIFICATION_MODE_DEFAULT) {
119             configMode = getControllingMode(displayId);
120         }
121         if (configMode == MAGNIFICATION_MODE_FULLSCREEN) {
122             return setScaleAndCenterForFullScreenMagnification(displayId, config.getScale(),
123                     config.getCenterX(), config.getCenterY(),
124                     animate, id);
125         } else if (configMode == MAGNIFICATION_MODE_WINDOW) {
126             return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
127                     config.getScale(), config.getCenterX(), config.getCenterY(),
128                     animate ? STUB_ANIMATION_CALLBACK : null,
129                     id);
130         }
131         return false;
132     }
133 
setScaleAndCenterForFullScreenMagnification(int displayId, float scale, float centerX, float centerY, boolean animate, int id)134     private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
135             float centerX, float centerY, boolean animate, int id) {
136 
137         if (!isRegistered(displayId)) {
138             register(displayId);
139         }
140         return mController.getFullScreenMagnificationController().setScaleAndCenter(
141                 displayId, scale, centerX, centerY, animate, id);
142     }
143 
144     /**
145      * Returns {@code true} if transition magnification mode needed. And it is no need to transition
146      * mode when the controlling mode is unchanged or the controlling magnifier is not activated.
147      */
transitionModeIfNeeded(int displayId, MagnificationConfig config, boolean animate, int id)148     private boolean transitionModeIfNeeded(int displayId, MagnificationConfig config,
149             boolean animate, int id) {
150         int currentMode = getControllingMode(displayId);
151         if (config.getMode() == MagnificationConfig.MAGNIFICATION_MODE_DEFAULT) {
152             return false;
153         }
154         // Target mode is as same as current mode and is not transitioning.
155         if (currentMode == config.getMode() && !mController.hasDisableMagnificationCallback(
156                 displayId)) {
157             return false;
158         }
159         mController.transitionMagnificationConfigMode(displayId, config, animate, id);
160         return true;
161     }
162 
163     /**
164      * Returns the magnification scale of full-screen magnification on the display.
165      * If an animation is in progress, this reflects the end state of the animation.
166      *
167      * @param displayId The logical display id.
168      * @return the scale
169      */
getScale(int displayId)170     public float getScale(int displayId) {
171         return mController.getFullScreenMagnificationController().getScale(displayId);
172     }
173 
174     /**
175      * Returns the magnification center in X coordinate of full-screen magnification.
176      * If the service can control magnification but fullscreen magnifier is not registered, it will
177      * register the magnifier for this call then unregister the magnifier finally to make the
178      * magnification center correct.
179      *
180      * @param displayId The logical display id
181      * @param canControlMagnification Whether the service can control magnification
182      * @return the X coordinate
183      */
getCenterX(int displayId, boolean canControlMagnification)184     public float getCenterX(int displayId, boolean canControlMagnification) {
185         boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
186                 canControlMagnification);
187         try {
188             return mController.getFullScreenMagnificationController().getCenterX(displayId);
189         } finally {
190             if (registeredJustForThisCall) {
191                 unregister(displayId);
192             }
193         }
194     }
195 
196     /**
197      * Returns the magnification center in Y coordinate of full-screen magnification.
198      * If the service can control magnification but fullscreen magnifier is not registered, it will
199      * register the magnifier for this call then unregister the magnifier finally to make the
200      * magnification center correct.
201      *
202      * @param displayId The logical display id
203      * @param canControlMagnification Whether the service can control magnification
204      * @return the Y coordinate
205      */
getCenterY(int displayId, boolean canControlMagnification)206     public float getCenterY(int displayId, boolean canControlMagnification) {
207         boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
208                 canControlMagnification);
209         try {
210             return mController.getFullScreenMagnificationController().getCenterY(displayId);
211         } finally {
212             if (registeredJustForThisCall) {
213                 unregister(displayId);
214             }
215         }
216     }
217 
218     /**
219      * Returns the region of the screen currently active for magnification if the
220      * controlling magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN}.
221      * Returns the region of screen projected on the magnification window if the controlling
222      * magnification is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW}.
223      * <p>
224      * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_FULLSCREEN},
225      * the returned region will be empty if the magnification is
226      * not active. And the magnification is active if magnification gestures are enabled
227      * or if a service is running that can control magnification.
228      * </p><p>
229      * If the controlling mode is {@link MagnificationConfig#MAGNIFICATION_MODE_WINDOW},
230      * the returned region will be empty if the magnification is not activated.
231      * </p>
232      *
233      * @param displayId The logical display id
234      * @param outRegion the region to populate
235      * @param canControlMagnification Whether the service can control magnification
236      */
getCurrentMagnificationRegion(int displayId, @NonNull Region outRegion, boolean canControlMagnification)237     public void getCurrentMagnificationRegion(int displayId, @NonNull Region outRegion,
238             boolean canControlMagnification) {
239         int currentMode = getControllingMode(displayId);
240         if (currentMode == MAGNIFICATION_MODE_FULLSCREEN) {
241             getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
242         } else if (currentMode == MAGNIFICATION_MODE_WINDOW) {
243             mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
244                     outRegion);
245         }
246     }
247 
248     /**
249      * Returns the magnification bounds of full-screen magnification on the given display.
250      *
251      * @param displayId The logical display id
252      * @param outRegion the region to populate
253      * @param canControlMagnification Whether the service can control magnification
254      */
getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion, boolean canControlMagnification)255     public void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
256             boolean canControlMagnification) {
257         boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
258                 canControlMagnification);
259         try {
260             mController.getFullScreenMagnificationController().getMagnificationRegion(displayId,
261                     outRegion);
262         } finally {
263             if (registeredJustForThisCall) {
264                 unregister(displayId);
265             }
266         }
267     }
268 
269     /**
270      * Resets the controlling magnifier on the given display.
271      * For resetting window magnifier, it disables the magnifier by setting the scale to 1.
272      *
273      * @param displayId The logical display id.
274      * @param animate   {@code true} to animate the transition, {@code false}
275      *                  to transition immediately
276      * @return {@code true} if the magnification spec changed, {@code false} if
277      * the spec did not change
278      */
resetCurrentMagnification(int displayId, boolean animate)279     public boolean resetCurrentMagnification(int displayId, boolean animate) {
280         int mode = getControllingMode(displayId);
281         if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
282             return mController.getFullScreenMagnificationController().reset(displayId, animate);
283         } else if (mode == MAGNIFICATION_MODE_WINDOW) {
284             return mController.getWindowMagnificationMgr().disableWindowMagnification(displayId,
285                     false, animate ? STUB_ANIMATION_CALLBACK : null);
286         }
287         return false;
288     }
289 
290     /**
291      * Resets the full-screen magnification on the given display.
292      *
293      * @param displayId The logical display id.
294      * @param animate   {@code true} to animate the transition, {@code false}
295      *                  to transition immediately
296      * @return {@code true} if the magnification spec changed, {@code false} if
297      * the spec did not change
298      */
resetFullscreenMagnification(int displayId, boolean animate)299     public boolean resetFullscreenMagnification(int displayId, boolean animate) {
300         return mController.getFullScreenMagnificationController().reset(displayId, animate);
301     }
302 
303     /**
304      * Resets all the magnifiers on all the displays.
305      * Called when the a11y service connection that has changed the current magnification spec is
306      * unbound or the binder died.
307      *
308      * @param connectionId The connection id
309      */
resetAllIfNeeded(int connectionId)310     public void resetAllIfNeeded(int connectionId) {
311         mController.getFullScreenMagnificationController().resetAllIfNeeded(connectionId);
312         mController.getWindowMagnificationMgr().resetAllIfNeeded(connectionId);
313     }
314 
315     /**
316      * {@link FullScreenMagnificationController#isMagnifying(int)}
317      * {@link WindowMagnificationManager#isWindowMagnifierEnabled(int)}
318      */
isMagnifying(int displayId)319     public boolean isMagnifying(int displayId) {
320         int mode = getControllingMode(displayId);
321         if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
322             return mController.getFullScreenMagnificationController().isMagnifying(displayId);
323         } else if (mode == MAGNIFICATION_MODE_WINDOW) {
324             return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
325         }
326         return false;
327     }
328 
329     /**
330      * Returns the current controlling magnification mode on the given display.
331      * If there is no magnifier activated, it fallbacks to the last activated mode.
332      * And the last activated mode is {@link FULLSCREEN_MODE} by default.
333      *
334      * @param displayId The logical display id
335      */
getControllingMode(int displayId)336     public int getControllingMode(int displayId) {
337         if (mController.isActivated(displayId,
338                 ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
339             return MAGNIFICATION_MODE_WINDOW;
340         } else if (mController.isActivated(displayId,
341                 ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) {
342             return MAGNIFICATION_MODE_FULLSCREEN;
343         } else {
344             return (mController.getLastMagnificationActivatedMode(displayId)
345                     == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
346                     ? MAGNIFICATION_MODE_WINDOW
347                     : MAGNIFICATION_MODE_FULLSCREEN;
348         }
349     }
350 
registerDisplayMagnificationIfNeeded(int displayId, boolean canControlMagnification)351     private boolean registerDisplayMagnificationIfNeeded(int displayId,
352             boolean canControlMagnification) {
353         if (!isRegistered(displayId) && canControlMagnification) {
354             register(displayId);
355             return true;
356         }
357         return false;
358     }
359 
isRegistered(int displayId)360     private boolean isRegistered(int displayId) {
361         return mController.getFullScreenMagnificationController().isRegistered(displayId);
362     }
363 
364     /**
365      * {@link FullScreenMagnificationController#register(int)}
366      */
register(int displayId)367     private void register(int displayId) {
368         mController.getFullScreenMagnificationController().register(displayId);
369     }
370 
371     /**
372      * {@link FullScreenMagnificationController#unregister(int)} (int)}
373      */
unregister(int displayId)374     private void unregister(int displayId) {
375         mController.getFullScreenMagnificationController().unregister(displayId);
376     }
377 
378     /**
379      * Dumps magnification configuration {@link MagnificationConfig} and state for each
380      * {@link Display}
381      */
dump(final PrintWriter pw, ArrayList<Display> displaysList)382     public void dump(final PrintWriter pw, ArrayList<Display> displaysList) {
383         for (int i = 0; i < displaysList.size(); i++) {
384             final int displayId = displaysList.get(i).getDisplayId();
385 
386             final MagnificationConfig config = getMagnificationConfig(displayId);
387             pw.println("Magnifier on display#" + displayId);
388             pw.append("    " + config).println();
389 
390             final Region region = new Region();
391             getCurrentMagnificationRegion(displayId, region, true);
392             if (!region.isEmpty()) {
393                 pw.append("    Magnification region=").append(region.toString()).println();
394             }
395             pw.append("    IdOfLastServiceToMagnify="
396                     + getIdOfLastServiceToMagnify(config.getMode(), displayId)).println();
397 
398             dumpTrackingTypingFocusEnabledState(pw, displayId, config.getMode());
399         }
400         pw.append("    SupportWindowMagnification="
401                 + mController.supportWindowMagnification()).println();
402         pw.append("    WindowMagnificationConnectionState="
403                 + mController.getWindowMagnificationMgr().getConnectionState()).println();
404     }
405 
getIdOfLastServiceToMagnify(int mode, int displayId)406     private int getIdOfLastServiceToMagnify(int mode, int displayId) {
407         return (mode == MAGNIFICATION_MODE_FULLSCREEN)
408                 ? mController.getFullScreenMagnificationController()
409                 .getIdOfLastServiceToMagnify(displayId)
410                 : mController.getWindowMagnificationMgr().getIdOfLastServiceToMagnify(
411                         displayId);
412     }
413 
dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId, int mode)414     private void dumpTrackingTypingFocusEnabledState(final PrintWriter pw, int displayId,
415             int mode) {
416         if (mode == MAGNIFICATION_MODE_WINDOW) {
417             pw.append("    TrackingTypingFocusEnabled="  + mController
418                             .getWindowMagnificationMgr().isTrackingTypingFocusEnabled(displayId))
419                     .println();
420         }
421     }
422 }
423