package com.reactivecascade.reactive.ui; import android.content.Context; import com.reactivecascade.Async; import com.reactivecascade.i.IThreadType; import com.reactivecascade.rest.MirrorService; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Created by Paul Houghton on 20-Oct-14. */ public class MirrorArrayAdapter<V> extends AltArrayAdapter<V> { public final MirrorDelegate mirrorDelegate = new MirrorDelegate("MirrorDelegate", Async.UI, Async.UI); public MirrorArrayAdapter(Context context, int resource) { super(context, resource); } public MirrorArrayAdapter(Context context, int resource, int textViewResourceId) { super(context, resource, textViewResourceId); } public MirrorArrayAdapter(Context context, int resource, V[] objects) { super(context, resource, objects); } public MirrorArrayAdapter(Context context, int resource, int textViewResourceId, V[] objects) { super(context, resource, textViewResourceId, objects); } public MirrorArrayAdapter(Context context, int resource, List objects) { super(context, resource, objects); } public MirrorArrayAdapter(Context context, int resource, int textViewResourceId, List objects) { super(context, resource, textViewResourceId, objects); } /** * This can be attached to an upstream {@link com.reactivecascade.rest.MirrorService} to copy * changes in the data model onto the screen. The idea it to manipulate the model (add remove sort) * and let the UI copy that. The filter mechanism in {@link android.widget.ArrayAdapter} should * be sufficient for reactive searching within the displayed items. * * It is recommended to do manipulation indirectly through the up-mirror model and no direct * changes to this view model. If you do need to change the model directly, subscribe changes to the * <code>MirrorDelegate</code> would be preferred as they keep open the possibility of * subscribing other {@link com.reactivecascade.reactive.ui.MirrorArrayAdapter}s to this one and they * receive the changes also (such as {@link android.widget.Filter}. Changes made for example * through {@link com.reactivecascade.reactive.ui.MirrorArrayAdapter#add(Object)} are not thread safe and * must be done from the UI thread. Changes made for example * through {@link com.reactivecascade.reactive.ui.MirrorArrayAdapter#addAsync(Object)} are safe from any * thread, but will not propagate to a downstream {@link com.reactivecascade.rest.MirrorService}. * * It is possible to subscribe for example several different mirrors for several different tabs on screen * of other UI components. At the moment the use for this is to maintain 4 differently-sorted and * differently-filtered views of the same list of cards. */ public class MirrorDelegate extends MirrorService<Integer, V> { public MirrorDelegate(String name, IThreadType readIThreadType, IThreadType writeIThreadType) { super(name, readIThreadType, writeIThreadType); } @Override public List<Integer> index() throws IOException { Async.assertUIThread(); final int count = MirrorArrayAdapter.this.getCount(); final ArrayList<Integer> index = new ArrayList<>(count); for (int i = 0; i < count; i++) { index.add(i); } return index; } @Override public V get(Integer key) throws IOException { Async.assertUIThread(); // There is no downstream publish for a "get()", it bounces off this mirror layer return MirrorArrayAdapter.this.getItem(key); } @Override public void put(Integer key, V value) throws Exception { Async.assertUIThread(); MirrorArrayAdapter.this.insert(value, key); // Publish the changes to any downstream mirror. Probably there aren't any super.put(key, value); } @Override public boolean delete(Integer key) throws Exception { Async.assertUIThread(); boolean deleted = MirrorArrayAdapter.this.getCount() > key; if (deleted) { MirrorArrayAdapter.this.remove(get(key)); // Publish the changes to any downstream mirror. Probably there aren't any super.delete(key); } return deleted; } /** * This onFireAction really is atomic because both the MirrorArrayAdapter and MirrorDelegate are limited * to the UI thread. * * @param key * @param value * @param expectedValue * @return * @throws Exception */ @Override public boolean replace(Integer key, V value, V expectedValue) throws Exception { Async.assertUIThread(); boolean actionPerformed = expectedValue.equals(MirrorArrayAdapter.this.getItem(key)); if (actionPerformed) { MirrorArrayAdapter.this.remove(expectedValue); MirrorArrayAdapter.this.insert(value, key); // Publish the changes to any downstream mirror. Probably there aren't any super.replace(key, value, expectedValue); } return actionPerformed; } } }