Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40bdf999 authored by Dan Applebaum's avatar Dan Applebaum Committed by Jesse Vincent
Browse files

Fixes Issue 2934 Fixes Issue 2935

Provides for storing Folder Settings in the central Preferences
Storage as a back-up to the settings stored on each folder.  In this
way, even if the LocalStore DB is recreated or otherwise lost, Folder
Settings can be recovered.

1) Does not change the methodology used to read settings while
running, nor the changes in r3107 & r3116 which tremendously improve
Accounts list loading time.

2) Loads Folder Settings from Preferences and stores on the folder
only when creating a new LocalFolder

3) Saves Folder Settings to Preferences and the DB row every time the
Folder Settings are changed.

4) When upgrading from DB version 41 to 42 or later, copies all
settings for existing folders from the DB storage to the Preferences
Storage.

5) Transactional bulk folder creation and single pass local folder
existence check during "Refresh folders" operation drastically reduces
time spent when refreshing folders from the remote store.

6) Uses prepared statement during Editor commit to reduce Preference
storing time.

Probably needs a reversion of r3239, but I'm unfamiliar with
translations, so am leaving that to others' discretion.
parent ea3619b7
Loading
Loading
Loading
Loading
+2 −8
Original line number Diff line number Diff line
@@ -143,14 +143,6 @@ public class FolderSettings extends K9PreferenceActivity
    public void onResume()
    {
        super.onResume();
        try
        {
            mFolder.refresh(Preferences.getPreferences(this));
        }
        catch (MessagingException me)
        {
            Log.e(K9.LOG_TAG, "Could not refresh folder preferences for folder " + mFolder.getName(), me);
        }
    }

    private void saveSettings() throws MessagingException
@@ -164,6 +156,8 @@ public class FolderSettings extends K9PreferenceActivity
        mFolder.setSyncClass(FolderClass.valueOf(mSyncClass.getValue()));
        mFolder.setPushClass(FolderClass.valueOf(mPushClass.getValue()));

        mFolder.save();

        FolderClass newPushClass = mFolder.getPushClass();
        FolderClass newDisplayClass = mFolder.getDisplayClass();

+14 −5
Original line number Diff line number Diff line
@@ -533,15 +533,24 @@ public class MessagingController implements Runnable

                    LocalStore localStore = account.getLocalStore();
                    HashSet<String> remoteFolderNames = new HashSet<String>();
                    for (int i = 0, count = remoteFolders.size(); i < count; i++)
                    List<LocalFolder> foldersToCreate = new LinkedList<LocalFolder>();

                    localFolders = localStore.getPersonalNamespaces(false);
                    HashSet<String> localFolderNames = new HashSet<String>();
                    for (Folder localFolder : localFolders)
                    {
                        LocalFolder localFolder = localStore.getFolder(remoteFolders.get(i).getName());
                        if (!localFolder.exists())
                        localFolderNames.add(localFolder.getName());
                    }
                    for (Folder remoteFolder : remoteFolders)
                    {
                        if (localFolderNames.contains(remoteFolder.getName()) == false)
                        {
                            localFolder.create(FolderType.HOLDS_MESSAGES, account.getDisplayCount());
                            LocalFolder localFolder = localStore.getFolder(remoteFolder.getName());
                            foldersToCreate.add(localFolder);
                        }
                        remoteFolderNames.add(remoteFolders.get(i).getName());
                        remoteFolderNames.add(remoteFolder.getName());
                    }
                    localStore.createFolders(foldersToCreate, account.getDisplayCount());

                    localFolders = localStore.getPersonalNamespaces(false);

+236 −64
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.util.Log;

@@ -101,7 +102,7 @@ public class LocalStore extends Store implements Serializable
    static private String GET_FOLDER_COLS = "id, name, unread_count, visible_limit, last_updated, status, push_state, last_pushed, flagged_count, integrate, top_group, poll_class, push_class, display_class";


    protected static final int DB_VERSION = 41;
    protected static final int DB_VERSION = 42;

    protected String uUid = null;

