| /* |
| * Copyright (C) 2016 Google Inc. |
| * |
| * 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.googlecode.android_scripting.trigger; |
| |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.SharedPreferences; |
| import android.preference.PreferenceManager; |
| |
| import com.google.common.collect.ArrayListMultimap; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.Multimaps; |
| import com.googlecode.android_scripting.IntentBuilders; |
| import com.googlecode.android_scripting.Log; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.util.Map.Entry; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| |
| import org.apache.commons.codec.binary.Base64Codec; |
| |
| /** |
| * A repository maintaining all currently scheduled triggers. This includes, for example, alarms or |
| * observers of arriving text messages etc. This class is responsible for serializing the list of |
| * triggers to the shared preferences store, and retrieving it from there. |
| * |
| * @author Felix Arends (felix.arends@gmail.com) |
| * @author Damon Kohler (damonkohler@gmail.com) |
| */ |
| public class TriggerRepository { |
| /** |
| * The list of triggers is serialized to the shared preferences entry with this name. |
| */ |
| private static final String TRIGGERS_PREF_KEY = "TRIGGERS"; |
| |
| private final SharedPreferences mPreferences; |
| private final Context mContext; |
| |
| /** |
| * An interface for objects that are notified when a trigger is added to the repository. |
| */ |
| public interface TriggerRepositoryObserver { |
| /** |
| * Invoked just before the trigger is added to the repository. |
| * |
| * @param trigger |
| * The trigger about to be added to the repository. |
| */ |
| void onPut(Trigger trigger); |
| |
| /** |
| * Invoked just after the trigger has been removed from the repository. |
| * |
| * @param trigger |
| * The trigger that has just been removed from the repository. |
| */ |
| void onRemove(Trigger trigger); |
| } |
| |
| private final Multimap<String, Trigger> mTriggers; |
| private final CopyOnWriteArrayList<TriggerRepositoryObserver> mTriggerObservers = |
| new CopyOnWriteArrayList<TriggerRepositoryObserver>(); |
| |
| public TriggerRepository(Context context) { |
| mContext = context; |
| mPreferences = PreferenceManager.getDefaultSharedPreferences(context); |
| String triggers = mPreferences.getString(TRIGGERS_PREF_KEY, null); |
| mTriggers = deserializeTriggersFromString(triggers); |
| } |
| |
| /** Returns a list of all triggers. The list is unmodifiable. */ |
| public synchronized Multimap<String, Trigger> getAllTriggers() { |
| return Multimaps.unmodifiableMultimap(mTriggers); |
| } |
| |
| /** |
| * Adds a new trigger to the repository. |
| * |
| * @param trigger |
| * the {@link Trigger} to add |
| */ |
| public synchronized void put(Trigger trigger) { |
| notifyOnAdd(trigger); |
| mTriggers.put(trigger.getEventName(), trigger); |
| storeTriggers(); |
| ensureTriggerServiceRunning(); |
| } |
| |
| /** Removes a specific {@link Trigger}. */ |
| public synchronized void remove(final Trigger trigger) { |
| mTriggers.get(trigger.getEventName()).remove(trigger); |
| storeTriggers(); |
| notifyOnRemove(trigger); |
| } |
| |
| /** Ensures that the {@link TriggerService} is running */ |
| private void ensureTriggerServiceRunning() { |
| Intent startTriggerServiceIntent = IntentBuilders.buildTriggerServiceIntent(); |
| mContext.startService(startTriggerServiceIntent); |
| } |
| |
| /** Notify all {@link TriggerRepositoryObserver}s that a {@link Trigger} was added. */ |
| private void notifyOnAdd(Trigger trigger) { |
| for (TriggerRepositoryObserver observer : mTriggerObservers) { |
| observer.onPut(trigger); |
| } |
| } |
| |
| /** Notify all {@link TriggerRepositoryObserver}s that a {@link Trigger} was removed. */ |
| private void notifyOnRemove(Trigger trigger) { |
| for (TriggerRepositoryObserver observer : mTriggerObservers) { |
| observer.onRemove(trigger); |
| } |
| } |
| |
| /** Writes the list of triggers to the shared preferences. */ |
| private synchronized void storeTriggers() { |
| SharedPreferences.Editor editor = mPreferences.edit(); |
| final String triggerValue = serializeTriggersToString(mTriggers); |
| if (triggerValue != null) { |
| editor.putString(TRIGGERS_PREF_KEY, triggerValue); |
| } |
| editor.commit(); |
| } |
| |
| /** Deserializes the {@link Multimap} of {@link Trigger}s from a base 64 encoded string. */ |
| @SuppressWarnings("unchecked") |
| private Multimap<String, Trigger> deserializeTriggersFromString(String triggers) { |
| if (triggers == null) { |
| return ArrayListMultimap.<String, Trigger> create(); |
| } |
| try { |
| final ByteArrayInputStream inputStream = |
| new ByteArrayInputStream(Base64Codec.decodeBase64(triggers.getBytes())); |
| final ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); |
| return (Multimap<String, Trigger>) objectInputStream.readObject(); |
| } catch (Exception e) { |
| Log.e(e); |
| } |
| return ArrayListMultimap.<String, Trigger> create(); |
| } |
| |
| /** Serializes the list of triggers to a Base64 encoded string. */ |
| private String serializeTriggersToString(Multimap<String, Trigger> triggers) { |
| try { |
| final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
| final ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); |
| objectOutputStream.writeObject(triggers); |
| return new String(Base64Codec.encodeBase64(outputStream.toByteArray())); |
| } catch (IOException e) { |
| Log.e(e); |
| return null; |
| } |
| } |
| |
| /** Returns {@code true} iff the list of triggers is empty. */ |
| public synchronized boolean isEmpty() { |
| return mTriggers.isEmpty(); |
| } |
| |
| /** Adds a {@link TriggerRepositoryObserver}. */ |
| public void addObserver(TriggerRepositoryObserver observer) { |
| mTriggerObservers.add(observer); |
| } |
| |
| /** |
| * Adds the given {@link TriggerRepositoryObserver} and invokes |
| * {@link TriggerRepositoryObserver#onPut} for all existing triggers. |
| * |
| * @param observer |
| * The observer to add. |
| */ |
| public synchronized void bootstrapObserver(TriggerRepositoryObserver observer) { |
| addObserver(observer); |
| for (Entry<String, Trigger> trigger : mTriggers.entries()) { |
| observer.onPut(trigger.getValue()); |
| } |
| } |
| |
| /** |
| * Removes a {@link TriggerRepositoryObserver}. |
| */ |
| public void removeObserver(TriggerRepositoryObserver observer) { |
| mTriggerObservers.remove(observer); |
| } |
| } |