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