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 17 package com.android.settings.notification.modes; 18 19 import static android.app.AutomaticZenRule.TYPE_BEDTIME; 20 import static android.app.AutomaticZenRule.TYPE_DRIVING; 21 import static android.app.AutomaticZenRule.TYPE_IMMERSIVE; 22 import static android.app.AutomaticZenRule.TYPE_MANAGED; 23 import static android.app.AutomaticZenRule.TYPE_OTHER; 24 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR; 25 import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME; 26 import static android.app.AutomaticZenRule.TYPE_THEATER; 27 import static android.app.AutomaticZenRule.TYPE_UNKNOWN; 28 import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; 29 30 import android.annotation.SuppressLint; 31 import android.app.ActionBar; 32 import android.app.AutomaticZenRule; 33 import android.app.settings.SettingsEnums; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.os.Bundle; 37 import android.util.Log; 38 import android.view.View; 39 import android.widget.Button; 40 import android.widget.ImageView; 41 import android.widget.TextView; 42 import android.widget.Toast; 43 import android.widget.Toolbar; 44 45 import androidx.activity.EdgeToEdge; 46 import androidx.annotation.NonNull; 47 import androidx.annotation.StringRes; 48 import androidx.annotation.VisibleForTesting; 49 import androidx.fragment.app.FragmentActivity; 50 51 import com.android.settings.R; 52 import com.android.settings.Utils; 53 import com.android.settingslib.notification.modes.ZenMode; 54 import com.android.settingslib.notification.modes.ZenModesBackend; 55 56 /** 57 * Interstitial page for modes that are disabled, but not disabled by the user. This page 58 * provides a button to enable the mode, and then goes to the mode setup page. 59 */ 60 public class SetupInterstitialActivity extends FragmentActivity { 61 private static final String TAG = "ModeSetupInterstitial"; 62 private ZenModesBackend mBackend; 63 64 /** 65 * Returns an intent leading to this page for the given mode and context. 66 */ getIntent(@onNull Context context, @NonNull ZenMode mode)67 public static @NonNull Intent getIntent(@NonNull Context context, @NonNull ZenMode mode) { 68 return new Intent(Intent.ACTION_MAIN) 69 .setClass(context, SetupInterstitialActivity.class) 70 .setPackage(context.getPackageName()) 71 .setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT) 72 .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, mode.getId()); 73 } 74 75 @Override onCreate(Bundle savedInstanceState)76 protected void onCreate(Bundle savedInstanceState) { 77 EdgeToEdge.enable(this); 78 Utils.setupEdgeToEdge(this); 79 super.onCreate(savedInstanceState); 80 mBackend = ZenModesBackend.getInstance(this); 81 setContentView(R.layout.mode_interstitial_layout); 82 83 // Set up toolbar to only have a back button & no title 84 Toolbar toolbar = findViewById(R.id.action_bar); 85 setActionBar(toolbar); 86 ActionBar actionBar = getActionBar(); 87 if (actionBar != null) { 88 actionBar.setDisplayHomeAsUpEnabled(true); 89 actionBar.setHomeButtonEnabled(true); 90 actionBar.setDisplayShowTitleEnabled(false); 91 } 92 } 93 94 @Override onNavigateUp()95 public boolean onNavigateUp() { 96 // have the home button on the action bar go back 97 getOnBackPressedDispatcher().onBackPressed(); 98 return true; 99 } 100 101 @Override onResume()102 protected void onResume() { 103 super.onResume(); 104 105 // See if we have mode data 106 final Intent intent = getIntent(); 107 if (intent == null) { 108 Log.w(TAG, "no intent found for modes interstitial"); 109 finish(); 110 return; 111 } 112 113 String modeId = intent.getStringExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID); 114 if (modeId == null) { 115 Log.w(TAG, "no mode id included in intent: " + intent); 116 finish(); 117 return; 118 } 119 120 ZenMode mode = mBackend.getMode(modeId); 121 if (mode == null) { 122 Log.w(TAG, "mode not found for mode id: " + modeId); 123 finish(); 124 return; 125 } 126 setTitle(mode.getName()); 127 128 TextView title = findViewById(R.id.mode_name_title); 129 if (title != null) { 130 title.setText(mode.getName()); 131 } 132 133 TextView subtitle = findViewById(R.id.mode_name_subtitle); 134 if (subtitle != null) { 135 subtitle.setText(getSubtitle(mode)); 136 } 137 138 ImageView img = findViewById(R.id.image); 139 if (img != null) { 140 setImage(img, mode); 141 } 142 143 Button button = findViewById(R.id.enable_mode_button); 144 if (button != null) { 145 setupButton(button, mode); 146 } 147 } 148 149 @StringRes 150 @SuppressLint("SwitchIntDef") getSubtitle(ZenMode mode)151 private static int getSubtitle(ZenMode mode) { 152 if (mode.isSystemOwned()) { 153 return switch (mode.getType()) { 154 case TYPE_SCHEDULE_TIME -> R.string.zen_mode_inspiration_schedule_time; 155 case TYPE_SCHEDULE_CALENDAR -> R.string.zen_mode_inspiration_schedule_calendar; 156 default -> R.string.zen_mode_inspiration_generic; // Custom Manual 157 }; 158 } else { 159 return switch (mode.getType()) { 160 case TYPE_BEDTIME -> R.string.zen_mode_inspiration_bedtime; 161 case TYPE_DRIVING -> R.string.zen_mode_inspiration_driving; 162 case TYPE_IMMERSIVE -> R.string.zen_mode_inspiration_immersive; 163 case TYPE_THEATER -> R.string.zen_mode_inspiration_theater; 164 case TYPE_MANAGED -> R.string.zen_mode_inspiration_managed; 165 default -> R.string.zen_mode_inspiration_generic; // Including OTHER, UNKNOWN. 166 }; 167 } 168 } 169 170 private void setImage(@NonNull ImageView img, @NonNull ZenMode mode) { 171 int drawableRes = switch (mode.getType()) { 172 case TYPE_BEDTIME -> R.drawable.modes_interstitial_bedtime; 173 case TYPE_DRIVING -> R.drawable.modes_interstitial_driving; 174 case TYPE_IMMERSIVE -> R.drawable.modes_interstitial_immersive; 175 case TYPE_THEATER -> R.drawable.modes_interstitial_theater; 176 case TYPE_MANAGED -> R.drawable.modes_interstitial_managed; 177 case TYPE_OTHER, TYPE_SCHEDULE_CALENDAR, TYPE_SCHEDULE_TIME -> 178 R.drawable.modes_interstitial_other; 179 case TYPE_UNKNOWN -> R.drawable.modes_interstitial_unknown; 180 default -> R.drawable.modes_interstitial_unknown; 181 }; 182 183 img.setImageResource(drawableRes); 184 } 185 186 private void setupButton(Button button, @NonNull ZenMode mode) { 187 button.setText(getString(R.string.zen_mode_setup_button_label, mode.getName())); 188 button.setOnClickListener(enableButtonListener(mode.getId(), mode.getType())); 189 } 190 191 @VisibleForTesting 192 View.OnClickListener enableButtonListener(String modeId, @AutomaticZenRule.Type int modeType) { 193 return unused -> { 194 // When clicked, we first reload mode info in case it has changed in the interim, 195 // then enable the mode and then send the user to the mode's configuration page. 196 boolean updated = enableMode(modeId); 197 198 int metricsCategory = switch (modeType) { 199 case TYPE_BEDTIME -> SettingsEnums.ZEN_MODE_INTERSTITIAL_BEDTIME; 200 case TYPE_DRIVING -> SettingsEnums.ZEN_MODE_INTERSTITIAL_DRIVING; 201 default -> SettingsEnums.ZEN_MODE_INTERSTITIAL; 202 }; 203 204 // Don't come back to this activity after sending the user to the modes page, if 205 // they happen to go back. Forward the activity result in case we got here (indirectly) 206 // from some app that is waiting for the result. 207 if (updated) { 208 ZenSubSettingLauncher.forModeFragment(this, ZenModeFragment.class, modeId, 209 metricsCategory) 210 .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT).launch(); 211 } 212 finish(); 213 }; 214 } 215 216 // Enables the given mode after first refreshing its data from the backend. Returns true if 217 // the update went through, and false if for some reason the mode wasn't found. 218 private boolean enableMode(@NonNull String modeId) { 219 if (mBackend == null) { 220 return false; 221 } 222 223 ZenMode modeToUpdate = mBackend.getMode(modeId); 224 if (modeToUpdate == null) { 225 // tell the user the mode isn't found, for some reason 226 Toast.makeText(this, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) 227 .show(); 228 return false; 229 } 230 231 modeToUpdate.setEnabled(true); 232 mBackend.updateMode(modeToUpdate); 233 return true; 234 } 235 } 236