/* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.accessibility; import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY; import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.ACTION_RESTORED; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.ACTION_OPEN_SETTINGS; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_CHANNEL; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_ID; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_SHOWN; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_UNNECESSARY; import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.UNKNOWN; import static com.google.common.truth.Truth.assertThat; import android.app.Application; import android.app.Notification; import android.app.NotificationManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.os.Bundle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; import com.android.graphics.hwui.flags.Flags; import com.android.settings.Utils; import com.google.common.truth.Expect; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.Shadows; import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowNotification; import org.robolectric.shadows.ShadowNotificationManager; import org.robolectric.shadows.ShadowPackageManager; import java.util.List; /** Tests for {@link HighContrastTextMigrationReceiver}. */ @RunWith(RobolectricTestRunner.class) public class HighContrastTextMigrationReceiverTest { @Rule public final Expect expect = Expect.create(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private final Context mContext = ApplicationProvider.getApplicationContext(); private HighContrastTextMigrationReceiver mReceiver; private ShadowApplication mShadowApplication; private ShadowNotificationManager mShadowNotificationManager; @Before public void setUp() { NotificationManager notificationManager = mContext.getSystemService(NotificationManager.class); mShadowNotificationManager = Shadows.shadowOf(notificationManager); mShadowApplication = Shadows.shadowOf((Application) mContext); // Setup Settings app as a system app ShadowPackageManager shadowPm = Shadows.shadowOf(mContext.getPackageManager()); ComponentName textReadingComponent = new ComponentName(Utils.SETTINGS_PACKAGE_NAME, com.android.settings.Settings.TextReadingSettingsActivity.class.getName()); ActivityInfo activityInfo = shadowPm.addActivityIfNotPresent(textReadingComponent); activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; shadowPm.addOrUpdateActivity(activityInfo); mReceiver = new HighContrastTextMigrationReceiver(); } @Test @DisableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onReceive_flagOff_settingsNotSet() { mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED)); assertPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onRestored_hctStateOn_showPromptHctKeepsOn() { setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON); mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED)); assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, ON); verifyNotificationSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onRestored_hctStateOff_showPromptHctKeepsOff() { setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF); mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED)); assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, OFF); verifyNotificationSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onPreBootCompleted_promptStateUnknownHctOn_showPromptAndAutoDisableHct() { setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON); Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); mReceiver.onReceive(mContext, intent); assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF); verifyNotificationSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onPreBootCompleted_promptStateUnknownAndHctOff_promptIsUnnecessaryHctKeepsOff() { setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF); Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); mReceiver.onReceive(mContext, intent); assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF); verifyNotificationNotSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onPreBootCompleted_promptStateShownAndHctOn_promptStateUnchangedHctKeepsOn() { setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON); Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); mReceiver.onReceive(mContext, intent); assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON); verifyNotificationNotSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onPreBootCompleted_promptStateShownAndHctOff_promptStateUnchangedHctKeepsOff() { setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF); Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); mReceiver.onReceive(mContext, intent); assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF); verifyNotificationNotSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onPreBootCompleted_promptStateUnnecessaryAndHctOn_promptStateUnchangedHctKeepsOn() { setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON); Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); mReceiver.onReceive(mContext, intent); assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON); verifyNotificationNotSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onPreBootCompleted_promptStateUnnecessaryHctOff_promptStateUnchangedHctKeepsOff() { setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF); Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED); mReceiver.onReceive(mContext, intent); assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF); verifyNotificationNotSent(); } @Test @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT) public void onReceive_openSettingsIntent_openHighContrastTextPreference() { Intent intent = new Intent(ACTION_OPEN_SETTINGS); mReceiver.onReceive(mContext, intent); List broadcastIntents = mShadowApplication.getBroadcastIntents(); assertThat(broadcastIntents.size()).isEqualTo(1); assertThat(broadcastIntents.get(0).getAction()) .isEqualTo(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); Intent startedActivitie = mShadowApplication.getNextStartedActivity(); assertThat(startedActivitie).isNotNull(); Bundle fragmentArgs = startedActivitie.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS); assertThat(fragmentArgs).isNotNull(); assertThat(fragmentArgs.getString(EXTRA_FRAGMENT_ARG_KEY)) .isEqualTo(TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY); Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID); assertThat(notification).isNull(); } private void verifyNotificationNotSent() { Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID); assertThat(notification).isNull(); } private void verifyNotificationSent() { // Verify hct channel created assertThat(mShadowNotificationManager.getNotificationChannels().stream().anyMatch( channel -> channel.getId().equals(NOTIFICATION_CHANNEL))).isTrue(); // Verify hct notification is sent with correct content Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID); assertThat(notification).isNotNull(); ShadowNotification shadowNotification = Shadows.shadowOf(notification); expect.that(shadowNotification.getContentTitle()).isEqualTo("Improve text contrast"); expect.that(shadowNotification.getContentText()).isEqualTo( "Outline text has replaced high contrast text. You can turn it on in Settings."); expect.that(notification.actions.length).isEqualTo(1); expect.that(notification.actions[0].title.toString()).isEqualTo("Go to Settings"); } private void assertPromptStateAndHctState( @HighContrastTextMigrationReceiver.PromptState int promptState, @AccessibilityUtil.State int hctState) { assertThat(Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, UNKNOWN)) .isEqualTo(promptState); assertThat(Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF)) .isEqualTo(hctState); } private void setPromptStateAndHctState( @HighContrastTextMigrationReceiver.PromptState int promptState, @AccessibilityUtil.State int hctState) { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, promptState); Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, hctState); } }