1 /*
2  * Copyright (C) 2012 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 package com.android.mail.preferences;
17 
18 import android.content.ContentUris;
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.media.RingtoneManager;
22 import android.net.Uri;
23 import android.provider.Settings;
24 
25 import com.android.mail.providers.Account;
26 import com.android.mail.providers.Folder;
27 import com.android.mail.providers.UIProvider.AccountCapabilities;
28 import com.android.mail.providers.UIProvider.FolderCapabilities;
29 import com.android.mail.utils.NotificationActionUtils.NotificationActionType;
30 import com.google.android.mail.common.base.Strings;
31 import com.google.common.collect.ImmutableSet;
32 
33 import java.util.LinkedHashSet;
34 import java.util.Set;
35 
36 /**
37  * Preferences relevant to one specific folder. In Email, this would only be used for an account's
38  * inbox. In Gmail, this is used for every account/label pair.
39  */
40 public class FolderPreferences extends VersionedPrefs {
41 
42     private static final String PREFS_NAME_PREFIX = "Folder";
43 
44     public static final class PreferenceKeys {
45         /** Boolean value indicating whether notifications are enabled */
46         public static final String NOTIFICATIONS_ENABLED = "notifications-enabled";
47         /** String value of the notification ringtone URI */
48         public static final String NOTIFICATION_RINGTONE = "notification-ringtone";
49         /** Boolean value indicating whether we should explicitly vibrate */
50         public static final String NOTIFICATION_VIBRATE = "notification-vibrate";
51         /**
52          * Boolean value indicating whether we notify for every message (<code>true</code>), or just
53          * once for the folder (<code>false</code>)
54          */
55         public static final String NOTIFICATION_NOTIFY_EVERY_MESSAGE =
56                 "notification-notify-every-message";
57 
58         public static final ImmutableSet<String> BACKUP_KEYS =
59                 new ImmutableSet.Builder<String>()
60                         .add(NOTIFICATIONS_ENABLED)
61                         .add(NOTIFICATION_RINGTONE)
62                         .add(NOTIFICATION_VIBRATE)
63                         .add(NOTIFICATION_NOTIFY_EVERY_MESSAGE)
64                         .build();
65     }
66 
67     private final Folder mFolder;
68     /** An id that is constant across app installations. */
69     private final String mPersistentId;
70     private final boolean mUseInboxDefaultNotificationSettings;
71 
72     /**
73      * @param accountEmail The account email. This must never change for the account.
74      * @param folder The folder
75      */
FolderPreferences(final Context context, final String accountEmail, final Folder folder, final boolean useInboxDefaultNotificationSettings)76     public FolderPreferences(final Context context, final String accountEmail, final Folder folder,
77             final boolean useInboxDefaultNotificationSettings) {
78         this(context, accountEmail, folder, folder.persistentId,
79                 useInboxDefaultNotificationSettings);
80     }
81 
82     /**
83      * A constructor that can be used when no {@link Folder} object is available (like during a
84      * restore). This will function as expected except when calling
85      * {@link #getDefaultNotificationActions(Context)}, so
86      * {@link #FolderPreferences(Context, String, Folder, boolean)} should be used if at all
87      * possible.
88      *
89      * @param accountEmail The account email. This must never change for the account.
90      * @param persistentId An identifier for the folder that does not change across app
91      *        installations.
92      */
FolderPreferences(final Context context, final String accountEmail, final String persistentId, final boolean useInboxDefaultNotificationSettings)93     public FolderPreferences(final Context context, final String accountEmail, final String persistentId,
94             final boolean useInboxDefaultNotificationSettings) {
95         this(context, accountEmail, null, persistentId, useInboxDefaultNotificationSettings);
96     }
97 
FolderPreferences(final Context context, final String accountEmail, final Folder folder, final String persistentId, final boolean useInboxDefaultNotificationSettings)98     private FolderPreferences(final Context context, final String accountEmail, final Folder folder,
99             final String persistentId, final boolean useInboxDefaultNotificationSettings) {
100         super(context, buildSharedPrefsName(accountEmail, persistentId));
101         mFolder = folder;
102         mPersistentId = persistentId;
103         mUseInboxDefaultNotificationSettings = useInboxDefaultNotificationSettings;
104     }
105 
buildSharedPrefsName(final String account, final String persistentId)106     private static String buildSharedPrefsName(final String account, final String persistentId) {
107         return PREFS_NAME_PREFIX + '-' + account + '-' + persistentId;
108     }
109 
110     @Override
performUpgrade(final int oldVersion, final int newVersion)111     protected void performUpgrade(final int oldVersion, final int newVersion) {
112         if (oldVersion > newVersion) {
113             throw new IllegalStateException(
114                     "You appear to have downgraded your app. Please clear app data.");
115         }
116     }
117 
118     @Override
canBackup(final String key)119     protected boolean canBackup(final String key) {
120         if (mPersistentId == null) {
121             return false;
122         }
123 
124         return PreferenceKeys.BACKUP_KEYS.contains(key);
125     }
126 
127     @Override
getBackupValue(final String key, final Object value)128     protected Object getBackupValue(final String key, final Object value) {
129         if (PreferenceKeys.NOTIFICATION_RINGTONE.equals(key)) {
130             return getRingtoneTitle((String) value);
131         }
132 
133         return super.getBackupValue(key, value);
134     }
135 
136     @Override
getRestoreValue(final String key, final Object value)137     protected Object getRestoreValue(final String key, final Object value) {
138         if (PreferenceKeys.NOTIFICATION_RINGTONE.equals(key)) {
139             return getRingtoneUri((String) value);
140         }
141 
142         return super.getBackupValue(key, value);
143     }
144 
getRingtoneTitle(final String ringtoneUriString)145     private String getRingtoneTitle(final String ringtoneUriString) {
146         if (ringtoneUriString.length() == 0) {
147             return ringtoneUriString;
148         }
149         final Uri uri = Uri.parse(ringtoneUriString);
150         if (RingtoneManager.isDefault(uri)) {
151             return ringtoneUriString;
152         }
153         final RingtoneManager ringtoneManager = new RingtoneManager(getContext());
154         ringtoneManager.setType(RingtoneManager.TYPE_NOTIFICATION);
155         final Cursor cursor = ringtoneManager.getCursor();
156         try {
157             while (cursor.moveToNext()) {
158                 final Uri cursorUri = ContentUris.withAppendedId(
159                         Uri.parse(cursor.getString(RingtoneManager.URI_COLUMN_INDEX)),
160                         cursor.getLong(RingtoneManager.ID_COLUMN_INDEX));
161                 if (cursorUri.toString().equals(ringtoneUriString)) {
162                     final String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
163                     if (!Strings.isNullOrEmpty(title)) {
164                         return title;
165                     }
166                 }
167             }
168         } finally {
169             cursor.close();
170         }
171         return null;
172     }
173 
getRingtoneUri(final String name)174     private String getRingtoneUri(final String name) {
175         if (name.length() == 0 || RingtoneManager.isDefault(Uri.parse(name))) {
176             return name;
177         }
178 
179         final RingtoneManager ringtoneManager = new RingtoneManager(getContext());
180         ringtoneManager.setType(RingtoneManager.TYPE_NOTIFICATION);
181         final Cursor cursor = ringtoneManager.getCursor();
182         try {
183             while (cursor.moveToNext()) {
184                 String title = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX);
185                 if (name.equals(title)) {
186                     Uri uri = ContentUris.withAppendedId(
187                             Uri.parse(cursor.getString(RingtoneManager.URI_COLUMN_INDEX)),
188                             cursor.getLong(RingtoneManager.ID_COLUMN_INDEX));
189                     return uri.toString();
190                 }
191             }
192         } finally {
193             cursor.close();
194         }
195         return null;
196     }
197 
198     /**
199      * If <code>true</code>, we use inbox-defaults for notification settings. If <code>false</code>,
200      * we use standard defaults.
201      */
getUseInboxDefaultNotificationSettings()202     private boolean getUseInboxDefaultNotificationSettings() {
203         return mUseInboxDefaultNotificationSettings;
204     }
205 
isNotificationsEnabledSet()206     public boolean isNotificationsEnabledSet() {
207         return getSharedPreferences().contains(PreferenceKeys.NOTIFICATIONS_ENABLED);
208     }
209 
areNotificationsEnabled()210     public boolean areNotificationsEnabled() {
211         return getSharedPreferences().getBoolean(
212                 PreferenceKeys.NOTIFICATIONS_ENABLED, getUseInboxDefaultNotificationSettings());
213     }
214 
setNotificationsEnabled(final boolean enabled)215     public void setNotificationsEnabled(final boolean enabled) {
216         getEditor().putBoolean(PreferenceKeys.NOTIFICATIONS_ENABLED, enabled).apply();
217         notifyBackupPreferenceChanged();
218     }
219 
getNotificationRingtoneUri()220     public String getNotificationRingtoneUri() {
221         return getSharedPreferences().getString(PreferenceKeys.NOTIFICATION_RINGTONE,
222                 Settings.System.DEFAULT_NOTIFICATION_URI.toString());
223     }
224 
setNotificationRingtoneUri(final String uri)225     public void setNotificationRingtoneUri(final String uri) {
226         getEditor().putString(PreferenceKeys.NOTIFICATION_RINGTONE, uri).apply();
227         notifyBackupPreferenceChanged();
228     }
229 
isNotificationVibrateEnabled()230     public boolean isNotificationVibrateEnabled() {
231         return getSharedPreferences().getBoolean(PreferenceKeys.NOTIFICATION_VIBRATE, false);
232     }
233 
setNotificationVibrateEnabled(final boolean enabled)234     public void setNotificationVibrateEnabled(final boolean enabled) {
235         getEditor().putBoolean(PreferenceKeys.NOTIFICATION_VIBRATE, enabled).apply();
236         notifyBackupPreferenceChanged();
237     }
238 
isEveryMessageNotificationEnabled()239     public boolean isEveryMessageNotificationEnabled() {
240         return getSharedPreferences()
241                 .getBoolean(PreferenceKeys.NOTIFICATION_NOTIFY_EVERY_MESSAGE, false);
242     }
243 
setEveryMessageNotificationEnabled(final boolean enabled)244     public void setEveryMessageNotificationEnabled(final boolean enabled) {
245         getEditor().putBoolean(PreferenceKeys.NOTIFICATION_NOTIFY_EVERY_MESSAGE, enabled).apply();
246         notifyBackupPreferenceChanged();
247     }
248 
getNotificationActions(final Account account)249     public Set<String> getNotificationActions(final Account account) {
250         final boolean supportsArchiveRemoveLabel =
251                 mFolder.supportsCapability(FolderCapabilities.ARCHIVE)
252                 || mFolder.supportsCapability(FolderCapabilities.ALLOWS_REMOVE_CONVERSATION);
253         final boolean preferDelete = MailPrefs.RemovalActions.DELETE.equals(
254                 MailPrefs.get(getContext()).getRemovalAction(
255                         account.supportsCapability(AccountCapabilities.ARCHIVE)));
256         final NotificationActionType destructiveActionType =
257                 supportsArchiveRemoveLabel && !preferDelete ?
258                         NotificationActionType.ARCHIVE_REMOVE_LABEL : NotificationActionType.DELETE;
259         final String destructiveAction = destructiveActionType.getPersistedValue();
260 
261         final String replyAction =
262                 MailPrefs.get(getContext()).getDefaultReplyAll()
263                         ? NotificationActionType.REPLY_ALL.getPersistedValue()
264                         : NotificationActionType.REPLY.getPersistedValue();
265 
266         final Set<String> notificationActions = new LinkedHashSet<String>(2);
267         notificationActions.add(destructiveAction);
268         notificationActions.add(replyAction);
269 
270         return notificationActions;
271     }
272 }
273