• 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 
17 package com.android.settings.notification.modes;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Preconditions.checkState;
21 
22 import android.content.Context;
23 import android.os.Bundle;
24 
25 import androidx.annotation.DrawableRes;
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.VisibleForTesting;
29 
30 import com.android.settings.R;
31 import com.android.settings.dashboard.DashboardFragment;
32 import com.android.settingslib.core.AbstractPreferenceController;
33 import com.android.settingslib.notification.modes.ZenIconLoader;
34 import com.android.settingslib.notification.modes.ZenMode;
35 import com.android.settingslib.notification.modes.ZenModesBackend;
36 
37 import com.google.common.base.Strings;
38 import com.google.common.collect.ImmutableList;
39 
40 import java.util.List;
41 
42 /**
43  * Base class for the "add a mode" and "edit mode name and icon" fragments. In both cases we are
44  * editing a {@link ZenMode}, but the mode shouldn't be saved immediately after each atomic change
45  * -- instead, it will be saved to the backend upon user confirmation.
46  *
47  * <p>As a result, instead of using {@link ZenModesBackend} to apply each change, we instead modify
48  * an in-memory {@link ZenMode}, that is preserved/restored in extras. This also means we don't
49  * listen to changes -- whatever the user sees should be applied.
50  */
51 public abstract class ZenModeEditNameIconFragmentBase extends DashboardFragment {
52 
53     private static final String MODE_KEY = "ZenMode";
54 
55     @Nullable private ZenMode mZenMode;
56 
57     private ZenModesBackend mBackend;
58 
59     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
setBackend(ZenModesBackend backend)60     void setBackend(ZenModesBackend backend) {
61         mBackend = backend;
62     }
63 
64     @Override
onAttach(Context context)65     public void onAttach(Context context) {
66         super.onAttach(context);
67         if (mBackend == null) {
68             mBackend = ZenModesBackend.getInstance(context);
69         }
70     }
71 
72     @Override
onCreate(Bundle icicle)73     public final void onCreate(Bundle icicle) {
74         super.onCreate(icicle);
75         mZenMode = icicle != null
76                 ? icicle.getParcelable(MODE_KEY, ZenMode.class)
77                 : onCreateInstantiateZenMode();
78 
79         if (mZenMode != null) {
80             for (var controller : getZenPreferenceControllers()) {
81                 controller.setZenMode(mZenMode);
82             }
83         } else {
84             finish();
85         }
86     }
87 
88     /**
89      * Provides the mode that will be edited. Called in {@link #onCreate}, the first time (the
90      * value returned here is persisted on Fragment recreation).
91      *
92      * <p>If {@code null} is returned, the fragment will {@link #finish()}.
93      */
94     @Nullable
onCreateInstantiateZenMode()95     protected abstract ZenMode onCreateInstantiateZenMode();
96 
97     @Override
getPreferenceScreenResId()98     protected final int getPreferenceScreenResId() {
99         return R.xml.modes_edit_name_icon;
100     }
101 
102     @Override
createPreferenceControllers( Context context)103     protected final List<AbstractPreferenceController> createPreferenceControllers(
104             Context context) {
105         return ImmutableList.of(
106                 new ZenModeIconPickerIconPreferenceController(context, ZenIconLoader.getInstance(),
107                         "chosen_icon", this),
108                 new ZenModeEditNamePreferenceController(context, "name", this::setModeName),
109                 new ZenModeIconPickerListPreferenceController(context, "icon_list",
110                         this::setModeIcon),
111                 new ZenModeEditDonePreferenceController(context, "done", this::saveMode)
112         );
113     }
114 
getZenPreferenceControllers()115     private Iterable<AbstractZenModePreferenceController> getZenPreferenceControllers() {
116         return getPreferenceControllers().stream()
117                 .flatMap(List::stream)
118                 .filter(AbstractZenModePreferenceController.class::isInstance)
119                 .map(AbstractZenModePreferenceController.class::cast)
120                 .toList();
121     }
122 
123     @VisibleForTesting(otherwise = VisibleForTesting.NONE)
124     @Nullable
getZenMode()125     ZenMode getZenMode() {
126         return mZenMode;
127     }
128 
129     @VisibleForTesting
setModeName(String name)130     final void setModeName(String name) {
131         checkNotNull(mZenMode).setName(Strings.nullToEmpty(name));
132         forceUpdatePreferences(); // Updates confirmation button.
133     }
134 
135     @VisibleForTesting
setModeIcon(@rawableRes int iconResId)136     final void setModeIcon(@DrawableRes int iconResId) {
137         checkNotNull(mZenMode).setIconResId(iconResId);
138         forceUpdatePreferences();  // Updates icon at the top.
139     }
140 
141 
142     @VisibleForTesting
saveMode()143     final void saveMode() {
144         saveMode(checkNotNull(mZenMode));
145     }
146 
147     /**
148      * Called to actually save the mode, after the user confirms. This method is also responsible
149      * for calling {@link #finish()}, if appropriate.
150      *
151      * <p>Note that {@code mode} is the <em>in-memory</em> mode and, as such, may have obsolete
152      * data. If the concrete fragment is editing an existing mode, it should first fetch it from
153      * the backend, and copy the new name and icon before saving. */
saveMode(ZenMode mode)154     abstract void saveMode(ZenMode mode);
155 
156     @NonNull
requireBackend()157     protected ZenModesBackend requireBackend() {
158         checkState(mBackend != null);
159         return mBackend;
160     }
161 
162     @Override
onSaveInstanceState(Bundle outState)163     public void onSaveInstanceState(Bundle outState) {
164         super.onSaveInstanceState(outState);
165         outState.putParcelable(MODE_KEY, mZenMode);
166     }
167 }
168