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 Original line Diff line number Diff line
@@ -143,14 +143,6 @@ public class FolderSettings extends K9PreferenceActivity
    public void onResume()
    public void onResume()
    {
    {
        super.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
    private void saveSettings() throws MessagingException
@@ -164,6 +156,8 @@ public class FolderSettings extends K9PreferenceActivity
        mFolder.setSyncClass(FolderClass.valueOf(mSyncClass.getValue()));
        mFolder.setSyncClass(FolderClass.valueOf(mSyncClass.getValue()));
        mFolder.setPushClass(FolderClass.valueOf(mPushClass.getValue()));
        mFolder.setPushClass(FolderClass.valueOf(mPushClass.getValue()));


        mFolder.save();

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


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


                    LocalStore localStore = account.getLocalStore();
                    LocalStore localStore = account.getLocalStore();
                    HashSet<String> remoteFolderNames = new HashSet<String>();
                    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());
                        localFolderNames.add(localFolder.getName());
                        if (!localFolder.exists())
                    }
                    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);
                    localFolders = localStore.getPersonalNamespaces(false);


+236 −64
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.net.Uri;
import android.util.Log;
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";
    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;
    protected String uUid = null;


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


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

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


                            SharedPreferences prefs = Preferences.getPreferences(mApplication).getPreferences();
                            SharedPreferences prefs = getPreferences();
                            SharedPreferences.Editor editor = prefs.edit();
                            cursor = db.rawQuery("SELECT id, name FROM folders", null);
                            cursor = db.rawQuery("SELECT id, name FROM folders", null);
                            while (cursor.moveToNext())
                            while (cursor.moveToNext())
                            {
                            {
@@ -343,16 +348,13 @@ public class LocalStore extends Store implements Serializable
                                {
                                {
                                    int id = cursor.getInt(0);
                                    int id = cursor.getInt(0);
                                    String name = cursor.getString(1);
                                    String name = cursor.getString(1);
                                    update41Metadata(db, prefs, editor, id, name);
                                    update41Metadata(db, prefs, id, name);
                                }
                                }
                                catch (Exception e)
                                catch (Exception e)
                                {
                                {
                                    Log.e(K9.LOG_TAG," error trying to ugpgrade a folder class: " +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, int id, String name)
        private void update41Metadata(final SQLiteDatabase  db, SharedPreferences prefs, SharedPreferences.Editor editor, 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 = ?",
            db.execSQL("UPDATE folders SET integrate = ?, top_group = ?, poll_class=?, push_class =?, display_class = ? WHERE id = ?",
                       new Object[] { integrate, inTopGroup,syncClass,pushClass,displayClass, 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 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
    public class LocalFolder extends Folder implements Serializable
    {
    {
        /**
        /**
@@ -1297,6 +1373,7 @@ public class LocalStore extends Store implements Serializable
        private int mUnreadMessageCount = -1;
        private int mUnreadMessageCount = -1;
        private int mFlaggedMessageCount = -1;
        private int mFlaggedMessageCount = -1;
        private int mVisibleLimit = -1;
        private int mVisibleLimit = -1;
        private String prefId = null;
        private FolderClass mDisplayClass = FolderClass.NO_CLASS;
        private FolderClass mDisplayClass = FolderClass.NO_CLASS;
        private FolderClass mSyncClass = FolderClass.INHERITED;
        private FolderClass mSyncClass = FolderClass.INHERITED;
        private FolderClass mPushClass = FolderClass.SECOND_CLASS;
        private FolderClass mPushClass = FolderClass.SECOND_CLASS;
@@ -1486,53 +1563,20 @@ public class LocalStore extends Store implements Serializable
            {
            {
                throw new MessagingException("Folder " + mName + " already exists.");
                throw new MessagingException("Folder " + mName + " already exists.");
            }
            }
            database.execute(false, new DbCallback<Void>()
            List<LocalFolder> foldersToCreate = new ArrayList<LocalFolder>(1);
            {
            foldersToCreate.add(this);
                @Override
            LocalStore.this.createFolders(foldersToCreate, visibleLimit);
                public Void doDbWork(final SQLiteDatabase db) throws WrappedException
                {
                    db.execSQL("INSERT INTO folders (name, visible_limit) VALUES (?, ?)", new Object[]
                               {
                                   mName,
                                   visibleLimit
                               });



                    return null;
            return true;
        }
        }
            });


            // When created, special folders should always be displayed
        private class PreferencesHolder
            // inbox should be integrated
            // and the inbox and drafts folders should be syncced by default
            if (mAccount.isSpecialFolder(mName))
        {
        {
                LocalFolder f = new LocalFolder(mName);
            FolderClass displayClass = mDisplayClass;
                f.open(OpenMode.READ_WRITE);
            FolderClass syncClass = mSyncClass;
                f.setInTopGroup(true);
            FolderClass pushClass = mPushClass;
                f.setDisplayClass(FolderClass.FIRST_CLASS);
            boolean inTopGroup = mInTopGroup;
                if (mName.equalsIgnoreCase(K9.INBOX))
            boolean integrate = mIntegrate;
                {
                    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;
        }
        }


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

        public String getPushState()
        public String getPushState()
        {
        {
            return mPushState;
            return mPushState;
@@ -1788,6 +1833,134 @@ public class LocalStore extends Store implements Serializable
            updateFolderColumn( "integrate", mIntegrate ? 1 : 0 );
            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
        @Override
        public void fetch(final Message[] messages, final FetchProfile fp, final MessageRetrievalListener listener)
        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 mToMe = false;
        private boolean mCcMe = false;
        private boolean mCcMe = false;



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


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


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


    protected void put(String key, String value)
    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();
        ContentValues cv = new ContentValues();
        cv.put("primkey", key);
        cv.put("primkey", key);
        cv.put("value", value);
        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);
        workingStorage.get().put(key, value);


        keyChange(key);
        keyChange(key);