aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Android_Studio/Tango.SharedUI/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Android_Studio/Tango.SharedUI/src/main/java')
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/binding_adapters/BindingAdapters.java34
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityBase.java121
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityCallbackEngine.java55
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBase.java111
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBaseV4.java107
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/BindingConverters.java19
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/DependencyProperty.java49
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/FieldUtils.java68
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ReadOnlyField.java74
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/RelayCommand.java68
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewContract.java8
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewModelBase.java21
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/AndroidNotificationProvider.java22
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/NotificationProvider.java11
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerAdapter.java61
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerZoomTransform.java44
-rw-r--r--Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/validation/ViewValidator.java23
17 files changed, 896 insertions, 0 deletions
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/binding_adapters/BindingAdapters.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/binding_adapters/BindingAdapters.java
new file mode 100644
index 000000000..04d1ea5e7
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/binding_adapters/BindingAdapters.java
@@ -0,0 +1,34 @@
+package com.twine.tango.sharedui.binding_adapters;
+
+import android.databinding.BindingAdapter;
+import android.graphics.Bitmap;
+import android.widget.ImageView;
+import android.widget.ListView;
+
+/**
+ * Created by Roy on 11/7/2017.
+ */
+
+public class BindingAdapters {
+
+ @BindingAdapter("android:memory_bitmap")
+ public static void bitmapAdapter(ImageView view, Bitmap bitmap) {
+ if (bitmap != null) {
+ view.setImageBitmap(bitmap);
+ }
+ }
+
+ @BindingAdapter("android:onItemClick")
+ public static void listViewClickAdapter(ListView view, ListView.OnItemClickListener listener) {
+ if (view != null && listener != null) {
+ view.setOnItemClickListener(listener);
+ }
+ }
+
+ @BindingAdapter("android:selectedItemPosition")
+ public static void listViewSelectedItemPosition(ListView view, int position) {
+ if (view != null) {
+ view.setItemChecked(position, true);
+ }
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityBase.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityBase.java
new file mode 100644
index 000000000..7f52ffae6
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityBase.java
@@ -0,0 +1,121 @@
+package com.twine.tango.sharedui.containers;
+
+import android.content.Intent;
+import android.databinding.DataBindingUtil;
+import android.databinding.ViewDataBinding;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.mobsandgeeks.saripaar.ValidationError;
+import com.mobsandgeeks.saripaar.Validator;
+import com.twine.tango.sharedui.mvvm.ViewContract;
+import com.twine.tango.sharedui.mvvm.ViewModelBase;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+import javax.inject.Inject;
+
+import butterknife.ButterKnife;
+import io.reactivex.functions.Consumer;
+
+public abstract class ActivityBase<BindingView extends ViewDataBinding, VM extends ViewModelBase> extends AppCompatActivity implements ViewContract, Validator.ValidationListener {
+
+ protected static final String ACTIVITY_CALLBACK_INTENT = "ACTIVITY_CALLBACK_INTENT";
+
+ private Consumer<Boolean> lastValidationConsumer;
+ private Validator validator;
+
+ @Inject
+ public VM vm;
+
+ public BindingView binding;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ inject();
+ binding = DataBindingUtil.setContentView(this, getLayoutId());
+ try {
+
+ Method method = binding.getClass().getDeclaredMethod("setVm", vm.getClass());
+ method.invoke(binding, vm);
+
+ attachView();
+
+ ButterKnife.bind(this, binding.getRoot());
+
+ validator = new Validator(this);
+ validator.setValidationListener(this);
+
+ String key = getIntent().getStringExtra(ACTIVITY_CALLBACK_INTENT);
+ if (key != null && !key.isEmpty()) {
+ ActivityCallbackEngine.getInstance().runAndRemove(key);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void attachView() {
+ vm.attachView(this);
+ }
+
+ @Override
+ public void validateFields(Consumer<Boolean> consumer) {
+ lastValidationConsumer = consumer;
+ validator.validate();
+ }
+
+ @Override
+ public void onValidationSucceeded() {
+ try {
+ lastValidationConsumer.accept(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onValidationFailed(List<ValidationError> errors) {
+ for (ValidationError error : errors) {
+ View view = error.getView();
+ String message = error.getCollatedErrorMessage(this);
+
+ if (view instanceof EditText) {
+ view.requestFocus();
+ ((EditText) view).setError(message);
+ } else {
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ if (lastValidationConsumer != null) {
+ try {
+ lastValidationConsumer.accept(false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected abstract int getLayoutId();
+
+ protected abstract void inject();
+
+ public void startActivityNotify(Class<?> activity, Runnable callback) {
+
+ String key = ActivityCallbackEngine.getInstance().putCallback(callback);
+ Intent i = new Intent(getApplicationContext(), activity);
+ i.putExtra(ACTIVITY_CALLBACK_INTENT, key);
+ startActivity(i);
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityCallbackEngine.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityCallbackEngine.java
new file mode 100644
index 000000000..5aec7852a
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/ActivityCallbackEngine.java
@@ -0,0 +1,55 @@
+package com.twine.tango.sharedui.containers;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Created by Roy on 11/7/2017.
+ */
+
+public class ActivityCallbackEngine {
+
+ private static ActivityCallbackEngine instance;
+
+ private Map<String, Runnable> callbacks;
+
+ private ActivityCallbackEngine() {
+ callbacks = new HashMap<>();
+ }
+
+ public static ActivityCallbackEngine getInstance() {
+ if (instance == null) {
+ instance = new ActivityCallbackEngine();
+ }
+
+ return instance;
+ }
+
+ public void putCallback(String key, Runnable callback) {
+ callbacks.put(key,callback);
+ }
+
+ public String putCallback(Runnable callback) {
+
+ String key = UUID.randomUUID().toString();
+ callbacks.put(key,callback);
+ return key;
+ }
+
+ public void removeCallback(String key)
+ {
+ callbacks.remove(key);
+ }
+
+ public void run(String key)
+ {
+ callbacks.get(key).run();
+ }
+
+ public void runAndRemove(String key)
+ {
+ callbacks.get(key).run();
+ removeCallback(key);
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBase.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBase.java
new file mode 100644
index 000000000..19d472e24
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBase.java
@@ -0,0 +1,111 @@
+package com.twine.tango.sharedui.containers;
+
+import android.app.Fragment;
+import android.databinding.DataBindingUtil;
+import android.databinding.ViewDataBinding;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.mobsandgeeks.saripaar.ValidationError;
+import com.mobsandgeeks.saripaar.Validator;
+import com.twine.tango.sharedui.mvvm.ViewContract;
+import com.twine.tango.sharedui.mvvm.ViewModelBase;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.ButterKnife;
+import io.reactivex.functions.Consumer;
+
+public abstract class FragmentBase<BindingView extends ViewDataBinding, VM extends ViewModelBase> extends Fragment implements ViewContract, Validator.ValidationListener {
+
+ private Consumer<Boolean> lastValidationConsumer;
+ private Validator validator;
+
+ @Inject
+ VM vm;
+
+ BindingView binding;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ inject();
+ binding = DataBindingUtil.inflate(inflater,getLayoutId(),container,false);
+
+ try {
+
+ Method method = binding.getClass().getDeclaredMethod("setVm", vm.getClass());
+ method.invoke(binding, vm);
+
+ attachView();
+
+ ButterKnife.bind(this, binding.getRoot());
+
+ validator = new Validator(this);
+ validator.setValidationListener(this);
+
+ return binding.getRoot();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void attachView() {
+ vm.attachView(this);
+ }
+
+ @Override
+ public void validateFields(Consumer<Boolean> consumer) {
+ lastValidationConsumer = consumer;
+ validator.validate();
+ }
+
+ @Override
+ public void onValidationSucceeded() {
+ try {
+ lastValidationConsumer.accept(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onValidationFailed(List<ValidationError> errors) {
+ for (ValidationError error : errors) {
+ View view = error.getView();
+ String message = error.getCollatedErrorMessage(this.getActivity().getBaseContext());
+
+ if (view instanceof EditText) {
+ view.requestFocus();
+ ((EditText) view).setError(message);
+ } else {
+ Toast.makeText(this.getActivity().getBaseContext(), message, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ if (lastValidationConsumer != null) {
+ try {
+ lastValidationConsumer.accept(false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected abstract int getLayoutId();
+
+ protected abstract void inject();
+
+ public abstract String getTitle();
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBaseV4.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBaseV4.java
new file mode 100644
index 000000000..7f56dd49e
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/containers/FragmentBaseV4.java
@@ -0,0 +1,107 @@
+package com.twine.tango.sharedui.containers;
+
+import android.support.v4.app.Fragment;
+import android.databinding.DataBindingUtil;
+import android.databinding.ViewDataBinding;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.Toast;
+import com.mobsandgeeks.saripaar.ValidationError;
+import com.mobsandgeeks.saripaar.Validator;
+import com.twine.tango.sharedui.mvvm.ViewContract;
+import com.twine.tango.sharedui.mvvm.ViewModelBase;
+import java.lang.reflect.Method;
+import java.util.List;
+import javax.inject.Inject;
+import butterknife.ButterKnife;
+import io.reactivex.functions.Consumer;
+
+public abstract class FragmentBaseV4<BindingView extends ViewDataBinding, VM extends ViewModelBase> extends Fragment implements ViewContract, Validator.ValidationListener {
+
+ private Consumer<Boolean> lastValidationConsumer;
+ private Validator validator;
+
+ @Inject
+ VM vm;
+
+ BindingView binding;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ inject();
+ binding = DataBindingUtil.inflate(inflater,getLayoutId(),container,false);
+
+ try {
+
+ Method method = binding.getClass().getDeclaredMethod("setVm", vm.getClass());
+ method.invoke(binding, vm);
+
+ attachView();
+
+ ButterKnife.bind(this, binding.getRoot());
+
+ validator = new Validator(this);
+ validator.setValidationListener(this);
+
+ return binding.getRoot();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void attachView() {
+ vm.attachView(this);
+ }
+
+ @Override
+ public void validateFields(Consumer<Boolean> consumer) {
+ lastValidationConsumer = consumer;
+ validator.validate();
+ }
+
+ @Override
+ public void onValidationSucceeded() {
+ try {
+ lastValidationConsumer.accept(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onValidationFailed(List<ValidationError> errors) {
+ for (ValidationError error : errors) {
+ View view = error.getView();
+ String message = error.getCollatedErrorMessage(this.getContext());
+
+ if (view instanceof EditText) {
+ view.requestFocus();
+ ((EditText) view).setError(message);
+ } else {
+ Toast.makeText(this.getContext(), message, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ if (lastValidationConsumer != null) {
+ try {
+ lastValidationConsumer.accept(false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ protected abstract int getLayoutId();
+
+ protected abstract void inject();
+
+ public abstract String getTitle();
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/BindingConverters.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/BindingConverters.java
new file mode 100644
index 000000000..130890c07
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/BindingConverters.java
@@ -0,0 +1,19 @@
+package com.twine.tango.sharedui.mvvm;
+
+import android.app.Application;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+
+import java.util.Calendar;
+
+/**
+ * Created by Roy on 7/27/2017.
+ */
+
+public class BindingConverters {
+
+ public static String convertDateToYear(Calendar calendar) {
+ return String.valueOf(calendar.get(Calendar.YEAR));
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/DependencyProperty.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/DependencyProperty.java
new file mode 100644
index 000000000..b39be3dc3
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/DependencyProperty.java
@@ -0,0 +1,49 @@
+package com.twine.tango.sharedui.mvvm;
+
+import android.databinding.ObservableField;
+
+/**
+ * Created by Roy on 7/27/2017.
+ */
+
+public class DependencyProperty<T> extends ObservableField<T> {
+
+ public interface OnPropertyChangedCallback<T> {
+ void onPropertyChanged(DependencyProperty<T> dp, T value);
+ }
+
+ private OnPropertyChangedCallback<T> callback;
+
+ public DependencyProperty(OnPropertyChangedCallback<T> callback) {
+ this.callback = callback;
+ init();
+ }
+
+ public DependencyProperty(T defaultValue, OnPropertyChangedCallback<T> callback) {
+ this.set(defaultValue);
+ this.callback = callback;
+ init();
+ }
+
+ public DependencyProperty(T defaultValue) {
+ this.set(defaultValue);
+ init();
+ }
+
+ public DependencyProperty() {
+ init();
+ }
+
+ private void init() {
+ final DependencyProperty<T> that = this;
+
+ this.addOnPropertyChangedCallback(new android.databinding.Observable.OnPropertyChangedCallback() {
+ @Override
+ public void onPropertyChanged(android.databinding.Observable observable, int i) {
+ if (that.callback != null) {
+ that.callback.onPropertyChanged(that, that.get());
+ }
+ }
+ });
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/FieldUtils.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/FieldUtils.java
new file mode 100644
index 000000000..94f99359a
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/FieldUtils.java
@@ -0,0 +1,68 @@
+package com.twine.tango.sharedui.mvvm;/*
+ * Copyright 2016 Manas Chaudhari
+ *
+ * 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.
+ */
+
+import android.databinding.Observable.OnPropertyChangedCallback;
+import android.databinding.ObservableField;
+import android.support.annotation.NonNull;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableEmitter;
+import io.reactivex.ObservableOnSubscribe;
+import io.reactivex.functions.Cancellable;
+
+
+public class FieldUtils {
+ /**
+ * Converts an ObservableField to an Observable. Note that setting null value inside
+ * ObservableField (except for initial value) throws a NullPointerException.
+ * @return Observable that contains the latest value in the ObservableField
+ */
+ @NonNull
+ public static <T> Observable<T> toObservable(@NonNull final ObservableField<T> field) {
+
+ return Observable.create(new ObservableOnSubscribe<T>() {
+ @Override
+ public void subscribe(final ObservableEmitter<T> e) throws Exception {
+ T initialValue = field.get();
+ if (initialValue != null) {
+ e.onNext(initialValue);
+ }
+ final OnPropertyChangedCallback callback = new OnPropertyChangedCallback() {
+ @Override
+ public void onPropertyChanged(android.databinding.Observable observable, int i) {
+ e.onNext(field.get());
+ }
+ };
+ field.addOnPropertyChangedCallback(callback);
+ e.setCancellable(new Cancellable() {
+ @Override
+ public void cancel() throws Exception {
+ field.removeOnPropertyChangedCallback(callback);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * A convenient wrapper for {@code ReadOnlyField#create(Observable)}
+ * @return DataBinding field created from the specified Observable
+ */
+ @NonNull
+ public static <T> ReadOnlyField<T> toField(@NonNull final Observable<T> observable) {
+ return ReadOnlyField.create(observable);
+ }
+} \ No newline at end of file
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ReadOnlyField.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ReadOnlyField.java
new file mode 100644
index 000000000..cf6f8abd8
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ReadOnlyField.java
@@ -0,0 +1,74 @@
+package com.twine.tango.sharedui.mvvm;/*
+ * Copyright 2016 Manas Chaudhari
+ *
+ * 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.
+ */
+
+import android.databinding.ObservableField;
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import java.util.HashMap;
+
+import io.reactivex.Observable;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.functions.Consumer;
+
+
+public class ReadOnlyField<T> extends ObservableField<T> {
+ private final Observable<T> source;
+ private final HashMap<OnPropertyChangedCallback, Disposable> subscriptions = new HashMap<>();
+
+ public static <U> ReadOnlyField<U> create(@NonNull Observable<U> source) {
+ return new ReadOnlyField<>(source);
+ }
+
+ private ReadOnlyField(@NonNull Observable<T> source) {
+ super();
+ this.source = source.doOnNext(new Consumer<T>() {
+ @Override
+ public void accept(T t) throws Exception {
+ ReadOnlyField.super.set(t);
+ }
+ }).doOnError(new Consumer<Throwable>() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ Log.e("Twine", "onError in source observable", throwable);
+ }
+ }).onErrorResumeNext(Observable.<T>empty()).share();
+ }
+
+ /**
+ * @deprecated Setter of com.sirilix.mvvm.ReadOnlyField does nothing. Merge with the source Observable instead.
+ * See <a href="https://github.com/manas-chaudhari/android-mvvm/tree/master/Documentation/ObservablesAndSetters.md">Documentation/ObservablesAndSetters.md</a>
+ */
+ @Deprecated
+ @Override
+ public void set(T value) {
+ }
+
+ @Override
+ public synchronized void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
+ super.addOnPropertyChangedCallback(callback);
+ subscriptions.put(callback, source.subscribe());
+ }
+
+ @Override
+ public synchronized void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
+ super.removeOnPropertyChangedCallback(callback);
+ Disposable subscription = subscriptions.remove(callback);
+ if (subscription != null && !subscription.isDisposed()) {
+ subscription.dispose();
+ }
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/RelayCommand.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/RelayCommand.java
new file mode 100644
index 000000000..586ef9ef6
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/RelayCommand.java
@@ -0,0 +1,68 @@
+package com.twine.tango.sharedui.mvvm;
+
+import android.databinding.BindingAdapter;
+import android.view.View;
+
+/**
+ * Created by Roy on 7/27/2017.
+ */
+
+public class RelayCommand {
+
+ public interface OnCommandExecute
+ {
+ void execute();
+ }
+
+ public interface CommandCanExecute
+ {
+ boolean canExecute();
+ }
+
+ public OnCommandExecute onCommandExecute;
+
+ public CommandCanExecute commandCanExecute;
+
+ private View bindedView;
+
+ @BindingAdapter("android:command")
+ public static void CommandBinding(View view, RelayCommand command)
+ {
+ command.bindedView = view;
+
+ view.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ command.execute();
+ }
+ });
+
+ command.invalidateCommand();
+ }
+
+ public RelayCommand(OnCommandExecute onCommandExecute) {
+ this.onCommandExecute = onCommandExecute;
+ }
+
+ public RelayCommand(OnCommandExecute onCommandExecute, CommandCanExecute commandCanExecute) {
+ this.onCommandExecute = onCommandExecute;
+ this.commandCanExecute = commandCanExecute;
+ }
+
+ public RelayCommand() {
+
+ }
+
+ public void execute()
+ {
+ onCommandExecute.execute();
+ }
+
+ public void invalidateCommand()
+ {
+ if (commandCanExecute != null)
+ {
+ bindedView.setEnabled(commandCanExecute.canExecute());
+ }
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewContract.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewContract.java
new file mode 100644
index 000000000..89427070b
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewContract.java
@@ -0,0 +1,8 @@
+package com.twine.tango.sharedui.mvvm;
+
+import io.reactivex.functions.Consumer;
+
+public interface ViewContract {
+ void attachView();
+ void validateFields(Consumer<Boolean> consumer);
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewModelBase.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewModelBase.java
new file mode 100644
index 000000000..4e66d88e9
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/mvvm/ViewModelBase.java
@@ -0,0 +1,21 @@
+package com.twine.tango.sharedui.mvvm;
+
+import android.util.Log;
+
+/**
+ * Created by Roy on 7/26/2017.
+ */
+
+public abstract class ViewModelBase<T extends ViewContract> {
+
+ public T view; //TODO: Maybe use WeakReference<T> to not prevent garbage collector from disposing the activity ?
+
+ public void attachView(T view) {
+ this.view = view;
+ onViewAttached(view);
+ }
+
+ protected void onViewAttached(T view) {
+ Log.i("MVVM",view.getClass().getSimpleName() + " attached to " + this.getClass().getSimpleName());
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/AndroidNotificationProvider.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/AndroidNotificationProvider.java
new file mode 100644
index 000000000..7fd2cfacd
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/AndroidNotificationProvider.java
@@ -0,0 +1,22 @@
+package com.twine.tango.sharedui.notifications;
+
+import android.content.Context;
+import android.widget.Toast;
+
+/**
+ * Created by Roy on 7/29/2017.
+ */
+
+public class AndroidNotificationProvider implements NotificationProvider {
+
+ private Context context;
+
+ public AndroidNotificationProvider(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ public void notify(String message) {
+ Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/NotificationProvider.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/NotificationProvider.java
new file mode 100644
index 000000000..87fe8359d
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/notifications/NotificationProvider.java
@@ -0,0 +1,11 @@
+package com.twine.tango.sharedui.notifications;
+
+/**
+ * Created by Roy on 7/29/2017.
+ */
+
+public interface NotificationProvider {
+
+ void notify(String message);
+
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerAdapter.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerAdapter.java
new file mode 100644
index 000000000..2e151cc01
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerAdapter.java
@@ -0,0 +1,61 @@
+package com.twine.tango.sharedui.paging;
+
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+
+import com.twine.tango.sharedui.containers.FragmentBaseV4;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Roy on 7/24/2017.
+ */
+
+public class PagerAdapter extends FragmentPagerAdapter {
+
+ private List<FragmentBaseV4> fragments;
+ private int currentPosition;
+
+ public PagerAdapter(FragmentManager fm) {
+ super(fm);
+
+ fragments = new ArrayList<>();
+ }
+
+ public void addFragment(FragmentBaseV4 fragment)
+ {
+ fragments.add(fragment);
+ }
+
+ public void removeFragment(FragmentBaseV4 fragment)
+ {
+ fragments.remove(fragment);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ currentPosition = position;
+ return fragments.get(position);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getCurrentFragment() {
+ return (T) getItem(currentPosition);
+ }
+
+ @Override
+ public int getCount() {
+ return fragments.size();
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ if (fragments.get(position) != null) {
+ return fragments.get(position).getTitle();
+ } else {
+ return "";
+ }
+ }
+}
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerZoomTransform.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerZoomTransform.java
new file mode 100644
index 000000000..9c114c01c
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/paging/PagerZoomTransform.java
@@ -0,0 +1,44 @@
+package com.twine.tango.sharedui.paging;
+
+import android.support.v4.view.ViewPager;
+import android.view.View;
+
+public class PagerZoomTransform implements ViewPager.PageTransformer {
+ private static final float MIN_SCALE = 0.85f;
+ private static final float MIN_ALPHA = 0.5f;
+
+ public void transformPage(View view, float position) {
+ int pageWidth = view.getWidth();
+ int pageHeight = view.getHeight();
+
+ if (position < -1) { // [-Infinity,-1)
+ // This page is way off-screen to the left.
+ view.setAlpha(0);
+
+ } else if (position <= 1) { // [-1,1]
+ // Modify the default slide transition to shrink the page as well
+ float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
+ float vertMargin = pageHeight * (1 - scaleFactor) / 2;
+ float horzMargin = pageWidth * (1 - scaleFactor) / 2;
+ if (position < 0) {
+ view.setTranslationX(horzMargin - vertMargin / 2);
+ } else {
+ view.setTranslationX(-horzMargin + vertMargin / 2);
+ }
+
+ // Scale the page down (between MIN_SCALE and 1)
+ view.setScaleX(scaleFactor);
+ view.setScaleY(scaleFactor);
+
+ // Fade the page relative to its size.
+ view.setAlpha(MIN_ALPHA +
+ (scaleFactor - MIN_SCALE) /
+ (1 - MIN_SCALE) * (1 - MIN_ALPHA));
+
+ } else { // (1,+Infinity]
+ // This page is way off-screen to the right.
+ view.setAlpha(0);
+ }
+ }
+}
+
diff --git a/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/validation/ViewValidator.java b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/validation/ViewValidator.java
new file mode 100644
index 000000000..4c62b7548
--- /dev/null
+++ b/Software/Android_Studio/Tango.SharedUI/src/main/java/com/twine/tango/sharedui/validation/ViewValidator.java
@@ -0,0 +1,23 @@
+package com.twine.tango.sharedui.validation;
+
+import com.mobsandgeeks.saripaar.ValidationError;
+import com.mobsandgeeks.saripaar.Validator;
+
+import java.util.List;
+
+/**
+ * Created by Roy on 11/6/2017.
+ */
+
+public class ViewValidator implements Validator.ValidationListener {
+
+ @Override
+ public void onValidationSucceeded() {
+
+ }
+
+ @Override
+ public void onValidationFailed(List<ValidationError> errors) {
+
+ }
+}