• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.settings.accessibility;
18 
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.os.SystemClock;
22 import android.util.Log;
23 import android.view.Choreographer;
24 import android.view.View;
25 
26 import androidx.annotation.NonNull;
27 import androidx.preference.PreferenceScreen;
28 
29 import com.android.settings.R;
30 import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
31 import com.android.settings.core.BasePreferenceController;
32 import com.android.settings.core.instrumentation.SettingsStatsLog;
33 import com.android.settings.display.PreviewPagerAdapter;
34 import com.android.settings.widget.LabeledSeekBarPreference;
35 
36 import java.util.Objects;
37 
38 /**
39  * A {@link BasePreferenceController} for controlling the preview pager of the text and reading
40  * options.
41  */
42 class TextReadingPreviewController extends BasePreferenceController implements
43         PreviewSizeSeekBarController.ProgressInteractionListener {
44     private static final String TAG = "TextReadingPreviewCtrl";
45     private static final int LAYER_INITIAL_INDEX = 0;
46     private static final int FRAME_INITIAL_INDEX = 0;
47     static final int[] PREVIEW_SAMPLE_RES_IDS = new int[]{
48             R.layout.accessibility_text_reading_preview_app_grid,
49             R.layout.screen_zoom_preview_1,
50             R.layout.accessibility_text_reading_preview_mail_content};
51 
52     private static final String PREVIEW_KEY = "preview";
53     private static final String FONT_SIZE_KEY = "font_size";
54     private static final String DISPLAY_SIZE_KEY = "display_size";
55     private static final long MIN_COMMIT_INTERVAL_MS = 800;
56     private static final long CHANGE_BY_SEEKBAR_DELAY_MS = 100;
57     private static final long CHANGE_BY_BUTTON_DELAY_MS = 300;
58     private final FontSizeData mFontSizeData;
59     private final DisplaySizeData mDisplaySizeData;
60     private int mLastFontProgress;
61     private int mLastDisplayProgress;
62     private long mLastCommitTime;
63     private TextReadingPreviewPreference mPreviewPreference;
64     private LabeledSeekBarPreference mFontSizePreference;
65     private LabeledSeekBarPreference mDisplaySizePreference;
66 
67     @EntryPoint
68     private int mEntryPoint;
69 
70     private final Choreographer.FrameCallback mCommit = f -> {
71         tryCommitFontSizeConfig();
72         tryCommitDisplaySizeConfig();
73 
74         mLastCommitTime = SystemClock.elapsedRealtime();
75     };
76 
TextReadingPreviewController(Context context, String preferenceKey, @NonNull FontSizeData fontSizeData, @NonNull DisplaySizeData displaySizeData)77     TextReadingPreviewController(Context context, String preferenceKey,
78             @NonNull FontSizeData fontSizeData, @NonNull DisplaySizeData displaySizeData) {
79         super(context, preferenceKey);
80         mFontSizeData = fontSizeData;
81         mDisplaySizeData = displaySizeData;
82     }
83 
84     @Override
getAvailabilityStatus()85     public int getAvailabilityStatus() {
86         return AVAILABLE;
87     }
88 
89     @Override
displayPreference(PreferenceScreen screen)90     public void displayPreference(PreferenceScreen screen) {
91         super.displayPreference(screen);
92 
93         mPreviewPreference = screen.findPreference(PREVIEW_KEY);
94 
95         mFontSizePreference = screen.findPreference(FONT_SIZE_KEY);
96         mDisplaySizePreference = screen.findPreference(DISPLAY_SIZE_KEY);
97         Objects.requireNonNull(mFontSizePreference,
98                 /* message= */ "Font size preference is null, the preview controller "
99                         + "couldn't get the info");
100         Objects.requireNonNull(mDisplaySizePreference,
101                 /* message= */ "Display size preference is null, the preview controller"
102                         + " couldn't get the info");
103 
104         mLastFontProgress = mFontSizeData.getInitialIndex();
105         mLastDisplayProgress = mDisplaySizeData.getInitialIndex();
106 
107         final Configuration origConfig = mContext.getResources().getConfiguration();
108         final boolean isLayoutRtl =
109                 origConfig.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
110         final PreviewPagerAdapter pagerAdapter = new PreviewPagerAdapter(mContext, isLayoutRtl,
111                 PREVIEW_SAMPLE_RES_IDS, createConfig(origConfig));
112         mPreviewPreference.setPreviewAdapter(pagerAdapter);
113         mPreviewPreference.setCurrentItem(
114                 isLayoutRtl ? PREVIEW_SAMPLE_RES_IDS.length - 1 : FRAME_INITIAL_INDEX);
115 
116         final int initialPagerIndex =
117                 mLastFontProgress * mDisplaySizeData.getValues().size() + mLastDisplayProgress;
118         mPreviewPreference.setLastLayerIndex(initialPagerIndex);
119         pagerAdapter.setPreviewLayer(initialPagerIndex, LAYER_INITIAL_INDEX,
120                 FRAME_INITIAL_INDEX, /* animate= */ false);
121     }
122 
123     @Override
notifyPreferenceChanged()124     public void notifyPreferenceChanged() {
125         mPreviewPreference.notifyPreviewPagerChanged(getPagerIndex());
126     }
127 
128     @Override
onProgressChanged()129     public void onProgressChanged() {
130         postCommitDelayed(CHANGE_BY_BUTTON_DELAY_MS);
131     }
132 
133     @Override
onEndTrackingTouch()134     public void onEndTrackingTouch() {
135         postCommitDelayed(CHANGE_BY_SEEKBAR_DELAY_MS);
136     }
137 
setCurrentItem(int index)138     void setCurrentItem(int index) {
139         mPreviewPreference.setCurrentItem(index);
140     }
141 
getCurrentItem()142     int getCurrentItem() {
143         return mPreviewPreference.getCurrentItem();
144     }
145 
146     /**
147      * The entry point is used for logging.
148      *
149      * @param entryPoint from which settings page
150      */
setEntryPoint(@ntryPoint int entryPoint)151     void setEntryPoint(@EntryPoint int entryPoint) {
152         mEntryPoint = entryPoint;
153     }
154 
155     /**
156      * Avoids the flicker when switching to the previous or next level.
157      *
158      * <p><br>[Flickering problem steps] commit()-> snapshot in framework(old screenshot) ->
159      * app update the preview -> snapshot(old screen) fade out</p>
160      *
161      * <p><br>To prevent flickering problem, we make sure that we update the local preview
162      * first and then we do the commit later. </p>
163      *
164      * <p><br><b>Note:</b> It doesn't matter that we use
165      * Choreographer or main thread handler since the delay time is longer
166      * than 1 frame. Use Choreographer to let developer understand it's a
167      * window update.</p>
168      *
169      * @param commitDelayMs the interval time after a action.
170      */
postCommitDelayed(long commitDelayMs)171     void postCommitDelayed(long commitDelayMs) {
172         if (SystemClock.elapsedRealtime() - mLastCommitTime < MIN_COMMIT_INTERVAL_MS) {
173             commitDelayMs += MIN_COMMIT_INTERVAL_MS;
174         }
175 
176         final Choreographer choreographer = Choreographer.getInstance();
177         choreographer.removeFrameCallback(mCommit);
178         choreographer.postFrameCallbackDelayed(mCommit, commitDelayMs);
179     }
180 
getPagerIndex()181     private int getPagerIndex() {
182         final int displayDataSize = mDisplaySizeData.getValues().size();
183         final int fontSizeProgress = mFontSizePreference.getProgress();
184         final int displaySizeProgress = mDisplaySizePreference.getProgress();
185 
186         // To be consistent with the {@link PreviewPagerAdapter#setPreviewLayer(int, int, int,
187         // boolean)} behavior, here also needs the same design. In addition, please also refer to
188         // the {@link #createConfig(Configuration)}.
189         return fontSizeProgress * displayDataSize + displaySizeProgress;
190     }
191 
tryCommitFontSizeConfig()192     private void tryCommitFontSizeConfig() {
193         final int fontProgress = mFontSizePreference.getProgress();
194         if (fontProgress != mLastFontProgress) {
195             mFontSizeData.commit(fontProgress);
196             mLastFontProgress = fontProgress;
197 
198             if (Log.isLoggable(TAG, Log.DEBUG)) {
199                 Log.d(TAG, "Font size: " + fontProgress);
200             }
201 
202             SettingsStatsLog.write(
203                     SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
204                     AccessibilityStatsLogUtils.convertToItemKeyName(mFontSizePreference.getKey()),
205                     fontProgress,
206                     AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
207         }
208     }
209 
tryCommitDisplaySizeConfig()210     private void tryCommitDisplaySizeConfig() {
211         final int displayProgress = mDisplaySizePreference.getProgress();
212         if (displayProgress != mLastDisplayProgress) {
213             mDisplaySizeData.commit(displayProgress);
214             mLastDisplayProgress = displayProgress;
215 
216             if (Log.isLoggable(TAG, Log.DEBUG)) {
217                 Log.d(TAG, "Display size: " + displayProgress);
218             }
219 
220             SettingsStatsLog.write(
221                     SettingsStatsLog.ACCESSIBILITY_TEXT_READING_OPTIONS_CHANGED,
222                     AccessibilityStatsLogUtils.convertToItemKeyName(
223                             mDisplaySizePreference.getKey()),
224                     displayProgress,
225                     AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
226         }
227     }
228 
createConfig(Configuration origConfig)229     private Configuration[] createConfig(Configuration origConfig) {
230         final int fontDataSize = mFontSizeData.getValues().size();
231         final int displayDataSize = mDisplaySizeData.getValues().size();
232         final int totalNum = fontDataSize * displayDataSize;
233         final Configuration[] configurations = new Configuration[totalNum];
234 
235         for (int i = 0; i < fontDataSize; ++i) {
236             for (int j = 0; j < displayDataSize; ++j) {
237                 final Configuration config = new Configuration(origConfig);
238                 config.fontScale = mFontSizeData.getValues().get(i);
239                 config.densityDpi = mDisplaySizeData.getValues().get(j);
240 
241                 configurations[i * displayDataSize + j] = config;
242             }
243         }
244 
245         return configurations;
246     }
247 }
248