@@ -133,6 +134,11 @@ public class LocalStore extends Store implements Serializable
        database.switchProvider(newStorageProviderId);
    }

    protected SharedPreferences getPreferences()
    {
        return Preferences.getPreferences(mApplication).getPreferences();
    }

    private class StoreSchemaDefinition implements LockableDatabase.SchemaDefinition
    {
        @Override
@@ -334,8 +340,7 @@ public class LocalStore extends Store implements Serializable
                        try
                        {

                            SharedPreferences prefs = Preferences.getPreferences(mApplication).getPreferences();
                            SharedPreferences.Editor editor = prefs.edit();
                            SharedPreferences prefs = getPreferences();
                            cursor = db.rawQuery("SELECT id, name FROM folders", null);
                            while (cursor.moveToNext())
                            {
@@ -343,16 +348,13 @@ public class LocalStore extends Store implements Serializable
                                {
                                    int id = cursor.getInt(0);
                                    String name = cursor.getString(1);
                                    update41Metadata(db, prefs, editor, id, name);
                                    update41Metadata(db, prefs, id, name);
                                }
                                catch (Exception e)
                                {
                                    Log.e(K9.LOG_TAG," error trying to ugpgrade a folder class: " +e);
                                }
                            }
                            editor.commit();


                        }


@@ -369,6 +371,32 @@ public class LocalStore extends Store implements Serializable
                            }
                        }
                    }
                    if (db.getVersion() == 41)
                    {
                        try
                        {
                            long startTime = System.currentTimeMillis();
                            SharedPreferences.Editor editor = getPreferences().edit();

                            List<? extends Folder>  folders = getPersonalNamespaces(true);
                            for (Folder folder : folders)
                            {
                                if (folder instanceof LocalFolder)
                                {
                                    LocalFolder lFolder = (LocalFolder)folder;
                                    lFolder.save(editor);
                                }
                            }

                            editor.commit();
                            long endTime = System.currentTimeMillis();
                            Log.i(K9.LOG_TAG, "Putting folder preferences for " + folders.size() + " folders back into Preferences took " + (endTime - startTime) + " ms");
                        }
                        catch (Exception e)
                        {
                            Log.e(K9.LOG_TAG, "Could not replace Preferences in upgrade from DB_VERSION 41", e);
                        }
                    }
                }
            }

@@ -400,8 +428,7 @@ public class LocalStore extends Store implements Serializable
            //}
        }


        private void update41Metadata(final SQLiteDatabase  db, SharedPreferences prefs, SharedPreferences.Editor editor, int id, String name)
        private void update41Metadata(final SQLiteDatabase  db, SharedPreferences prefs, int id, String name)
        {


@@ -448,15 +475,6 @@ public class LocalStore extends Store implements Serializable
            db.execSQL("UPDATE folders SET integrate = ?, top_group = ?, poll_class=?, push_class =?, display_class = ? WHERE id = ?",
                       new Object[] { integrate, inTopGroup,syncClass,pushClass,displayClass, id });

            // now that we managed to update this folder, obliterate the old data

            editor.remove(uUid+"."+name + ".displayMode");
            editor.remove(uUid+"."+name + ".syncMode");
            editor.remove(uUid+"."+name + ".pushMode");
            editor.remove(uUid+"."+name + ".inTopGroup");
            editor.remove(uUid+"."+name + ".integrate");


        }
    }

