• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 package com.android.settings.accessibility;
17 
18 import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
19 import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
20 import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
21 import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
22 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.ACTION_RESTORED;
23 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.ACTION_OPEN_SETTINGS;
24 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_CHANNEL;
25 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_ID;
26 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_SHOWN;
27 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_UNNECESSARY;
28 import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.UNKNOWN;
29 
30 import static com.google.common.truth.Truth.assertThat;
31 
32 import android.app.Application;
33 import android.app.Notification;
34 import android.app.NotificationManager;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.pm.ActivityInfo;
39 import android.content.pm.ApplicationInfo;
40 import android.os.Bundle;
41 import android.platform.test.annotations.DisableFlags;
42 import android.platform.test.annotations.EnableFlags;
43 import android.platform.test.flag.junit.SetFlagsRule;
44 import android.provider.Settings;
45 
46 import androidx.test.core.app.ApplicationProvider;
47 
48 import com.android.graphics.hwui.flags.Flags;
49 import com.android.settings.Utils;
50 
51 import com.google.common.truth.Expect;
52 
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.robolectric.RobolectricTestRunner;
58 import org.robolectric.Shadows;
59 import org.robolectric.shadows.ShadowApplication;
60 import org.robolectric.shadows.ShadowNotification;
61 import org.robolectric.shadows.ShadowNotificationManager;
62 import org.robolectric.shadows.ShadowPackageManager;
63 
64 import java.util.List;
65 
66 /** Tests for {@link HighContrastTextMigrationReceiver}. */
67 @RunWith(RobolectricTestRunner.class)
68 public class HighContrastTextMigrationReceiverTest {
69     @Rule
70     public final Expect expect = Expect.create();
71     @Rule
72     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
73     private final Context mContext = ApplicationProvider.getApplicationContext();
74     private HighContrastTextMigrationReceiver mReceiver;
75     private ShadowApplication mShadowApplication;
76     private ShadowNotificationManager mShadowNotificationManager;
77 
78     @Before
setUp()79     public void setUp() {
80         NotificationManager notificationManager =
81                 mContext.getSystemService(NotificationManager.class);
82         mShadowNotificationManager = Shadows.shadowOf(notificationManager);
83         mShadowApplication = Shadows.shadowOf((Application) mContext);
84 
85         // Setup Settings app as a system app
86         ShadowPackageManager shadowPm = Shadows.shadowOf(mContext.getPackageManager());
87         ComponentName textReadingComponent = new ComponentName(Utils.SETTINGS_PACKAGE_NAME,
88                 com.android.settings.Settings.TextReadingSettingsActivity.class.getName());
89         ActivityInfo activityInfo = shadowPm.addActivityIfNotPresent(textReadingComponent);
90         activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
91         shadowPm.addOrUpdateActivity(activityInfo);
92 
93         mReceiver = new HighContrastTextMigrationReceiver();
94     }
95 
96     @Test
97     @DisableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onReceive_flagOff_settingsNotSet()98     public void onReceive_flagOff_settingsNotSet() {
99         mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
100 
101         assertPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
102     }
103 
104     @Test
105     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onRestored_hctStateOn_showPromptHctKeepsOn()106     public void onRestored_hctStateOn_showPromptHctKeepsOn() {
107         setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON);
108 
109         mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
110 
111         assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, ON);
112         verifyNotificationSent();
113     }
114 
115     @Test
116     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onRestored_hctStateOff_showPromptHctKeepsOff()117     public void onRestored_hctStateOff_showPromptHctKeepsOff() {
118         setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
119 
120         mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
121 
122         assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, OFF);
123         verifyNotificationSent();
124     }
125 
126     @Test
127     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onPreBootCompleted_promptStateUnknownHctOn_showPromptAndAutoDisableHct()128     public void onPreBootCompleted_promptStateUnknownHctOn_showPromptAndAutoDisableHct() {
129         setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON);
130 
131         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
132         mReceiver.onReceive(mContext, intent);
133 
134         assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
135         verifyNotificationSent();
136     }
137 
138     @Test
139     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onPreBootCompleted_promptStateUnknownAndHctOff_promptIsUnnecessaryHctKeepsOff()140     public void onPreBootCompleted_promptStateUnknownAndHctOff_promptIsUnnecessaryHctKeepsOff() {
141         setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
142 
143         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
144         mReceiver.onReceive(mContext, intent);
145 
146         assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
147         verifyNotificationNotSent();
148     }
149 
150     @Test
151     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onPreBootCompleted_promptStateShownAndHctOn_promptStateUnchangedHctKeepsOn()152     public void onPreBootCompleted_promptStateShownAndHctOn_promptStateUnchangedHctKeepsOn() {
153         setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON);
154 
155         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
156         mReceiver.onReceive(mContext, intent);
157 
158         assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON);
159         verifyNotificationNotSent();
160     }
161 
162     @Test
163     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onPreBootCompleted_promptStateShownAndHctOff_promptStateUnchangedHctKeepsOff()164     public void onPreBootCompleted_promptStateShownAndHctOff_promptStateUnchangedHctKeepsOff() {
165         setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
166 
167         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
168         mReceiver.onReceive(mContext, intent);
169 
170         assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
171         verifyNotificationNotSent();
172     }
173 
174     @Test
175     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onPreBootCompleted_promptStateUnnecessaryAndHctOn_promptStateUnchangedHctKeepsOn()176     public void onPreBootCompleted_promptStateUnnecessaryAndHctOn_promptStateUnchangedHctKeepsOn() {
177         setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON);
178 
179         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
180         mReceiver.onReceive(mContext, intent);
181 
182         assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON);
183         verifyNotificationNotSent();
184     }
185 
186     @Test
187     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onPreBootCompleted_promptStateUnnecessaryHctOff_promptStateUnchangedHctKeepsOff()188     public void onPreBootCompleted_promptStateUnnecessaryHctOff_promptStateUnchangedHctKeepsOff() {
189         setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
190 
191         Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
192         mReceiver.onReceive(mContext, intent);
193 
194         assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
195         verifyNotificationNotSent();
196     }
197 
198     @Test
199     @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
onReceive_openSettingsIntent_openHighContrastTextPreference()200     public void onReceive_openSettingsIntent_openHighContrastTextPreference() {
201         Intent intent = new Intent(ACTION_OPEN_SETTINGS);
202         mReceiver.onReceive(mContext, intent);
203 
204         List<Intent> broadcastIntents = mShadowApplication.getBroadcastIntents();
205         assertThat(broadcastIntents.size()).isEqualTo(1);
206         assertThat(broadcastIntents.get(0).getAction())
207                 .isEqualTo(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
208 
209         Intent startedActivitie = mShadowApplication.getNextStartedActivity();
210         assertThat(startedActivitie).isNotNull();
211         Bundle fragmentArgs = startedActivitie.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
212         assertThat(fragmentArgs).isNotNull();
213         assertThat(fragmentArgs.getString(EXTRA_FRAGMENT_ARG_KEY))
214                 .isEqualTo(TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY);
215 
216         Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
217         assertThat(notification).isNull();
218     }
219 
verifyNotificationNotSent()220     private void verifyNotificationNotSent() {
221         Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
222         assertThat(notification).isNull();
223     }
224 
verifyNotificationSent()225     private void verifyNotificationSent() {
226         // Verify hct channel created
227         assertThat(mShadowNotificationManager.getNotificationChannels().stream().anyMatch(
228                 channel -> channel.getId().equals(NOTIFICATION_CHANNEL))).isTrue();
229 
230         // Verify hct notification is sent with correct content
231         Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
232         assertThat(notification).isNotNull();
233 
234         ShadowNotification shadowNotification = Shadows.shadowOf(notification);
235         expect.that(shadowNotification.getContentTitle()).isEqualTo("Improve text contrast");
236         expect.that(shadowNotification.getContentText()).isEqualTo(
237                 "Outline text has replaced high contrast text. You can turn it on in Settings.");
238 
239         expect.that(notification.actions.length).isEqualTo(1);
240         expect.that(notification.actions[0].title.toString()).isEqualTo("Go to Settings");
241     }
242 
assertPromptStateAndHctState( @ighContrastTextMigrationReceiver.PromptState int promptState, @AccessibilityUtil.State int hctState)243     private void assertPromptStateAndHctState(
244             @HighContrastTextMigrationReceiver.PromptState int promptState,
245             @AccessibilityUtil.State int hctState) {
246         assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
247                 Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, UNKNOWN))
248                 .isEqualTo(promptState);
249         assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
250                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF))
251                 .isEqualTo(hctState);
252     }
253 
setPromptStateAndHctState( @ighContrastTextMigrationReceiver.PromptState int promptState, @AccessibilityUtil.State int hctState)254     private void setPromptStateAndHctState(
255             @HighContrastTextMigrationReceiver.PromptState int promptState,
256             @AccessibilityUtil.State int hctState) {
257         Settings.Secure.putInt(mContext.getContentResolver(),
258                 Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, promptState);
259         Settings.Secure.putInt(mContext.getContentResolver(),
260                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, hctState);
261     }
262 }
263