1 /*
2  * Copyright (C) 2017 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.app;
18 
19 import static android.app.NotificationManager.IMPORTANCE_NONE;
20 
21 import android.annotation.Nullable;
22 import android.app.NotificationChannel;
23 import android.app.NotificationChannelGroup;
24 import android.app.NotificationManager;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.content.pm.ShortcutInfo;
28 import android.graphics.drawable.Drawable;
29 import android.os.UserManager;
30 import android.util.Log;
31 
32 import androidx.preference.Preference;
33 
34 import com.android.settings.notification.NotificationBackend;
35 import com.android.settingslib.RestrictedLockUtils;
36 import com.android.settingslib.core.AbstractPreferenceController;
37 
38 import java.util.Comparator;
39 import java.util.Objects;
40 
41 /**
42  * Parent class for preferences appearing on notification setting pages at the app,
43  * notification channel group, or notification channel level.
44  */
45 public abstract class NotificationPreferenceController extends AbstractPreferenceController {
46     private static final String TAG = "ChannelPrefContr";
47     @Nullable
48     protected NotificationChannel mChannel;
49     @Nullable
50     protected NotificationChannelGroup mChannelGroup;
51     protected RestrictedLockUtils.EnforcedAdmin mAdmin;
52     protected NotificationBackend.AppRow mAppRow;
53     protected final NotificationManager mNm;
54     protected final NotificationBackend mBackend;
55     protected final Context mContext;
56     protected final UserManager mUm;
57     protected final PackageManager mPm;
58     protected Preference mPreference;
59     @Nullable
60     protected Drawable mConversationDrawable;
61     @Nullable
62     protected ShortcutInfo mConversationInfo;
63 
NotificationPreferenceController(Context context, NotificationBackend backend)64     public NotificationPreferenceController(Context context, NotificationBackend backend) {
65         super(context);
66         mContext = context;
67         mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
68         mBackend = backend;
69         mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
70         mPm = mContext.getPackageManager();
71     }
72 
73     /**
74      * Returns true if field's parent object is not blocked.
75      */
76     @Override
isAvailable()77     public boolean isAvailable() {
78         if (mAppRow == null) {
79             return false;
80         }
81         if (mAppRow.banned) {
82             return false;
83         }
84         if (mChannelGroup != null) {
85             if (mChannelGroup.isBlocked()) {
86                 return false;
87             }
88         }
89         if (mChannel != null) {
90             return mChannel.getImportance() != IMPORTANCE_NONE;
91         }
92         return true;
93     }
94 
onResume(NotificationBackend.AppRow appRow, @Nullable NotificationChannel channel, @Nullable NotificationChannelGroup group, Drawable conversationDrawable, ShortcutInfo conversationInfo, RestrictedLockUtils.EnforcedAdmin admin)95     protected void onResume(NotificationBackend.AppRow appRow,
96             @Nullable NotificationChannel channel, @Nullable NotificationChannelGroup group,
97             Drawable conversationDrawable,
98             ShortcutInfo conversationInfo,
99             RestrictedLockUtils.EnforcedAdmin admin) {
100         mAppRow = appRow;
101         mChannel = channel;
102         mChannelGroup = group;
103         mAdmin = admin;
104         mConversationDrawable = conversationDrawable;
105         mConversationInfo = conversationInfo;
106     }
107 
checkCanBeVisible(int minImportanceVisible)108     protected boolean checkCanBeVisible(int minImportanceVisible) {
109         if (mChannel == null) {
110             Log.w(TAG, "No channel");
111             return false;
112         }
113 
114         int importance = mChannel.getImportance();
115         if (importance == NotificationManager.IMPORTANCE_UNSPECIFIED) {
116             return true;
117         }
118         return importance >= minImportanceVisible;
119     }
120 
saveChannel()121     protected void saveChannel() {
122         if (mChannel != null && mAppRow != null) {
123             mBackend.updateChannel(mAppRow.pkg, mAppRow.uid, mChannel);
124         }
125     }
126 
isChannelBlockable()127     protected boolean isChannelBlockable() {
128         return isChannelBlockable(mChannel);
129     }
130 
isChannelBlockable(NotificationChannel channel)131     protected boolean isChannelBlockable(NotificationChannel channel) {
132         if (channel != null && mAppRow != null) {
133             if (channel.isImportanceLockedByCriticalDeviceFunction()
134                     || channel.isImportanceLockedByOEM()) {
135                 return channel.getImportance() == IMPORTANCE_NONE;
136             }
137 
138             return channel.isBlockable() || !mAppRow.systemApp
139                     || channel.getImportance() == IMPORTANCE_NONE;
140         }
141         return false;
142     }
143 
isChannelConfigurable(NotificationChannel channel)144     protected boolean isChannelConfigurable(NotificationChannel channel) {
145         if (channel != null && mAppRow != null) {
146             return !channel.isImportanceLockedByOEM();
147         }
148         return false;
149     }
150 
isChannelGroupBlockable()151     protected boolean isChannelGroupBlockable() {
152         return isChannelGroupBlockable(mChannelGroup);
153     }
154 
isChannelGroupBlockable(NotificationChannelGroup group)155     protected boolean isChannelGroupBlockable(NotificationChannelGroup group) {
156         if (group != null && mAppRow != null) {
157             if (!mAppRow.systemApp) {
158                 return true;
159             }
160 
161             return group.isBlocked();
162         }
163         return false;
164     }
165 
hasValidGroup()166     protected boolean hasValidGroup() {
167         return mChannelGroup != null;
168     }
169 
isDefaultChannel()170     protected final boolean isDefaultChannel() {
171         if (mChannel == null) {
172             return false;
173         }
174         return Objects.equals(NotificationChannel.DEFAULT_CHANNEL_ID, mChannel.getId());
175     }
176 
177     public static final Comparator<NotificationChannelGroup> CHANNEL_GROUP_COMPARATOR =
178             new Comparator<NotificationChannelGroup>() {
179         @Override
180         public int compare(NotificationChannelGroup left, NotificationChannelGroup right) {
181             // Non-grouped channels (in placeholder group with a null id) come last
182             if (left.getId() == null && right.getId() != null) {
183                 return 1;
184             } else if (right.getId() == null && left.getId() != null) {
185                 return -1;
186             }
187             return left.getId().compareTo(right.getId());
188         }
189     };
190 
191     public static final Comparator<NotificationChannel> CHANNEL_COMPARATOR = (left, right) -> {
192         if (left.isDeleted() != right.isDeleted()) {
193             return Boolean.compare(left.isDeleted(), right.isDeleted());
194         } else if (left.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
195             // Uncategorized/miscellaneous legacy channel goes last
196             return 1;
197         } else if (right.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
198             return -1;
199         }
200 
201         return left.getId().compareTo(right.getId());
202     };
203 }
204