@@ -1286,6 +1304,64 @@ public class LocalStore extends Store implements Serializable
        public int size;
    }

    public void createFolders(final List<LocalFolder> foldersToCreate, final int visibleLimit) throws UnavailableStorageException
    {
        database.execute(true, new DbCallback<Void>()
        {
            @Override
            public Void doDbWork(final SQLiteDatabase db) throws WrappedException
            {
                for (LocalFolder folder : foldersToCreate)
                {
                    String name = folder.getName();
                    final  LocalFolder.PreferencesHolder prefHolder = folder.new PreferencesHolder();

                    // When created, special folders should always be displayed
                    // inbox should be integrated
                    // and the inbox and drafts folders should be syncced by default
                    if (mAccount.isSpecialFolder(name))
                    {
                        prefHolder.inTopGroup = true;
                        prefHolder.displayClass = LocalFolder.FolderClass.FIRST_CLASS;
                        if (name.equalsIgnoreCase(K9.INBOX))
                        {
                            prefHolder.integrate = true;
                            prefHolder.pushClass = LocalFolder.FolderClass.FIRST_CLASS;
                        }
                        else
                        {
                            prefHolder.pushClass = LocalFolder.FolderClass.INHERITED;

                        }
                        if ( name.equalsIgnoreCase(K9.INBOX) ||
                                name.equalsIgnoreCase(mAccount.getDraftsFolderName()) )
                        {
                            prefHolder.syncClass = LocalFolder.FolderClass.FIRST_CLASS;
                        }
                        else
                        {
                            prefHolder.syncClass = LocalFolder.FolderClass.NO_CLASS;
                        }
                    }
                    folder.refresh(name, prefHolder);   // Recover settings from Preferences

                    db.execSQL("INSERT INTO folders (name, visible_limit, top_group, display_class, poll_class, push_class, integrate) VALUES (?, ?, ?, ?, ?, ?, ?)", new Object[]
                               {
                            name,
                                   visibleLimit,
                                   prefHolder.inTopGroup ? 1 : 0,
                                   prefHolder.displayClass.name(),
                                   prefHolder.syncClass.name(),
                                   prefHolder.pushClass.name(),
                                   prefHolder.integrate ? 1 : 0,
                               });

                }
                return null;
            }
        });
    }

    public class LocalFolder extends Folder implements Serializable
    {
        /**
@@ -1297,6 +1373,7 @@ public class LocalStore extends Store implements Serializable
        private int mUnreadMessageCount = -1;
        private int mFlaggedMessageCount = -1;
        private int mVisibleLimit = -1;
        private String prefId = null;
        private FolderClass mDisplayClass = FolderClass.NO_CLASS;
        private FolderClass mSyncClass = FolderClass.INHERITED;
        private FolderClass mPushClass = FolderClass.SECOND_CLASS;
@@ -1486,53 +1563,20 @@ public class LocalStore extends Store implements Serializable
            {
                throw new MessagingException("Folder " + mName + " already exists.");
            }
            database.execute(false, new DbCallback<Void>()
            {
                @Override
                public Void doDbWork(final SQLiteDatabase db) throws WrappedException
                {
                    db.execSQL("INSERT INTO folders (name, visible_limit) VALUES (?, ?)", new Object[]
                               {
                                   mName,
                                   visibleLimit
                               });

            List<LocalFolder> foldersToCreate = new ArrayList<LocalFolder>(1);
            foldersToCreate.add(this);
            LocalStore.this.createFolders(foldersToCreate, visibleLimit);

                    return null;
            return true;
        }
            });

            // When created, special folders should always be displayed
            // inbox should be integrated
            // and the inbox and drafts folders should be syncced by default
            if (mAccount.isSpecialFolder(mName))
        private class PreferencesHolder
        {
                LocalFolder f = new LocalFolder(mName);
                f.open(OpenMode.READ_WRITE);
                f.setInTopGroup(true);
                f.setDisplayClass(FolderClass.FIRST_CLASS);
                if (mName.equalsIgnoreCase(K9.INBOX))
                {
                    f.setIntegrate(true);
                    f.setPushClass(FolderClass.FIRST_CLASS);
                }
                else
                {
                    f.setPushClass(FolderClass.INHERITED);

                }
                if ( mName.equalsIgnoreCase(K9.INBOX) ||
                        mName.equalsIgnoreCase(mAccount.getDraftsFolderName()) )
                {
                    f.setSyncClass(FolderClass.FIRST_CLASS);
                }
                else
                {
                    f.setSyncClass(FolderClass.NO_CLASS);
                }
            }

            return true;
            FolderClass displayClass = mDisplayClass;
            FolderClass syncClass = mSyncClass;
            FolderClass pushClass = mPushClass;
            boolean inTopGroup = mInTopGroup;
            boolean integrate = mIntegrate;
        }

        @Override
@@ -1712,6 +1756,7 @@ public class LocalStore extends Store implements Serializable
                throw (MessagingException) e.getCause();
            }
        }

        public String getPushState()
        {
            return mPushState;
@@ -1788,6 +1833,134 @@ public class LocalStore extends Store implements Serializable
            updateFolderColumn( "integrate", mIntegrate ? 1 : 0 );
        }

        private String getPrefId(String name)
        {
            if (prefId == null)
            {
                prefId = uUid + "." + name;
            }

            return prefId;
        }

        private String getPrefId() throws MessagingException
        {
            open(OpenMode.READ_WRITE);
            return getPrefId(mName);

        }

        public void delete() throws MessagingException
        {
            String id = getPrefId();

            SharedPreferences.Editor editor = LocalStore.this.getPreferences().edit();

            editor.remove(id + ".displayMode");
            editor.remove(id + ".syncMode");
            editor.remove(id + ".pushMode");
            editor.remove(id + ".inTopGroup");
            editor.remove(id + ".integrate");

            editor.commit();
        }

        public void save() throws MessagingException
        {
            SharedPreferences.Editor editor = LocalStore.this.getPreferences().edit();
            save(editor);
            editor.commit();
        }

        public void save(SharedPreferences.Editor editor) throws MessagingException
        {
            String id = getPrefId();

            // there can be a lot of folders.  For the defaults, let's not save prefs, saving space, except for INBOX
            if (mDisplayClass == FolderClass.NO_CLASS && !K9.INBOX.equals(getName()))
            {
                editor.remove(id + ".displayMode");
            }
            else
            {
                editor.putString(id + ".displayMode", mDisplayClass.name());
            }

            if (mSyncClass == FolderClass.INHERITED && !K9.INBOX.equals(getName()))
            {
                editor.remove(id + ".syncMode");
            }
            else
            {
                editor.putString(id + ".syncMode", mSyncClass.name());
            }

            if (mPushClass == FolderClass.SECOND_CLASS && !K9.INBOX.equals(getName()))
            {
                editor.remove(id + ".pushMode");
            }
            else
            {
                editor.putString(id + ".pushMode", mPushClass.name());
            }
            editor.putBoolean(id + ".inTopGroup", mInTopGroup);

            editor.putBoolean(id + ".integrate", mIntegrate);

        }

        public void refresh(String name, PreferencesHolder prefHolder)
        {
            String id = getPrefId(name);

            SharedPreferences preferences = LocalStore.this.getPreferences();

            try
            {
                prefHolder.displayClass = FolderClass.valueOf(preferences.getString(id + ".displayMode",
                        prefHolder.displayClass.name()));
            }
            catch (Exception e)
            {
                Log.e(K9.LOG_TAG, "Unable to load displayMode for " + getName(), e);
            }
            if (prefHolder.displayClass == FolderClass.NONE)
            {
                prefHolder.displayClass = FolderClass.NO_CLASS;
            }

            try
            {
                prefHolder.syncClass = FolderClass.valueOf(preferences.getString(id  + ".syncMode",
                        prefHolder.syncClass.name()));
            }
            catch (Exception e)
            {
                Log.e(K9.LOG_TAG, "Unable to load syncMode for " + getName(), e);

            }
            if (prefHolder.syncClass == FolderClass.NONE)
            {
                prefHolder.syncClass = FolderClass.INHERITED;
            }

            try
            {
                prefHolder.pushClass = FolderClass.valueOf(preferences.getString(id  + ".pushMode",
                        prefHolder.pushClass.name()));
            }
            catch (Exception e)
            {
                Log.e(K9.LOG_TAG, "Unable to load pushMode for " + getName(), e);
            }
            if (prefHolder.pushClass == FolderClass.NONE)
            {
                prefHolder.pushClass = FolderClass.INHERITED;
            }
            prefHolder.inTopGroup = preferences.getBoolean(id + ".inTopGroup", prefHolder.inTopGroup);
            prefHolder.integrate = preferences.getBoolean(id + ".integrate", prefHolder.integrate);

        }

        @Override
        public void fetch(final Message[] messages, final FetchProfile fp, final MessageRetrievalListener listener)
@@ -3371,7 +3544,6 @@ public class LocalStore extends Store implements Serializable
        private boolean mToMe = false;
        private boolean mCcMe = false;


        private boolean mHeadersLoaded = false;
        private boolean mMessageDirty = false;

+3 −1
Original line number Diff line number Diff line
@@ -97,6 +97,7 @@ public class Editor implements android.content.SharedPreferences.Editor
                {
                    storage.remove(removeKey);
                }
                Map<String, String> insertables = new HashMap<String, String>();
                for (Entry<String, String> entry : changes.entrySet())
                {
                    String key = entry.getKey();
@@ -104,9 +105,10 @@ public class Editor implements android.content.SharedPreferences.Editor
                    String oldValue = snapshot.get(key);
                    if (removeAll || removals.contains(key) || !newValue.equals(oldValue))
                    {
                        storage.put(key, newValue);
                        insertables.put(key, newValue);
                    }
                }
                storage.put(insertables);
            }
        };
        storage.doInTransaction(committer);
+32 −1
Original line number Diff line number Diff line
@@ -4,7 +4,9 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.util.Log;

import com.fsck.k9.K9;
@@ -254,11 +256,40 @@ public class Storage implements SharedPreferences
    }

    protected void put(String key, String value)
    {
        ContentValues cv = generateCV(key, value);
        workingDB.get().insert("preferences_storage", "primkey", cv);
        liveUpdate(key, value);
    }

    protected void put(Map<String, String> insertables)
    {
        String sql = "insert into preferences_storage (primkey, value) VALUES (?, ?)";
        SQLiteStatement stmt = workingDB.get().compileStatement(sql);

        for (Map.Entry<String, String> entry : insertables.entrySet())
        {
            String key = entry.getKey();
            String value = entry.getValue();
            stmt.bindString(1, key);
            stmt.bindString(2, value);
            stmt.execute();
            stmt.clearBindings();
            liveUpdate(key, value);
        }
        stmt.close();
    }

    private ContentValues generateCV(String key, String value)
    {
        ContentValues cv = new ContentValues();
        cv.put("primkey", key);
        cv.put("value", value);
        workingDB.get().insert("preferences_storage", "primkey", cv);
        return cv;
    }

    private void liveUpdate(String key, String value)
    {
        workingStorage.get().put(key, value);

        keyChange(key);