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