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 */
16package com.android.mail.preferences;
17
18import android.content.ContentUris;
19import android.content.Context;
20import android.database.Cursor;
21import android.media.RingtoneManager;
22import android.net.Uri;
23import android.provider.Settings;
24
25import com.android.mail.providers.Account;
26import com.android.mail.providers.Folder;
27import com.android.mail.providers.UIProvider.AccountCapabilities;
28import com.android.mail.providers.UIProvider.FolderCapabilities;
29import com.android.mail.utils.NotificationActionUtils.NotificationActionType;
30import com.google.android.mail.common.base.Strings;
31import com.google.common.collect.ImmutableSet;
32
33import java.util.LinkedHashSet;
34import 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 */
40public 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     */
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     */
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
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
106    private static String buildSharedPrefsName(final String account, final String persistentId) {
107        return PREFS_NAME_PREFIX + '-' + account + '-' + persistentId;
108    }
109
110    @Override
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
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
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
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
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
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     */
202    private boolean getUseInboxDefaultNotificationSettings() {
203        return mUseInboxDefaultNotificationSettings;
204    }
205
206    public boolean isNotificationsEnabledSet() {
207        return getSharedPreferences().contains(PreferenceKeys.NOTIFICATIONS_ENABLED);
208    }
209
210    public boolean areNotificationsEnabled() {
211        return getSharedPreferences().getBoolean(
212                PreferenceKeys.NOTIFICATIONS_ENABLED, getUseInboxDefaultNotificationSettings());
213    }
214
215    public void setNotificationsEnabled(final boolean enabled) {
216        getEditor().putBoolean(PreferenceKeys.NOTIFICATIONS_ENABLED, enabled).apply();
217        notifyBackupPreferenceChanged();
218    }
219
220    public String getNotificationRingtoneUri() {
221        return getSharedPreferences().getString(PreferenceKeys.NOTIFICATION_RINGTONE,
222                Settings.System.DEFAULT_NOTIFICATION_URI.toString());
223    }
224
225    public void setNotificationRingtoneUri(final String uri) {
226        getEditor().putString(PreferenceKeys.NOTIFICATION_RINGTONE, uri).apply();
227        notifyBackupPreferenceChanged();
228    }
229
230    public boolean isNotificationVibrateEnabled() {
231        return getSharedPreferences().getBoolean(PreferenceKeys.NOTIFICATION_VIBRATE, false);
232    }
233
234    public void setNotificationVibrateEnabled(final boolean enabled) {
235        getEditor().putBoolean(PreferenceKeys.NOTIFICATION_VIBRATE, enabled).apply();
236        notifyBackupPreferenceChanged();
237    }
238
239    public boolean isEveryMessageNotificationEnabled() {
240        return getSharedPreferences()
241                .getBoolean(PreferenceKeys.NOTIFICATION_NOTIFY_EVERY_MESSAGE, false);
242    }
243
244    public void setEveryMessageNotificationEnabled(final boolean enabled) {
245        getEditor().putBoolean(PreferenceKeys.NOTIFICATION_NOTIFY_EVERY_MESSAGE, enabled).apply();
246        notifyBackupPreferenceChanged();
247    }
248
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