All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.contentful.vault.Vault Maven / Gradle / Ivy

There is a newer version: 3.2.6
Show newest version
/*
 * Copyright (C) 2015 Contentful GmbH
 *
 * 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.contentful.vault;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.os.Looper;

import com.contentful.java.cda.CDAClient;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import rx.Observable;
import rx.subjects.PublishSubject;
import rx.subjects.SerializedSubject;
import rx.subjects.Subject;

public class Vault {
  public static final String ACTION_SYNC_COMPLETE = "com.contentful.vault.ACTION_SYNC_COMPLETE";

  public static final String EXTRA_SUCCESS = "EXTRA_SUCCESS";

  static final Locale LOCALE = Locale.US;

  static final Map, SqliteHelper> SQLITE_HELPERS = new LinkedHashMap<>();

  static final ExecutorService EXECUTOR_SYNC = Executors.newSingleThreadExecutor(
      new VaultThreadFactory());

  static final Executor EXECUTOR_CALLBACK = new Executor() {
    Handler handler = new Handler(Looper.getMainLooper());
    @Override public void execute(Runnable command) {
      handler.post(command);
    }
  };

  static final Map CALLBACKS = new HashMap<>();

  static final Subject SYNC_SUBJECT =
      new SerializedSubject<>(PublishSubject.create());

  private final Context context;

  private final Class space;

  private final SqliteHelper sqliteHelper;

  private Vault(Context context, Class space, SqliteHelper sqliteHelper) {
    this.context = context.getApplicationContext();
    this.space = space;
    this.sqliteHelper = sqliteHelper;
  }

  public static Vault with(Context context, Class space) {
    if (context == null) {
      throw new IllegalArgumentException("Cannot be invoked with null context.");
    }
    if (space == null) {
      throw new IllegalArgumentException("Cannot be invoked with null space.");
    }
    return new Vault(context, space, getOrCreateSqliteHelper(context, space));
  }

  public void requestSync(CDAClient client) {
    requestSync(client, null);
  }

  public void requestSync(CDAClient client, SyncCallback callback) {
    requestSync(SyncConfig.builder().setClient(client).build(), callback);
  }

  public void requestSync(SyncConfig config) {
    requestSync(config, null);
  }

  public void requestSync(SyncConfig config, SyncCallback callback) {
    requestSync(config, callback, null);
  }

  public void requestSync(SyncConfig config, SyncCallback callback, Executor callbackExecutor) {
    if (config == null) {
      throw new IllegalArgumentException("Cannot be invoked with null configuration.");
    }
    if (config.client() == null) {
      throw new IllegalArgumentException("Cannot be invoked with null client.");
    }

    String tag = Long.toString(System.currentTimeMillis());

    if (callback != null) {
      callback.setTag(tag);

      if (callbackExecutor == null) {
        callbackExecutor = EXECUTOR_CALLBACK;
      }

      CallbackBundle bundle = new CallbackBundle(callback, callbackExecutor);
      synchronized (CALLBACKS) {
        CALLBACKS.put(tag, bundle);
      }
    }

    EXECUTOR_SYNC.submit(SyncRunnable.builder()
        .setTag(tag)
        .setContext(context)
        .setSqliteHelper(sqliteHelper)
        .setSyncConfig(config)
        .build());
  }

  public  FetchQuery fetch(Class type) {
    return new FetchQuery<>(type, this);
  }

  public  ObserveQuery observe(Class type) {
    return new ObserveQuery<>(type, this);
  }

  public SQLiteDatabase getReadableDatabase() {
    return sqliteHelper.getReadableDatabase();
  }

  public static void cancel(SyncCallback callback) {
    if (callback == null) {
      throw new IllegalArgumentException("callback argument must not be null.");
    }

    String tag = callback.getTag();
    if (tag != null) {
      clearBundle(tag);
    }
  }

  public void releaseAll() {
    synchronized (SQLITE_HELPERS) {
      for (SqliteHelper spaceHelper : SQLITE_HELPERS.values()) {
        spaceHelper.close();
      }
      SQLITE_HELPERS.clear();
    }
  }

  public static Observable observeSyncResults() {
    return SYNC_SUBJECT.asObservable();
  }

  private static SqliteHelper createSqliteHelper(Context context, SpaceHelper spaceHelper) {
    return new SqliteHelper(context, spaceHelper);
  }

  private static SpaceHelper createSpaceHelper(Class space) {
    try {
      Class clazz = Class.forName(space.getName() + Constants.SUFFIX_SPACE);
      return (SpaceHelper) clazz.newInstance();
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("Cannot find generated class for space: " + space.getName(), e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    }
  }

  SqliteHelper getSqliteHelper() {
    return sqliteHelper;
  }

  private static SqliteHelper getOrCreateSqliteHelper(Context context, Class space) {
    synchronized (SQLITE_HELPERS) {
      SqliteHelper sqliteHelper = SQLITE_HELPERS.get(space);
      if (sqliteHelper == null) {
        SpaceHelper spaceHelper = createSpaceHelper(space);
        sqliteHelper = createSqliteHelper(context, spaceHelper);
        SQLITE_HELPERS.put(space, sqliteHelper);
      }
      return sqliteHelper;
    }
  }

  static void executeCallback(String tag, final SyncResult result) {
    final CallbackBundle bundle = clearBundle(tag);
    if (bundle != null) {
      bundle.executor.execute(new Runnable() {
        @Override public void run() {
          bundle.callback.onResult(result);
        }
      });
    }
  }

  static CallbackBundle clearBundle(String tag) {
    synchronized (CALLBACKS) {
      return CALLBACKS.remove(tag);
    }
  }

  static class CallbackBundle {
    final SyncCallback callback;
    final Executor executor;

    public CallbackBundle(SyncCallback callback, Executor executor) {
      this.callback = callback;
      this.executor = executor;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy