com.helpshift.kvstore.SharedPreferencesImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.helpshift.kvstore.SharedPreferencesImpl.java

Source

/*
 * Copyright (c) 2016. Subham Tyagi
 * 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.helpshift.kvstore;

import android.content.ContentValues;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.AsyncTask;
import android.util.Log;

import com.helpshift.kvstore.database.PreferencesContent;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;

public class SharedPreferencesImpl implements SharedPreferences {

    private static final String SEPARATOR = ":-:";

    private final Map<String, String> mMap = new ConcurrentHashMap<>();

    private final Map<String, String> mModifiedMap = new ConcurrentHashMap<>();

    private final Set<String> mModifiedKeys = Collections.synchronizedSet(new HashSet<String>());

    private SQLiteDatabase mSqLiteDatabase;

    private static final Object mContent = new Object();

    private String mPreferenceName;

    private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners = new WeakHashMap<>();

    public SharedPreferencesImpl(String preferenceName, SQLiteDatabase sqlDatabase) {
        mSqLiteDatabase = sqlDatabase;
        mPreferenceName = preferenceName;
        PreferencesContent.createTableQuery(preferenceName, mSqLiteDatabase);
    }

    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
        synchronized (this) {
            mListeners.put(listener, mContent);
        }
    }

    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
        synchronized (this) {
            mListeners.remove(listener);
        }
    }

    @Override
    public Map<String, ?> getAll() {
        Cursor cursor = null;
        try {
            String selectAllQuery = "SELECT * FROM " + mPreferenceName;
            cursor = mSqLiteDatabase.rawQuery(selectAllQuery, null);
            if (cursor != null) {
                while (cursor.moveToNext()) {
                    parseSettingCursor(cursor);
                }
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return mMap;
    }

    public String getString(String key, String defValue) {
        String value = getValueByKey(key);
        if (value == null) {
            return defValue;
        }
        return value;
    }

    @Override
    public Set<String> getStringSet(String key, Set<String> defValues) {
        String value = getValueByKey(key);
        Set<String> stringSet = new HashSet<>();
        if (value == null) {
            return defValues;
        }
        stringSet.addAll(Arrays.asList(value.split(SEPARATOR)));
        return stringSet;
    }

    public boolean getBoolean(String key, boolean defValue) {
        String value = getValueByKey(key);
        if (value == null) {
            return defValue;
        } else {
            try {
                return Boolean.parseBoolean(getValueByKey(key));
            } catch (NumberFormatException e) {
                throw new ClassCastException(value + "can not cast to Boolean");
            }
        }
    }

    @Override
    public boolean contains(String key) {
        return false;
    }

    @Override
    public Editor edit() {
        return new EditorImpl();
    }

    public int getInt(String key, int defValue) {
        String value = getValueByKey(key);
        if (value == null) {
            return defValue;
        } else {
            try {
                return Integer.parseInt(getValueByKey(key));
            } catch (NumberFormatException e) {
                throw new ClassCastException("can not cast to integer");
            }
        }
    }

    @Override
    public long getLong(String key, long defValue) {
        String value = getValueByKey(key);
        if (value == null) {
            return defValue;
        } else {
            try {
                return Long.parseLong(getValueByKey(key));
            } catch (NumberFormatException e) {
                throw new ClassCastException("can not cast to Long");
            }
        }
    }

    @Override
    public float getFloat(String key, float defValue) {
        String value = getValueByKey(key);
        if (value == null) {
            return defValue;
        } else {
            try {
                return Float.parseFloat(getValueByKey(key));
            } catch (NumberFormatException e) {
                throw new ClassCastException("can not cast to Float");
            }
        }
    }

    private String getValueByKey(String key) {
        if (mMap.containsKey(key)) {
            return mMap.get(key);
        } else {
            return getValueFromDB(key);
        }
    }

    private boolean checkIfKeyAlreadyExists(String key) {

        Cursor cursor = null;
        try {
            cursor = mSqLiteDatabase.query(mPreferenceName, PreferencesContent.PREFERENCES_PROJECTION,
                    PreferencesContent.SELECTION_VAI_KEY, new String[] { key }, null, null, null);
            if (cursor != null && cursor.moveToNext()) {
                return true;
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return false;
    }

    public String getValueFromDB(String key) {
        updateMapWithValueByKey(key);
        return mMap.get(key);
    }

    private void updateMapWithValueByKey(String key) {
        Cursor cursor = null;
        try {
            cursor = mSqLiteDatabase.query(mPreferenceName, PreferencesContent.PREFERENCES_PROJECTION,
                    PreferencesContent.SELECTION_VAI_KEY, new String[] { key }, null, null, null);
            if (cursor != null && cursor.moveToNext()) {
                parseSettingCursor(cursor);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

    }

    private void parseSettingCursor(Cursor cursor) {
        mMap.put(cursor.getString(0), cursor.getString(1));
    }

    public class EditorImpl implements Editor {

        public Editor putBoolean(String key, boolean value) {
            saveSetting(key, String.valueOf(value));
            return this;
        }

        @Override
        public Editor remove(String key) {
            mModifiedMap.put(key, null);
            return this;
        }

        @Override
        public Editor clear() {
            mMap.clear();
            mModifiedMap.clear();
            return this;
        }

        public Editor putInt(String key, int value) {
            saveSetting(key, String.valueOf(value));
            return this;
        }

        @Override
        public Editor putLong(String key, long value) {
            saveSetting(key, String.valueOf(value));
            return this;
        }

        @Override
        public Editor putFloat(String key, float value) {
            saveSetting(key, String.valueOf(value));
            return this;
        }

        public Editor putString(String key, String value) {
            saveSetting(key, value);
            return this;
        }

        @Override
        public Editor putStringSet(String key, Set<String> values) {
            StringBuilder builder = new StringBuilder();
            for (String s : values) {
                builder.append(s);
                builder.append(SEPARATOR);
            }
            saveSetting(key, String.valueOf(builder.toString()));
            return this;
        }

        private void saveSetting(String key, String value) {
            mModifiedMap.put(key, value);
            mMap.put(key, value);
        }

        @Override
        public boolean commit() {
            throw new UnsupportedOperationException("Commit is not supported");
        }

        @Override
        public void apply() {
            CommitPreferenceAsyncTask asyncTask = new CommitPreferenceAsyncTask();
            asyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
        }
    }

    private class CommitPreferenceAsyncTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... params) {
            mSqLiteDatabase.beginTransaction();
            for (Map.Entry<String, String> entry : mModifiedMap.entrySet()) {
                /*Add key to modified key */
                mModifiedKeys.add(entry.getKey());
                /*if map contains key mean we have to update or delete*/
                String key = entry.getKey();
                if (checkIfKeyAlreadyExists(key)) {
                    /*If value of any key is missing we are treating it as removed */
                    if (entry.getValue() == null) {
                        Log.d(getClass().getSimpleName(), "Delete operations");
                        mSqLiteDatabase.delete(mPreferenceName, PreferencesContent.SELECTION_VAI_KEY,
                                new String[] { entry.getKey() });
                        mMap.remove(entry.getKey());
                    } else {
                        Log.d(getClass().getSimpleName(), "update operations");
                        ContentValues contentValues = new ContentValues();
                        contentValues.put(PreferencesContent.COLUMN_NAME_KEY, entry.getKey());
                        contentValues.put(PreferencesContent.COLUMN_NAME_VALUE, entry.getValue());
                        mSqLiteDatabase.update(mPreferenceName, contentValues, PreferencesContent.SELECTION_VAI_KEY,
                                new String[] { entry.getKey() });
                        mMap.put(entry.getKey(), entry.getValue());
                    }
                } else {
                    Log.d(getClass().getSimpleName(), "new operations");
                    ContentValues contentValues = new ContentValues();
                    contentValues.put(PreferencesContent.COLUMN_NAME_KEY, entry.getKey());
                    contentValues.put(PreferencesContent.COLUMN_NAME_VALUE, entry.getValue());
                    mSqLiteDatabase.insert(mPreferenceName, "foo", contentValues);
                    mMap.put(entry.getKey(), entry.getValue());
                    try {
                        JSONObject jsonObject = new JSONObject("");
                        JSONArray jsonArray = jsonObject.getJSONArray("Employee");
                        for (int i = 0; i < jsonArray.length(); i++) {
                            JSONObject empObject = jsonArray.getJSONObject(i);
                            empObject.getInt("id");
                            empObject.getString("name");
                            empObject.getInt("salary");
                        }

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }

                }
            }
            mSqLiteDatabase.endTransaction();
            notifyListeners();
            return null;
        }
    }

    private void notifyListeners() {
        if (mModifiedKeys.size() == 0) {
            return;
        }
        for (String key : mModifiedKeys) {
            for (OnSharedPreferenceChangeListener listener : mListeners.keySet()) {
                if (listener != null) {
                    listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
                }
            }
        }
        mModifiedKeys.clear();
    }

}