Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Felix Arends committed Nov 19, 2010
2 parents f28608e + 2b183c2 commit 0a2769c
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,26 +58,32 @@ public class TriggerRepository {
* 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 =
Multimaps.synchronizedListMultimap(ArrayListMultimap.<String, Trigger> create());
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);
// Iterate and add one at a time instead of using putAll() so that observers are notified.
Multimap<String, Trigger> deserializeTriggersFromString =
deserializeTriggersFromString(triggers);
for (Entry<String, Trigger> entry : deserializeTriggersFromString.entries()) {
put(entry.getValue());
}
mTriggers = deserializeTriggersFromString(triggers);
}

/** Returns a list of all triggers. The list is unmodifiable. */
Expand All @@ -92,17 +98,15 @@ public synchronized Multimap<String, Trigger> getAllTriggers() {
* the {@link Trigger} to add
*/
public synchronized void put(Trigger trigger) {
notifyOnAdd(trigger);
mTriggers.put(trigger.getEventName(), trigger);
storeTriggers();
notifyOnAdd(trigger);
ensureTriggerServiceRunning();
}

/** Removes a specific {@link Trigger}. */
public synchronized void remove(final Trigger trigger) {
synchronized (mTriggers) {
mTriggers.get(trigger.getEventName()).remove(trigger);
}
mTriggers.get(trigger.getEventName()).remove(trigger);
storeTriggers();
notifyOnRemove(trigger);
}
Expand All @@ -128,7 +132,7 @@ private void notifyOnRemove(Trigger trigger) {
}

/** Writes the list of triggers to the shared preferences. */
private void storeTriggers() {
private synchronized void storeTriggers() {
SharedPreferences.Editor editor = mPreferences.edit();
final String triggerValue = serializeTriggersToString(mTriggers);
if (triggerValue != null) {
Expand Down Expand Up @@ -168,7 +172,7 @@ private String serializeTriggersToString(Multimap<String, Trigger> triggers) {
}

/** Returns {@code true} iff the list of triggers is empty. */
public boolean isEmpty() {
public synchronized boolean isEmpty() {
return mTriggers.isEmpty();
}

Expand All @@ -177,6 +181,20 @@ 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}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.googlecode.android_scripting.trigger;

import com.google.common.collect.Maps;
import com.googlecode.android_scripting.facade.FacadeConfiguration;
import com.googlecode.android_scripting.facade.FacadeManager;
import com.googlecode.android_scripting.rpc.MethodDescriptor;
import com.googlecode.android_scripting.trigger.TriggerRepository.TriggerRepositoryObserver;

import java.util.Map;

import org.json.JSONArray;

/**
* A {@link TriggerRepositoryObserver} that starts and stops the monitoring of events depending on
* whether or not triggers for the event exist.
*
* @author Felix Arends ([email protected])
*/
public class EventGenerationControllingObserver implements TriggerRepositoryObserver {
private final FacadeManager mFacadeManager;
private final Map<String, MethodDescriptor> mStartEventGeneratingMethodDescriptors;
private final Map<String, MethodDescriptor> mStopEventGeneratingMethodDescriptors;
private final Map<String, Integer> mEventTriggerRefCounts = Maps.newHashMap();

/**
* Creates a new StartEventMonitoringObserver for the given trigger repository.
*
* @param facadeManager
* @param triggerRepository
*/
public EventGenerationControllingObserver(FacadeManager facadeManager) {
mFacadeManager = facadeManager;
mStartEventGeneratingMethodDescriptors =
FacadeConfiguration.collectStartEventMethodDescriptors();
mStopEventGeneratingMethodDescriptors = FacadeConfiguration.collectStopEventMethodDescriptors();
}

private synchronized int incrementAndGetRefCount(String eventName) {
int refCount =
(mEventTriggerRefCounts.containsKey(eventName)) ? mEventTriggerRefCounts.get(eventName) : 0;
refCount++;
mEventTriggerRefCounts.put(eventName, refCount);
return refCount;
}

private synchronized int decrementAndGetRefCount(String eventName) {
int refCount =
(mEventTriggerRefCounts.containsKey(eventName)) ? mEventTriggerRefCounts.get(eventName) : 0;
refCount--;
mEventTriggerRefCounts.put(eventName, refCount);
return refCount;
}

@Override
public synchronized void onPut(Trigger trigger) {
// If we're not already monitoring the events corresponding to this trigger, do so.
if (incrementAndGetRefCount(trigger.getEventName()) == 1) {
startMonitoring(trigger.getEventName());
}
}

@Override
public synchronized void onRemove(Trigger trigger) {
// If there are no more triggers listening to this event, then we need to stop monitoring.
if (decrementAndGetRefCount(trigger.getEventName()) == 1) {
stopMonitoring(trigger.getEventName());
}
}

private void startMonitoring(String eventName) {
MethodDescriptor startEventGeneratingMethod =
mStartEventGeneratingMethodDescriptors.get(eventName);
try {
startEventGeneratingMethod.invoke(mFacadeManager, new JSONArray());
} catch (Throwable t) {
throw new RuntimeException(t);
}
}

private void stopMonitoring(String eventName) {
MethodDescriptor stopEventGeneratingMethod =
mStopEventGeneratingMethodDescriptors.get(eventName);
try {
stopEventGeneratingMethod.invoke(mFacadeManager, new JSONArray());
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.os.IBinder;
import android.widget.RemoteViews;

import com.google.common.base.Preconditions;
import com.googlecode.android_scripting.BaseApplication;
import com.googlecode.android_scripting.ForegroundService;
import com.googlecode.android_scripting.IntentBuilders;
Expand All @@ -35,6 +36,7 @@
import com.googlecode.android_scripting.facade.FacadeConfiguration;
import com.googlecode.android_scripting.facade.FacadeManager;
import com.googlecode.android_scripting.facade.EventFacade.EventObserver;
import com.googlecode.android_scripting.trigger.EventGenerationControllingObserver;
import com.googlecode.android_scripting.trigger.Trigger;
import com.googlecode.android_scripting.trigger.TriggerRepository;
import com.googlecode.android_scripting.trigger.TriggerRepository.TriggerRepositoryObserver;
Expand Down Expand Up @@ -82,12 +84,14 @@ public IBinder onBind(Intent intent) {
public void onCreate() {
super.onCreate();

mTriggerRepository = ((BaseApplication) getApplication()).getTriggerRepository();
mTriggerRepository.addObserver(new RepositoryObserver());
mFacadeManager =
new FacadeManager(FacadeConfiguration.getSdkLevel(), this, null, FacadeConfiguration
.getFacadeClasses());
mEventFacade = mFacadeManager.getReceiver(EventFacade.class);

mTriggerRepository = ((BaseApplication) getApplication()).getTriggerRepository();
mTriggerRepository.bootstrapObserver(new RepositoryObserver());
mTriggerRepository.bootstrapObserver(new EventGenerationControllingObserver(mFacadeManager));
installAlarm();
}

Expand Down Expand Up @@ -128,15 +132,19 @@ public void onEventReceived(Event event) {
}

private class RepositoryObserver implements TriggerRepositoryObserver {
int mTriggerCount = 0;

@Override
public void onPut(Trigger trigger) {
mTriggerCount++;
mEventFacade.addNamedEventObserver(trigger.getEventName(), new TriggerEventObserver(trigger));
}

@Override
public void onRemove(Trigger trigger) {
Preconditions.checkArgument(mTriggerCount > 0);
// TODO(damonkohler): Tear down EventObserver associated with trigger.
if (mTriggerRepository.isEmpty()) {
if (--mTriggerCount == 0) {
// TODO(damonkohler): Use stopSelfResult() which would require tracking startId.
stopSelf();
}
Expand Down

0 comments on commit 0a2769c

Please sign in to comment.