com.appsimobile.appsii.module.home.config.HomeItemConfigurationHelper.java Source code

Java tutorial

Introduction

Here is the source code for com.appsimobile.appsii.module.home.config.HomeItemConfigurationHelper.java

Source

/*
 * Copyright 2015. Appsi Mobile
 *
 * 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.appsimobile.appsii.module.home.config;

import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Looper;
import android.support.v4.util.LongSparseArray;
import android.text.TextUtils;

import com.appsimobile.appsii.annotation.VisibleForTesting;
import com.appsimobile.appsii.module.home.provider.HomeContract;

import net.jcip.annotations.GuardedBy;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * A helper facility to ease the process of loading and saving configurations for multiple
 * small views.
 * Created by nick on 21/01/15.
 */
@Singleton
public class HomeItemConfigurationHelper extends AbstractHomeItemConfiguration {

    @GuardedBy("this")
    private final LongSparseArray<ConfigurationProperty> mConfigurationProperties;
    // Confined to main thread
    QueryHandlerImpl mQueryHandler;
    HomeItemConfigurationLoader mHomeItemConfigurationLoader;

    @Inject
    public HomeItemConfigurationHelper(Context context, HomeItemConfigurationLoader loader) {
        super(context);
        mHomeItemConfigurationLoader = loader;
        mConfigurationProperties = mHomeItemConfigurationLoader.loadConfigurations(context);
    }

