| /* | |
| * 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 android.annotation.IntDef; | |
| import android.app.Notification; | |
| import android.app.NotificationChannel; | |
| import android.app.NotificationManager; | |
| import android.app.PendingIntent; | |
| import android.content.BroadcastReceiver; | |
| import android.content.Context; | |
| import android.content.Intent; | |
| import android.os.Bundle; | |
| import android.provider.Settings; | |
| import android.util.Log; | |
| import androidx.annotation.NonNull; | |
| import com.android.graphics.hwui.flags.Flags; | |
| import com.android.modules.expresslog.Counter; | |
| import com.android.settings.R; | |
| import com.google.common.annotations.VisibleForTesting; | |
| import java.lang.annotation.Retention; | |
| import java.lang.annotation.RetentionPolicy; | |
| /** | |
| * Handling smooth migration to the new high contrast text appearance | |
| */ | |
| public class HighContrastTextMigrationReceiver extends BroadcastReceiver { | |
| private static final String TAG = HighContrastTextMigrationReceiver.class.getSimpleName(); | |
| @VisibleForTesting | |
| static final String NOTIFICATION_CHANNEL = "accessibility_notification_channel"; | |
| @VisibleForTesting | |
| static final String ACTION_RESTORED = | |
| "com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED"; | |
| @VisibleForTesting | |
| static final String ACTION_OPEN_SETTINGS = | |
| "com.android.settings.accessibility.ACTION_OPEN_HIGH_CONTRAST_TEXT_SETTINGS"; | |
| private static final String ACTION_NOTIFICATION_DISMISSED = | |
| "com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_NOTIFICATION_DISMISSED"; | |
| @VisibleForTesting | |
| static final int NOTIFICATION_ID = R.string.accessibility_notification_high_contrast_text_title; | |
| @Retention(RetentionPolicy.SOURCE) | |
| @IntDef({ | |
| PromptState.UNKNOWN, | |
| PromptState.PROMPT_SHOWN, | |
| PromptState.PROMPT_UNNECESSARY, | |
| }) | |
| public @interface PromptState { | |
| int UNKNOWN = 0; | |
| int PROMPT_SHOWN = 1; | |
| int PROMPT_UNNECESSARY = 2; | |
| } | |
| @Override | |
| public void onReceive(@NonNull Context context, @NonNull Intent intent) { | |
| if (!Flags.highContrastTextSmallTextRect()) { | |
| return; | |
| } | |
| if (ACTION_OPEN_SETTINGS.equals(intent.getAction())) { | |
| // Close notification drawer before opening the HCT setting. | |
| context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); | |
| Intent settingsIntent = createHighContrastTextSettingsIntent(context); | |
| settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | |
| context.startActivity(settingsIntent); | |
| context.getSystemService(NotificationManager.class).cancel(NOTIFICATION_ID); | |
| } else if (ACTION_NOTIFICATION_DISMISSED.equals(intent.getAction())) { | |
| Counter.logIncrement("accessibility.value_hct_notification_dismissed"); | |
| } else if (ACTION_RESTORED.equals(intent.getAction())) { | |
| Log.i(TAG, "HCT attempted to be restored from backup; showing notification for userId: " | |
| + context.getUserId()); | |
| Settings.Secure.putInt(context.getContentResolver(), | |
| Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, | |
| PromptState.PROMPT_SHOWN); | |
| showNotification(context); | |
| } else if (Intent.ACTION_PRE_BOOT_COMPLETED.equals(intent.getAction())) { | |
| final boolean hasSeenPromptIfNecessary = Settings.Secure.getInt( | |
| context.getContentResolver(), | |
| Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, PromptState.UNKNOWN) | |
| != PromptState.UNKNOWN; | |
| if (hasSeenPromptIfNecessary) { | |
| Log.i(TAG, "Has seen HCT prompt if necessary; skip HCT migration for userId: " | |
| + context.getUserId()); | |
| return; | |
| } | |
| final boolean isHctEnabled = Settings.Secure.getInt(context.getContentResolver(), | |
| Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF) == ON; | |
| if (isHctEnabled) { | |
| Log.i(TAG, "HCT enabled before OTA update; performing migration for userId: " | |
| + context.getUserId()); | |
| Settings.Secure.putInt(context.getContentResolver(), | |
| Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, | |
| OFF); | |
| Settings.Secure.putInt(context.getContentResolver(), | |
| Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, | |
| PromptState.PROMPT_SHOWN); | |
| showNotification(context); | |
| } else { | |
| Log.i(TAG, | |
| "HCT was not enabled before OTA update; not performing migration for " | |
| + "userId: " + context.getUserId()); | |
| Settings.Secure.putInt(context.getContentResolver(), | |
| Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, | |
| PromptState.PROMPT_UNNECESSARY); | |
| } | |
| } | |
| } | |
| private String getNotificationContentText(Context context) { | |
| final String settingsAppName = context.getString(R.string.settings_label); | |
| return context.getString( | |
| R.string.accessibility_notification_high_contrast_text_body_message, | |
| settingsAppName); | |
| } | |
| private void showNotification(Context context) { | |
| Notification.Builder notificationBuilder = new Notification.Builder(context, | |
| NOTIFICATION_CHANNEL) | |
| .setSmallIcon(R.drawable.ic_settings_24dp) | |
| .setContentTitle(context.getString( | |
| R.string.accessibility_notification_high_contrast_text_title)) | |
| .setContentText(getNotificationContentText(context)) | |
| .setFlag(Notification.FLAG_NO_CLEAR, true); | |
| Intent settingsIntent = createHighContrastTextSettingsIntent(context); | |
| if (settingsIntent.resolveActivity(context.getPackageManager()) != null) { | |
| PendingIntent settingsPendingIntent = PendingIntent.getActivity(context, | |
| /* requestCode = */ 0, settingsIntent, PendingIntent.FLAG_IMMUTABLE); | |
| Intent actionIntent = new Intent(context, HighContrastTextMigrationReceiver.class); | |
| actionIntent.setAction(ACTION_OPEN_SETTINGS); | |
| PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, | |
| actionIntent, PendingIntent.FLAG_IMMUTABLE); | |
| final int actionResId = | |
| R.string.accessibility_notification_high_contrast_text_action_open_settings; | |
| Notification.Action settingsAction = new Notification.Action.Builder( | |
| /* icon= */ null, | |
| context.getString(actionResId, context.getString(R.string.settings_label)), | |
| actionPendingIntent | |
| ).build(); | |
| notificationBuilder | |
| .setContentIntent(settingsPendingIntent) | |
| .addAction(settingsAction) | |
| .setAutoCancel(true); | |
| } | |
| Intent deleteIntent = new Intent(context, HighContrastTextMigrationReceiver.class); | |
| deleteIntent.setAction(ACTION_NOTIFICATION_DISMISSED); | |
| PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, 0, | |
| deleteIntent, PendingIntent.FLAG_IMMUTABLE); | |
| notificationBuilder.setDeleteIntent(deletePendingIntent); | |
| NotificationManager notificationManager = | |
| context.getSystemService(NotificationManager.class); | |
| NotificationChannel notificationChannel = new NotificationChannel( | |
| NOTIFICATION_CHANNEL, | |
| context.getString(R.string.accessibility_settings), | |
| NotificationManager.IMPORTANCE_LOW); | |
| notificationManager.createNotificationChannel(notificationChannel); | |
| notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); | |
| Counter.logIncrement("accessibility.value_hct_notification_posted"); | |
| } | |
| private Intent createHighContrastTextSettingsIntent(Context context) { | |
| Intent settingsIntent = new Intent(Settings.ACTION_TEXT_READING_SETTINGS); | |
| settingsIntent.setPackage(context.getPackageName()); | |
| Bundle fragmentArgs = new Bundle(); | |
| fragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY, | |
| TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY); | |
| fragmentArgs.putInt(TextReadingPreferenceFragment.EXTRA_LAUNCHED_FROM, | |
| TextReadingPreferenceFragment.EntryPoint.HIGH_CONTRAST_TEXT_NOTIFICATION); | |
| settingsIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs); | |
| return settingsIntent; | |
| } | |
| } |