com.android.email.activity.setup.MailboxSettings.java Source code

Java tutorial

Introduction

Here is the source code for com.android.email.activity.setup.MailboxSettings.java

Source

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.email.activity.setup;

import android.app.ActionBar;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.content.res.Resources;
import android.database.Cursor;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Vibrator;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.util.Pair;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.email.R;
import com.android.email.provider.EmailProvider;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.Policy;
import com.android.emailcommon.utility.EmailAsyncTask;
import com.android.emailcommon.utility.Utility;
import com.android.mail.preferences.FolderPreferences;
import com.android.mail.preferences.FolderPreferences.NotificationLight;
import com.android.mail.preferences.notifications.FolderNotificationLightPreference;
import com.android.mail.providers.Folder;
import com.android.mail.providers.UIProvider;
import com.android.mail.ui.MailAsyncTaskLoader;
import com.android.mail.utils.LogUtils;
import com.google.common.base.Preconditions;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * "Mailbox settings" activity.
 *
 * It's used to update per-mailbox sync settings.  It normally updates Mailbox settings, unless
 * the target mailbox is Inbox, in which case it updates Account settings instead.
 *
 * All changes made by the user will not be immediately saved to the database, as changing the
 * sync window may result in removal of messages.  Instead, we only save to the database in {@link
 * #onDestroy()}, unless it's called for configuration changes.
 */
public class MailboxSettings extends PreferenceActivity {
    public static final String PREFERENCE_SYNC_SETTINGS = "account_sync_settings";
    public static final String PREFERENCE_PER_FOLDER_NOTIFICATIONS = "account_per_folder_notifications";

    private static final String EXTRA_FOLDERS_URI = "FOLDERS_URI";
    private static final String EXTRA_INBOX_ID = "INBOX_ID";
    private static final String EXTRA_TYPE = "TYPE";

    private static final String EXTRA_HEADER_FOLDER_INDENT = "folder-indent";

    private static final int FOLDERS_LOADER_ID = 0;
    private Uri mFoldersUri;
    private int mInboxId;
    private String mType;
    private final List<Pair<Folder, String>> mFolders = new ArrayList<>();

    /**
     * Starts the activity
     */
    public static Intent getIntent(Context context, Uri foldersUri, Folder inbox, String type) {
        final Intent i = new Intent(context, MailboxSettings.class);
        i.putExtra(EXTRA_FOLDERS_URI, foldersUri);
        i.putExtra(EXTRA_INBOX_ID, inbox.id);
        i.putExtra(EXTRA_TYPE, type);
        return i;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // This needs to happen before super.onCreate() since that calls onBuildHeaders()
        mInboxId = getIntent().getIntExtra(EXTRA_INBOX_ID, -1);
        mFoldersUri = getIntent().getParcelableExtra(EXTRA_FOLDERS_URI);
        mType = getIntent().getStringExtra(EXTRA_TYPE);

        if (mFoldersUri != null) {
            getLoaderManager().initLoader(FOLDERS_LOADER_ID, null, new MailboxSettingsFolderLoaderCallbacks());
        }

        super.onCreate(savedInstanceState);

        // Always show "app up" as we expect our parent to be an Email activity.
        ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP);
            // Hide the app icon.
            actionBar.setIcon(android.R.color.transparent);
            actionBar.setDisplayUseLogoEnabled(false);
            if (mType != null && mType.equals(PREFERENCE_SYNC_SETTINGS)) {
                actionBar.setTitle(getString(R.string.mailbox_settings_activity_title));
            } else if (mType != null && mType.equals(PREFERENCE_PER_FOLDER_NOTIFICATIONS)) {
                actionBar.setTitle(getString(R.string.mailbox_notify_settings_activity_title));
            }
        }
    }

    @Override
    public void onBuildHeaders(List<Header> target) {
        if (mFolders.isEmpty()) {
            final Header dummy = new Header();
            dummy.titleRes = R.string.mailbox_name_display_inbox;
            dummy.fragment = MailboxSettingsFragment.class.getName();
            dummy.fragmentArguments = MailboxSettingsFragment.getArguments(mInboxId, null);

        } else {
            for (final Pair<Folder, String> f : mFolders) {
                final Header h = new Header();
                h.title = f.first.name;
                setMailboxHeaderIcon(h, f.first);
                h.extras = new Bundle();
                h.extras.putInt(EXTRA_HEADER_FOLDER_INDENT, f.second.split("\\/").length - 1);
                if (mType != null && mType.equals(PREFERENCE_SYNC_SETTINGS)) {
                    h.fragment = MailboxSettingsFragment.class.getName();
                    h.fragmentArguments = MailboxSettingsFragment.getArguments(f.first.id, mType);
                } else if (mType != null && mType.equals(PREFERENCE_PER_FOLDER_NOTIFICATIONS)) {
                    h.fragment = MailboxNotificationsFragment.class.getName();
                    h.fragmentArguments = MailboxNotificationsFragment.getArguments(f.first, mType);
                }
                target.add(h);
            }
        }

        setListAdapter(new MailboxHeadersAdapter(this, target));
    }

    @Override
    protected boolean isValidFragment(String fragmentName) {
        // Activity is not exported
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void setMailboxHeaderIcon(Header header, Folder folder) {
        if (folder.isSent()) {
            header.iconRes = R.drawable.ic_drawer_sent_24dp;
        } else if (folder.isInbox()) {
            header.iconRes = R.drawable.ic_drawer_inbox_24dp;
        } else {
            header.iconRes = folder.hasChildren ? R.drawable.ic_folder_parent_24dp
                    : R.drawable.ic_drawer_folder_24dp;
        }
    }

    /**
     * Setup the entries and entry values for the sync lookback preference
     * @param context the caller's context
     * @param pref a ListPreference to be set up
     * @param maxLookback The maximum lookback allowed, or 0 if no max.
     * @param showWithDefault Whether to show the version with default, or without.
     */
    public static void setupLookbackPreferenceOptions(final Context context, final ListPreference pref,
            final int maxLookback, final boolean showWithDefault) {
        final Resources resources = context.getResources();
        // Load the complete list of entries/values
        CharSequence[] entries;
        CharSequence[] values;
        final int offset;
        if (showWithDefault) {
            entries = resources.getTextArray(R.array.account_settings_mail_window_entries_with_default);
            values = resources.getTextArray(R.array.account_settings_mail_window_values_with_default);
            offset = 1;
        } else {
            entries = resources.getTextArray(R.array.account_settings_mail_window_entries);
            values = resources.getTextArray(R.array.account_settings_mail_window_values);
            offset = 0;
        }
        // If we have a maximum lookback policy, enforce it
        if (maxLookback > 0) {
            final int size = maxLookback + offset;
            entries = Arrays.copyOf(entries, size);
            values = Arrays.copyOf(values, size);
        }
        // Set up the preference
        pref.setEntries(entries);
        pref.setEntryValues(values);
        pref.setSummary(pref.getEntry());
    }

    private static class MailboxHeadersAdapter extends ArrayAdapter<Header> {
        private static class HeaderViewHolder {
            View spacer;
            ImageView icon;
            TextView title;
        }

        private LayoutInflater mInflater;
        private int mFolderIndent;

        public MailboxHeadersAdapter(Context context, List<Header> objects) {
            super(context, 0, objects);
            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mFolderIndent = (int) context.getResources().getDimension(R.dimen.child_folder_indent);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            HeaderViewHolder holder;
            View view;

            if (convertView == null) {
                view = mInflater.inflate(R.layout.preference_mailbox_item, parent, false);
                holder = new HeaderViewHolder();
                holder.spacer = view.findViewById(R.id.spacer);
                holder.icon = (ImageView) view.findViewById(R.id.icon);
                holder.title = (TextView) view.findViewById(android.R.id.title);
                view.setTag(holder);
            } else {
                view = convertView;
                holder = (HeaderViewHolder) view.getTag();
            }

            // All view fields must be updated every time, because the view may be recycled
            Header header = getItem(position);
            int headerIndent = header.extras.getInt(EXTRA_HEADER_FOLDER_INDENT, 0);
            holder.spacer.getLayoutParams().width = mFolderIndent * headerIndent;
            holder.icon.setImageResource(header.iconRes);
            holder.title.setText(header.getTitle(getContext().getResources()));
            return view;
        }
    }

    private class MailboxSettingsFolderLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {

        @Override
        public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
            return new CursorLoader(MailboxSettings.this, mFoldersUri, UIProvider.FOLDERS_PROJECTION, null, null,
                    null);
        }

        @Override
        public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
            if (cursor == null) {
                return;
            }
            mFolders.clear();

            // Convert the cursor to an temp array and map all the folders
            Map<Uri, Folder> folders = new HashMap<>();
            List<Folder> tmp = new ArrayList<>();
            Folder inbox = null;
            Folder sent = null;
            while (cursor.moveToNext()) {
                final Folder folder = new Folder(cursor);
                if (!folder.supportsCapability(UIProvider.FolderCapabilities.IS_VIRTUAL) && !folder.isTrash()
                        && !folder.isDraft() && !folder.isOutbox()) {
                    if (folder.id == mInboxId) {
                        inbox = folder;
                    } else if (folder.isSent()) {
                        sent = folder;
                    }
                    tmp.add(folder);
                    folders.put(folder.folderUri.fullUri, folder);
                }
            }

            // Create the hierarchical paths of all the folders
            int count = tmp.size();
            for (int i = 0; i < count; i++) {
                Folder folder = tmp.get(i);
                mFolders.add(new Pair<Folder, String>(folder, getHierarchicalFolder(folder, folders)));
            }

            // Sort folders by hierarchical path
            final String inboxFolderName = inbox.name;
            final String sentFolderName = sent.name;
            Collections.sort(mFolders, new Comparator<Pair<Folder, String>>() {
                private final Collator mCollator = Collator.getInstance();

                @Override
                public int compare(Pair<Folder, String> lhs, Pair<Folder, String> rhs) {
                    boolean lInbox = lhs.second.startsWith(inboxFolderName);
                    boolean rInbox = rhs.second.startsWith(inboxFolderName);
                    boolean lSent = lhs.second.startsWith(sentFolderName);
                    boolean rSent = rhs.second.startsWith(sentFolderName);
                    String lParent = getHierarchicalParentFolder(lhs.second);
                    String rParent = getHierarchicalParentFolder(rhs.second);
                    if (lInbox && !rInbox) {
                        return -1;
                    } else if (!lInbox && rInbox) {
                        return 1;
                    }
                    if (lSent && !rSent) {
                        return -1;
                    } else if (!lSent && rSent) {
                        return 1;
                    }
                    if (lhs.second.startsWith(rhs.second)) {
                        return 1;
                    }
                    if (rhs.second.startsWith(lhs.second)) {
                        return -1;
                    }
                    if (lParent != null && rParent != null && lParent.equals(rParent)) {
                        return mCollator.compare(lhs.first.name, rhs.first.name);
                    }
                    return mCollator.compare(lhs.second, rhs.second);
                }
            });

            invalidateHeaders();
        }

        @Override
        public void onLoaderReset(Loader<Cursor> cursorLoader) {
            mFolders.clear();
        }

        private String getHierarchicalFolder(Folder folder, Map<Uri, Folder> folders) {
            if (!TextUtils.isEmpty(folder.hierarchicalDesc)) {
                return folder.hierarchicalDesc;
            }
            String name = folder.name;
            Folder tmp = folder;
            while (tmp != null && tmp.parent != null && !tmp.parent.toString().isEmpty()) {
                tmp = folders.get(tmp.parent);
                if (tmp != null) {
                    name = tmp.name + "/" + name;
                }
            }
            return name;
        }

        private String getHierarchicalParentFolder(String folder) {
            int pos = folder.lastIndexOf("/");
            if (pos != -1) {
                return folder.substring(0, pos);
            }
            return null;
        }
    }

    public static class MailboxNotificationsFragment extends PreferenceFragment {
        private static final String EXTRA_MAILBOX_ID = "MailboxId";
        private static final String EXTRA_MAILBOX_PERSISTEND_ID = "MailboxPersistentId";
        private static final String EXTRA_MAILBOX_IS_INBOX = "MailboxIsInbox";

        private static final String BUNDLE_ACCOUNT = "MailboxNotifySettings.account";
        private static final String BUNDLE_MAILBOX = "MailboxNotifySettings.mailbox";
        private static final String BUNDLE_NOTIF_ENABLED = "MailboxNotifySettings.enabled";
        private static final String BUNDLE_NOTIF_RINGTONE = "MailboxSettings.ringtone";
        private static final String BUNDLE_NOTIF_VIBRATE = "MailboxSettings.vibrate";
        private static final String BUNDLE_NOTIF_LIGHTS = "MailboxSettings.lights";

        private static final String PREF_NOTIF_ENABLED_KEY = "notifications-enabled";
        private static final String PREF_NOTIF_RINGTONE_KEY = "notification-ringtone";
        private static final String PREF_NOTIF_VIBRATE_KEY = "notification-vibrate";
        private static final String PREF_NOTIF_LIGHTS_KEY = "notification-lights";

        private static final int RINGTONE_REQUEST_CODE = MailboxNotificationsFragment.class.hashCode();

        private FolderPreferences mPreferences;
        private com.android.mail.providers.Account mUiAccount;
        private Account mAccount;
        private Mailbox mMailbox;

        private CheckBoxPreference mPrefNotifEnabled;
        private Preference mPrefNotifRingtone;
        private CheckBoxPreference mPrefNotifVibrate;
        private FolderNotificationLightPreference mPrefNotifLights;

        private boolean mOldMailboxEnabled;
        private String mOldMailboxRingtone;
        private boolean mOldMailboxVibrate;
        private String mOldMailboxLights;

        private Uri mRingtoneUri;
        private Ringtone mRingtone;

        private static Bundle getArguments(Folder folder, String type) {
            final Bundle b = new Bundle(4);
            b.putLong(EXTRA_MAILBOX_ID, folder.id);
            b.putString(EXTRA_MAILBOX_PERSISTEND_ID, folder.persistentId);
            b.putBoolean(EXTRA_MAILBOX_IS_INBOX, folder.isInbox());
            b.putString(EXTRA_TYPE, type);
            return b;
        }

        public MailboxNotificationsFragment() {
        }

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            final long mailboxId = getArguments().getLong(EXTRA_MAILBOX_ID, Mailbox.NO_MAILBOX);
            final String mailboxPersistenId = getArguments().getString(EXTRA_MAILBOX_PERSISTEND_ID, null);
            final boolean mailboxIsInbox = getArguments().getBoolean(EXTRA_MAILBOX_IS_INBOX, false);
            if (mailboxId == Mailbox.NO_MAILBOX || mailboxPersistenId == null) {
                getActivity().finish();
            }

            addPreferencesFromResource(R.xml.mailbox_notifications_preferences);

            mPrefNotifEnabled = (CheckBoxPreference) findPreference(PREF_NOTIF_ENABLED_KEY);
            mPrefNotifRingtone = findPreference(PREF_NOTIF_RINGTONE_KEY);
            mPrefNotifRingtone.setOnPreferenceClickListener(new OnPreferenceClickListener() {
                @Override
                public boolean onPreferenceClick(Preference preference) {
                    showRingtonePicker();
                    return true;
                }
            });
            mPrefNotifVibrate = (CheckBoxPreference) findPreference(PREF_NOTIF_VIBRATE_KEY);
            Vibrator vibrator = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
            if (!vibrator.hasVibrator()) {
                mPrefNotifVibrate.setChecked(false);
                getPreferenceScreen().removePreference(mPrefNotifVibrate);
                mPrefNotifVibrate = null;
            }
            mPrefNotifLights = (FolderNotificationLightPreference) findPreference(PREF_NOTIF_LIGHTS_KEY);
            boolean isArgbNotifColorSupported = getResources()
                    .getBoolean(com.android.internal.R.bool.config_multiColorNotificationLed);
            if (mPrefNotifLights != null && !isArgbNotifColorSupported) {
                getPreferenceScreen().removePreference(mPrefNotifLights);
            }

            if (savedInstanceState != null) {
                mAccount = savedInstanceState.getParcelable(BUNDLE_ACCOUNT);
                mMailbox = savedInstanceState.getParcelable(BUNDLE_MAILBOX);
                mPreferences = new FolderPreferences(getActivity(), mAccount.mEmailAddress, mailboxPersistenId,
                        mailboxIsInbox);

                mPrefNotifEnabled.setChecked(savedInstanceState.getBoolean(BUNDLE_NOTIF_ENABLED));
                setRingtone(savedInstanceState.getString(BUNDLE_NOTIF_RINGTONE));
                if (mPrefNotifVibrate != null) {
                    mPrefNotifVibrate.setChecked(savedInstanceState.getBoolean(BUNDLE_NOTIF_VIBRATE));
                }
                NotificationLight notifLight = NotificationLight
                        .fromStringPref(savedInstanceState.getString(BUNDLE_NOTIF_LIGHTS, ""));
                updateNotificationLight(notifLight);

                onDataLoaded();
            } else {
                // Make them disabled until we load data
                enablePreferences(false);
                getLoaderManager().initLoader(0, getArguments(), new MailboxLoaderCallbacks());
            }
        }

        private void setRingtone(String ringtone) {
            if (!TextUtils.isEmpty(ringtone)) {
                mRingtoneUri = Uri.parse(ringtone);
                mRingtone = RingtoneManager.getRingtone(getActivity(), mRingtoneUri);
            } else {
                mRingtoneUri = null;
                mRingtone = null;
            }
            setRingtoneSummary();
        }

        private void setRingtoneSummary() {
            final String summary = mRingtone != null ? mRingtone.getTitle(getActivity())
                    : getString(R.string.silent_ringtone);
            mPrefNotifRingtone.setSummary(summary);
        }

        @Override
        public void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == RINGTONE_REQUEST_CODE) {
                if (resultCode == Activity.RESULT_OK && data != null) {
                    Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
                    String ringtone = "";
                    if (uri != null) {
                        ringtone = uri.toString();
                    }
                    setRingtone(ringtone);
                }
            }
        }

        private void enablePreferences(boolean enabled) {
            mPrefNotifEnabled.setEnabled(enabled);
            mPrefNotifRingtone.setEnabled(enabled);
            if (mPrefNotifVibrate != null) {
                mPrefNotifVibrate.setEnabled(enabled);
            }
            if (mPrefNotifLights != null) {
                mPrefNotifLights.setEnabled(enabled);
            }
        }

        @Override
        public void onSaveInstanceState(@NonNull Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putParcelable(BUNDLE_ACCOUNT, mAccount);
            outState.putParcelable(BUNDLE_MAILBOX, mMailbox);
            outState.putBoolean(BUNDLE_NOTIF_ENABLED, mPrefNotifEnabled.isChecked());
            String ringtoneUri = "";
            if (mRingtoneUri != null) {
                ringtoneUri = mRingtoneUri.toString();
            }
            outState.putString(BUNDLE_NOTIF_RINGTONE, ringtoneUri);
            outState.putBoolean(PREF_NOTIF_VIBRATE_KEY,
                    mPrefNotifVibrate != null ? mPrefNotifVibrate.isChecked() : false);
            outState.putString(BUNDLE_NOTIF_LIGHTS, getNotificationLightPref());
        }

        /**
         * We save all the settings in onDestroy, *unless it's for configuration changes*.
         */
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (!getActivity().isChangingConfigurations()) {
                savePreferences();
            }
        }

        private void loadOldPreferencesValues() {
            mOldMailboxEnabled = mPreferences.areNotificationsEnabled();
            mOldMailboxRingtone = mPreferences.getNotificationRingtoneUri();
            mOldMailboxVibrate = mPreferences.isNotificationVibrateEnabled();
            mOldMailboxLights = mPreferences.getNotificationLight().toStringPref();
        }

        /**
         * Shows the system ringtone picker.
         */
        private void showRingtonePicker() {
            Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
            final String ringtoneUri = mPreferences.getNotificationRingtoneUri();
            if (!TextUtils.isEmpty(ringtoneUri)) {
                intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(ringtoneUri));
            }
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, Settings.System.DEFAULT_NOTIFICATION_URI);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true);
            intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
            startActivityForResult(intent, RINGTONE_REQUEST_CODE);
        }

        /**
         * Called when {@link #mMailbox} is loaded (either by the loader or from the saved state).
         */
        private void onDataLoaded() {
            Preconditions.checkNotNull(mAccount);
            Preconditions.checkNotNull(mMailbox);

            // Update the title with the mailbox name.
            final ActionBar actionBar = getActivity().getActionBar();
            final String mailboxName = mMailbox.mDisplayName;
            if (actionBar != null) {
                actionBar.setTitle(mailboxName);
                actionBar.setSubtitle(getString(R.string.mailbox_notify_settings_activity_title));
            } else {
                getActivity().setTitle(
                        getString(R.string.mailbox_notify_settings_activity_title_with_mailbox, mailboxName));
            }
        }

        private void updateNotificationLight(NotificationLight notificationLight) {
            if (mPrefNotifLights == null) {
                return;
            }

            if (notificationLight.mOn) {
                mPrefNotifLights.setColor(notificationLight.mColor);
                mPrefNotifLights.setOnOffValue(notificationLight.mTimeOn, notificationLight.mTimeOff);
            } else {
                int color = mUiAccount != null && mUiAccount.color != 0 ? mUiAccount.color
                        : FolderNotificationLightPreference.DEFAULT_COLOR;
                mPrefNotifLights.setColor(color);
                mPrefNotifLights.setOnOffValue(FolderNotificationLightPreference.DEFAULT_TIME,
                        FolderNotificationLightPreference.DEFAULT_TIME);
            }
            mPrefNotifLights.setOn(notificationLight.mOn);
        }

        private String getNotificationLightPref() {
            return mPrefNotifLights == null || !mPrefNotifLights.getOn() ? ""
                    : TextUtils.join("|", new Integer[] { mPrefNotifLights.getColor(),
                            mPrefNotifLights.getOnValue(), mPrefNotifLights.getOffValue() });
        }

        /**
         * Save changes to the preferences folder backend.
         *
         * Note it's called from {@link #onDestroy()}
         */
        private void savePreferences() {
            if (mPreferences == null) {
                return;
            }

            boolean mailboxEnabled = mPrefNotifEnabled.isChecked();
            String mailboxRingtone = "";
            if (mRingtoneUri != null) {
                mailboxRingtone = mRingtoneUri.toString();
            }
            boolean mailboxVibrate = mPrefNotifVibrate != null ? mPrefNotifVibrate.isChecked() : false;
            String mailboxLights = getNotificationLightPref();
            if (mailboxEnabled != mOldMailboxEnabled) {
                mPreferences.setNotificationsEnabled(mailboxEnabled);
                mOldMailboxEnabled = mailboxEnabled;
            }
            if (!mailboxRingtone.equals(mOldMailboxRingtone)) {
                mPreferences.setNotificationRingtoneUri(mailboxRingtone);
                mOldMailboxRingtone = mailboxRingtone;
            }
            if (mailboxVibrate != mOldMailboxVibrate) {
                mPreferences.setNotificationVibrateEnabled(mailboxVibrate);
                mOldMailboxVibrate = mailboxVibrate;
            }
            if (!mailboxLights.equals(mOldMailboxLights)) {
                mPreferences.setNotificationLights(NotificationLight.fromStringPref(mailboxLights));
                mOldMailboxLights = mailboxLights;
            }
        }

        private static class MailboxLoader extends MailAsyncTaskLoader<Map<String, Object>> {

            public static final String RESULT_KEY_MAILBOX = "mailbox";
            public static final String RESULT_KEY_ACCOUNT = "account";
            public static final String RESULT_KEY_UIACCOUNT = "uiAccount";

            private final long mMailboxId;

            private MailboxLoader(Context context, long mailboxId) {
                super(context);
                mMailboxId = mailboxId;
            }

            @Override
            public Map<String, Object> loadInBackground() {
                final Map<String, Object> result = new HashMap<>();

                final Mailbox mailbox = Mailbox.restoreMailboxWithId(getContext(), mMailboxId);
                if (mailbox == null) {
                    return null;
                }
                Account account = Account.restoreAccountWithId(getContext(), mailbox.mAccountKey);
                if (account == null) {
                    return null;
                }
                result.put(RESULT_KEY_MAILBOX, mailbox);
                result.put(RESULT_KEY_ACCOUNT, account);

                // Recover the uiAccount
                final Cursor uiAccountCursor = getContext().getContentResolver().query(
                        EmailProvider.uiUri("uiaccount", account.getId()), UIProvider.ACCOUNTS_PROJECTION, null,
                        null, null);

                if (uiAccountCursor != null && uiAccountCursor.moveToFirst()) {
                    final com.android.mail.providers.Account uiAccount = com.android.mail.providers.Account
                            .builder().buildFrom(uiAccountCursor);
                    result.put(RESULT_KEY_UIACCOUNT, uiAccount);
                }

                return result;
            }

            @Override
            protected void onDiscardResult(Map<String, Object> result) {
            }
        }

        private class MailboxLoaderCallbacks implements LoaderManager.LoaderCallbacks<Map<String, Object>> {

            private long mMailboxId;
            private String mMailboxPersistentId;
            private boolean mMailboxIsInbox;

            @Override
            public Loader<Map<String, Object>> onCreateLoader(int id, Bundle args) {
                mMailboxId = getArguments().getLong(EXTRA_MAILBOX_ID, Mailbox.NO_MAILBOX);
                mMailboxPersistentId = getArguments().getString(EXTRA_MAILBOX_PERSISTEND_ID, null);
                mMailboxIsInbox = getArguments().getBoolean(EXTRA_MAILBOX_IS_INBOX, false);
                return new MailboxLoader(getActivity(), mMailboxId);
            }

            @Override
            public void onLoadFinished(Loader<Map<String, Object>> loader, Map<String, Object> data) {
                final Mailbox mailbox = (Mailbox) (data == null ? null
                        : data.get(MailboxLoader.RESULT_KEY_MAILBOX));
                final Account account = (Account) (data == null ? null
                        : data.get(MailboxLoader.RESULT_KEY_ACCOUNT));
                if (mailbox == null || account == null) {
                    getActivity().finish();
                    return;
                }

                mUiAccount = (com.android.mail.providers.Account) data.get(MailboxLoader.RESULT_KEY_UIACCOUNT);
                mAccount = account;
                mMailbox = mailbox;
                mPreferences = new FolderPreferences(getActivity(), mAccount.mEmailAddress, mMailboxPersistentId,
                        mMailboxIsInbox);
                loadOldPreferencesValues();

                mPrefNotifEnabled.setChecked(mPreferences.areNotificationsEnabled());
                setRingtone(mPreferences.getNotificationRingtoneUri());
                if (mPrefNotifVibrate != null) {
                    mPrefNotifVibrate.setChecked(mPreferences.isNotificationVibrateEnabled());
                }
                updateNotificationLight(mPreferences.getNotificationLight());

                onDataLoaded();
                if (mMailbox.mType != Mailbox.TYPE_DRAFTS) {
                    enablePreferences(true);
                }
            }

            @Override
            public void onLoaderReset(Loader<Map<String, Object>> loader) {
            }
        }
    }

    public static class MailboxSettingsFragment extends PreferenceFragment {
        private static final String EXTRA_MAILBOX_ID = "MailboxId";

        private static final String BUNDLE_MAILBOX = "MailboxSettings.mailbox";
        private static final String BUNDLE_MAX_LOOKBACK = "MailboxSettings.maxLookback";
        private static final String BUNDLE_SYNC_ENABLED_VALUE = "MailboxSettings.syncEnabled";
        private static final String BUNDLE_SYNC_WINDOW_VALUE = "MailboxSettings.syncWindow";

        private static final String PREF_SYNC_ENABLED_KEY = "sync_enabled";
        private static final String PREF_SYNC_WINDOW_KEY = "sync_window";

        private Mailbox mMailbox;
        /** The maximum lookback allowed for this mailbox, or 0 if no max. */
        private int mMaxLookback;

        private CheckBoxPreference mSyncEnabledPref;
        private ListPreference mSyncLookbackPref;

        private static Bundle getArguments(long mailboxId, String type) {
            final Bundle b = new Bundle(1);
            b.putLong(EXTRA_MAILBOX_ID, mailboxId);
            b.putString(EXTRA_TYPE, type);
            return b;
        }

        public MailboxSettingsFragment() {
        }

        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            final long mailboxId = getArguments().getLong(EXTRA_MAILBOX_ID, Mailbox.NO_MAILBOX);
            if (mailboxId == Mailbox.NO_MAILBOX) {
                getActivity().finish();
            }

            addPreferencesFromResource(R.xml.mailbox_preferences);

            mSyncEnabledPref = (CheckBoxPreference) findPreference(PREF_SYNC_ENABLED_KEY);
            mSyncLookbackPref = (ListPreference) findPreference(PREF_SYNC_WINDOW_KEY);

            mSyncLookbackPref.setOnPreferenceChangeListener(mPreferenceChanged);

            if (savedInstanceState != null) {
                mMailbox = savedInstanceState.getParcelable(BUNDLE_MAILBOX);
                mMaxLookback = savedInstanceState.getInt(BUNDLE_MAX_LOOKBACK);
                mSyncEnabledPref.setChecked(savedInstanceState.getBoolean(BUNDLE_SYNC_ENABLED_VALUE));
                mSyncLookbackPref.setValue(savedInstanceState.getString(BUNDLE_SYNC_WINDOW_VALUE));
                onDataLoaded();
            } else {
                // Make them disabled until we load data
                enablePreferences(false);
                getLoaderManager().initLoader(0, getArguments(), new MailboxLoaderCallbacks());
            }
        }

        private void enablePreferences(boolean enabled) {
            mSyncEnabledPref.setEnabled(enabled);
            mSyncLookbackPref.setEnabled(enabled);
        }

        @Override
        public void onSaveInstanceState(@NonNull Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putParcelable(BUNDLE_MAILBOX, mMailbox);
            outState.putInt(BUNDLE_MAX_LOOKBACK, mMaxLookback);
            outState.putBoolean(BUNDLE_SYNC_ENABLED_VALUE, mSyncEnabledPref.isChecked());
            outState.putString(BUNDLE_SYNC_WINDOW_VALUE, mSyncLookbackPref.getValue());
        }

        /**
         * We save all the settings in onDestroy, *unless it's for configuration changes*.
         */
        @Override
        public void onDestroy() {
            super.onDestroy();
            if (!getActivity().isChangingConfigurations()) {
                saveToDatabase();
            }
        }

        private static class MailboxLoader extends MailAsyncTaskLoader<Map<String, Object>> {
            /** Projection for loading an account's policy key. */
            private static final String[] POLICY_KEY_PROJECTION = { AccountColumns.POLICY_KEY };
            private static final int POLICY_KEY_COLUMN = 0;

            /** Projection for loading the max email lookback. */
            private static final String[] MAX_EMAIL_LOOKBACK_PROJECTION = { Policy.MAX_EMAIL_LOOKBACK };
            private static final int MAX_EMAIL_LOOKBACK_COLUMN = 0;

            public static final String RESULT_KEY_MAILBOX = "mailbox";
            public static final String RESULT_KEY_MAX_LOOKBACK = "maxLookback";

            private final long mMailboxId;

            private MailboxLoader(Context context, long mailboxId) {
                super(context);
                mMailboxId = mailboxId;
            }

            @Override
            public Map<String, Object> loadInBackground() {
                final Map<String, Object> result = new HashMap<>();

                final Mailbox mailbox = Mailbox.restoreMailboxWithId(getContext(), mMailboxId);
                result.put(RESULT_KEY_MAILBOX, mailbox);
                result.put(RESULT_KEY_MAX_LOOKBACK, 0);

                if (mailbox == null) {
                    return result;
                }

                // Get the max lookback from our policy, if we have one.
                final Long policyKey = Utility.getFirstRowLong(getContext(),
                        ContentUris.withAppendedId(Account.CONTENT_URI, mailbox.mAccountKey), POLICY_KEY_PROJECTION,
                        null, null, null, POLICY_KEY_COLUMN);
                if (policyKey == null) {
                    // No policy, nothing to look up.
                    return result;
                }

                final int maxLookback = Utility.getFirstRowInt(getContext(),
                        ContentUris.withAppendedId(Policy.CONTENT_URI, policyKey), MAX_EMAIL_LOOKBACK_PROJECTION,
                        null, null, null, MAX_EMAIL_LOOKBACK_COLUMN, 0);
                result.put(RESULT_KEY_MAX_LOOKBACK, maxLookback);

                return result;
            }

            @Override
            protected void onDiscardResult(Map<String, Object> result) {
            }
        }

        private class MailboxLoaderCallbacks implements LoaderManager.LoaderCallbacks<Map<String, Object>> {
            @Override
            public Loader<Map<String, Object>> onCreateLoader(int id, Bundle args) {
                final long mailboxId = args.getLong(EXTRA_MAILBOX_ID);
                return new MailboxLoader(getActivity(), mailboxId);
            }

            @Override
            public void onLoadFinished(Loader<Map<String, Object>> loader, Map<String, Object> data) {
                final Mailbox mailbox = (Mailbox) (data == null ? null
                        : data.get(MailboxLoader.RESULT_KEY_MAILBOX));
                if (mailbox == null) {
                    getActivity().finish();
                    return;
                }

                mMailbox = mailbox;
                mMaxLookback = (Integer) data.get(MailboxLoader.RESULT_KEY_MAX_LOOKBACK);

                mSyncEnabledPref.setChecked(mMailbox.mSyncInterval != 0);
                mSyncLookbackPref.setValue(String.valueOf(mMailbox.mSyncLookback));
                onDataLoaded();
                if (mMailbox.mType != Mailbox.TYPE_DRAFTS) {
                    enablePreferences(true);
                }
            }

            @Override
            public void onLoaderReset(Loader<Map<String, Object>> loader) {
            }
        }

        /**
         * Called when {@link #mMailbox} is loaded (either by the loader or from the saved state).
         */
        private void onDataLoaded() {
            Preconditions.checkNotNull(mMailbox);

            // Update the title with the mailbox name.
            final ActionBar actionBar = getActivity().getActionBar();
            final String mailboxName = mMailbox.mDisplayName;
            if (actionBar != null) {
                actionBar.setTitle(mailboxName);
                actionBar.setSubtitle(getString(R.string.mailbox_settings_activity_title));
            } else {
                getActivity()
                        .setTitle(getString(R.string.mailbox_settings_activity_title_with_mailbox, mailboxName));
            }

            MailboxSettings.setupLookbackPreferenceOptions(getActivity(), mSyncLookbackPref, mMaxLookback, true);
        }

        private final OnPreferenceChangeListener mPreferenceChanged = new OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                mSyncLookbackPref.setValue((String) newValue);
                mSyncLookbackPref.setSummary(mSyncLookbackPref.getEntry());
                return false;
            }
        };

        /**
         * Save changes to the database.
         *
         * Note it's called from {@link #onDestroy()}, which is called on the UI thread where we're
         * not allowed to touch the database, so it uses {@link EmailAsyncTask} to do the save on a
         * bg thread. This unfortunately means there's a chance that the app gets killed before the
         * save is finished.
         */
        private void saveToDatabase() {
            if (mMailbox == null) {
                // We haven't loaded yet, nothing to save.
                return;
            }
            final int syncInterval = mSyncEnabledPref.isChecked() ? 1 : 0;
            final int syncLookback = Integer.valueOf(mSyncLookbackPref.getValue());

            final boolean syncIntervalChanged = syncInterval != mMailbox.mSyncInterval;
            final boolean syncLookbackChanged = syncLookback != mMailbox.mSyncLookback;

            // Only save if a preference has changed value.
            if (!syncIntervalChanged && !syncLookbackChanged) {
                return;
            }

            LogUtils.i(Logging.LOG_TAG, "Saving mailbox settings...");
            enablePreferences(false);

            final long id = mMailbox.mId;
            final Context context = getActivity().getApplicationContext();

            new EmailAsyncTask<Void, Void, Void>(null /* no cancel */) {
                @Override
                protected Void doInBackground(Void... params) {
                    final ContentValues cv = new ContentValues(2);
                    final Uri uri;
                    if (syncIntervalChanged) {
                        cv.put(MailboxColumns.SYNC_INTERVAL, syncInterval);
                    }
                    if (syncLookbackChanged) {
                        cv.put(MailboxColumns.SYNC_LOOKBACK, syncLookback);
                    }
                    uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, id);
                    context.getContentResolver().update(uri, cv, null, null);

                    LogUtils.i(Logging.LOG_TAG, "Saved: " + uri);
                    return null;
                }
            }.executeSerial((Void[]) null);
        }
    }
}