    void ensureQueryHandler() {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new IllegalStateException("Method can only be called on the main thread");
        }
        mQueryHandler = new QueryHandlerImpl(mContext.getContentResolver());
    }

    @Override
    public void updateProperty(long cellId, String key, String value) {
        ensureQueryHandler();
        mQueryHandler.updateProperty(cellId, key, value);
    }

    @Override
    public synchronized String getProperty(long cellId, String key, String nullValue) {
        ConfigurationProperty property = mConfigurationProperties.get(cellId);
        if (property == null)
            return nullValue;

        return property.getProperty(key, nullValue);
    }

    @Override
    public void removeProperty(long cellId, String key) {
        ensureQueryHandler();
        mQueryHandler.deleteProperty(cellId, key);
    }

    @Override
    public void removeAllProperties(long cellId) {
        ensureQueryHandler();
        synchronized (this) {
            ConfigurationProperty props = mConfigurationProperties.get(cellId);
            if (props != null) {
                int N = props.size();
                for (int i = 0; i < N; i++) {
                    String key = props.keyAt(i);
                    mQueryHandler.deleteProperty(cellId, key);
                }
            }
        }
    }

    @Override
    public String[] getWeatherWidgetWoeids(String key) {
        Set<String> result = new HashSet<>();
        synchronized (this) {
            int len = mConfigurationProperties.size();
            for (int i = 0; i < len; i++) {
                ConfigurationProperty property = mConfigurationProperties.valueAt(i);
                String value = property.getProperty(key, null);
                if (value != null) {
                    result.add(value);
                }
            }
        }
        return result.toArray(new String[result.size()]);
    }

    @Override
    public long findCellWithPropertyValue(String propertyName, String value) {
        synchronized (this) {
            int len = mConfigurationProperties.size();
            for (int i = 0; i < len; i++) {
                ConfigurationProperty property = mConfigurationProperties.valueAt(i);
                String propValue = property.getProperty(propertyName, null);
                if (propValue != null && TextUtils.equals(value, propValue)) {
                    return property.mCellId;
                }
            }
        }

        return -1;
    }

    void onPropertyUpdated(long cellId, String key, String value) {
        synchronized (this) {
            ConfigurationProperty property = mConfigurationProperties.get(cellId);
            if (property == null) {
                property = createProperty(mConfigurationProperties, cellId);
            }
            property.put(key, value);
        }
        notifyPropertyChanged(cellId, key, value);
    }

    void onPropertyDeleted(long cellId, String key) {
        synchronized (this) {
            ConfigurationProperty property = mConfigurationProperties.get(cellId);
            if (property != null) {
                property.remove(key);
            }
        }
        notifyPropertyDeleted(cellId, key);
    }

    @Singleton
    public static class HomeItemConfigurationLoader {

        ContentResolver mContentResolver;

        @Inject
        HomeItemConfigurationLoader(ContentResolver contentResolver) {
            mContentResolver = contentResolver;
        }

        @VisibleForTesting
        public LongSparseArray<ConfigurationProperty> loadConfigurations(Context context) {
            String[] projection = new String[] { HomeContract.Configuration._CELL_ID,
                    HomeContract.Configuration.KEY, HomeContract.Configuration.VALUE, };

            Cursor cursor = mContentResolver.query(HomeContract.Configuration.CONTENT_URI, projection, null, null,
                    null);

            if (cursor == null)
                return null;

            LongSparseArray<ConfigurationProperty> result = new LongSparseArray<>();
            try {
                while (cursor.moveToNext()) {

                    long cellId = cursor.getLong(0);

                    ConfigurationProperty info = result.get(cellId);
                    if (info == null) {
                        info = createProperty(result, cellId);
                    }
                    String key = cursor.getString(1);
                    String value = cursor.getString(2);
                    info.put(key, value);
                }
            } finally {
                cursor.close();
            }
            return result;
        }
    }

    class QueryHandlerImpl extends AsyncQueryHandler {

        final String mSelection = HomeContract.Configuration._CELL_ID + "=? AND " + HomeContract.Configuration.KEY
                + "=?";

        /**
         * This is a cookie pool, this prevents that we create lots of objects that need
         * to be garbage collected. Once an object is acquired from the pool, it is used
         * as a cookie in a query.
         * When done with this cookie it must be released, which will add it to the pool
         * again.
         * SimpleCookie instances must not be leaked outside this class.
         */
        final LinkedList<SimpleCookie> mCookiePool = new LinkedList<>();

        public QueryHandlerImpl(ContentResolver cr) {
            super(cr);
        }

        void deleteProperty(long cellId, String key) {
            SimpleCookie cookie = acquireCookie(cellId, key);
            startDelete(0, cookie, HomeContract.Configuration.CONTENT_URI, mSelection,
                    new String[] { String.valueOf(cellId), key });
        }

        SimpleCookie acquireCookie(long cellId, String key) {
            if (mCookiePool.isEmpty())
                return new SimpleCookie(cellId, key);
            return mCookiePool.remove(0).set(cellId, key);
        }

        void updateProperty(long cellId, String key, String value) {
            SimpleCookie cookie = acquireCookie(cellId, key);
            cookie.mValue = value;

            ContentValues values = new ContentValues(3);
            values.put(HomeContract.Configuration._CELL_ID, cellId);
            values.put(HomeContract.Configuration.KEY, key);
            values.put(HomeContract.Configuration.VALUE, value);
            startInsert(0, cookie, HomeContract.Configuration.CONTENT_URI, values);
        }

        @Override
        protected void onInsertComplete(int token, Object cookie, Uri uri) {
            SimpleCookie simpleCookie = (SimpleCookie) cookie;
            onPropertyUpdated(simpleCookie.mId, simpleCookie.mKey, simpleCookie.mValue);
            simpleCookie.release();
        }

        @Override
        protected void onDeleteComplete(int token, Object cookie, int result) {
            SimpleCookie simpleCookie = (SimpleCookie) cookie;
            onPropertyDeleted(simpleCookie.mId, simpleCookie.mKey);
            simpleCookie.release();
        }

        class SimpleCookie {

            long mId;

            String mKey;

            String mValue;

            public SimpleCookie(long cellId, String key) {
                mId = cellId;
                mKey = key;
            }

            void release() {
                mValue = null;
                mCookiePool.add(this);
            }

            public SimpleCookie set(long cellId, String key) {
                mId = cellId;
                mKey = key;
                return this;
            }
        }
    }

}