data) {
- for (M item : data) {
- addInternal(item);
- }
-
- int addedSize = data.size();
- int oldSize = models.size() - addedSize;
- notifyItemRangeInserted(oldSize, addedSize);
- }
-
- public void addItem(M item) {
- addInternal(item);
- notifyItemInserted(models.size());
- }
-
- public void updateItem(M item) {
- Object modelId = getModelId(item);
-
- // Swap the model
- int position = getItemPosition(item);
- if (position >= 0) {
- models.remove(position);
- models.add(position, item);
- }
-
- // Swap the presenter
- P existingPresenter = presenters.get(modelId);
- if (existingPresenter != null) {
- existingPresenter.setModel(item);
- }
-
- if (position >= 0) {
- notifyItemChanged(position);
- }
- }
-
- public void removeItem(M item) {
- int position = getItemPosition(item);
- if (position >= 0) {
- models.remove(item);
- }
- presenters.remove(getModelId(item));
-
- if (position >= 0) {
- notifyItemRemoved(position);
- }
- }
-
- private int getItemPosition(M item) {
- Object modelId = getModelId(item);
-
- int position = -1;
- for (int i = 0; i < models.size(); i++) {
- M model = models.get(i);
- if (getModelId(model).equals(modelId)) {
- position = i;
- break;
- }
- }
- return position;
- }
-
- private void addInternal(M item) {
- System.err.println("Adding item " + getModelId(item));
- models.add(item);
- presenters.put(getModelId(item), createPresenter(item));
- }
-
- @Override
- public int getItemCount() {
- return models.size();
- }
-
- @Override
- protected M getItem(int position) {
- return models.get(position);
- }
-}
diff --git a/app/src/main/java/com/remind101/archexample/MvpViewHolder.java b/app/src/main/java/com/remind101/archexample/MvpViewHolder.java
deleted file mode 100644
index 5cbef44..0000000
--- a/app/src/main/java/com/remind101/archexample/MvpViewHolder.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.remind101.archexample;
-
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-import com.remind101.archexample.presenters.BasePresenter;
-
-public abstract class MvpViewHolder extends RecyclerView.ViewHolder {
- protected P presenter;
-
- public MvpViewHolder(View itemView) {
- super(itemView);
- }
-
- public void bindPresenter(P presenter) {
- this.presenter = presenter;
- presenter.bindView(this);
- }
-
- public void unbindPresenter() {
- presenter = null;
- }
-}
diff --git a/app/src/main/java/com/remind101/archexample/PresenterManager.java b/app/src/main/java/com/remind101/archexample/PresenterManager.java
deleted file mode 100644
index cf4fdce..0000000
--- a/app/src/main/java/com/remind101/archexample/PresenterManager.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.remind101.archexample;
-
-import android.os.Bundle;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.remind101.archexample.presenters.BasePresenter;
-
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-
-public class PresenterManager {
- private static final String SIS_KEY_PRESENTER_ID = "presenter_id";
- private static PresenterManager instance;
-
- private final AtomicLong currentId;
-
- private final Cache> presenters;
-
- PresenterManager(long maxSize, long expirationValue, TimeUnit expirationUnit) {
- currentId = new AtomicLong();
-
- presenters = CacheBuilder.newBuilder()
- .maximumSize(maxSize)
- .expireAfterWrite(expirationValue, expirationUnit)
- .build();
- }
-
- public static PresenterManager getInstance() {
- if (instance == null) {
- instance = new PresenterManager(10, 30, TimeUnit.SECONDS);
- }
- return instance;
- }
-
- public > P restorePresenter(Bundle savedInstanceState) {
- Long presenterId = savedInstanceState.getLong(SIS_KEY_PRESENTER_ID);
- P presenter = (P) presenters.getIfPresent(presenterId);
- presenters.invalidate(presenterId);
- return presenter;
- }
-
- public void savePresenter(BasePresenter, ?> presenter, Bundle outState) {
- long presenterId = currentId.incrementAndGet();
- presenters.put(presenterId, presenter);
- outState.putLong(SIS_KEY_PRESENTER_ID, presenterId);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/remind101/archexample/Utils.java b/app/src/main/java/com/remind101/archexample/Utils.java
new file mode 100644
index 0000000..bed0183
--- /dev/null
+++ b/app/src/main/java/com/remind101/archexample/Utils.java
@@ -0,0 +1,10 @@
+package com.remind101.archexample;
+
+import android.util.Log;
+
+public class Utils {
+
+ public static void logIt(String message) {
+ Log.e("APP_TAG", message);
+ }
+}
diff --git a/app/src/main/java/com/remind101/archexample/presenters/BasePresenter.java b/app/src/main/java/com/remind101/archexample/presenters/BasePresenter.java
deleted file mode 100644
index f97142c..0000000
--- a/app/src/main/java/com/remind101/archexample/presenters/BasePresenter.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.remind101.archexample.presenters;
-
-import android.support.annotation.NonNull;
-
-import java.lang.ref.WeakReference;
-
-public abstract class BasePresenter {
- protected M model;
- private WeakReference view;
-
- public void setModel(M model) {
- resetState();
- this.model = model;
- if (setupDone()) {
- updateView();
- }
- }
-
- protected void resetState() {
- }
-
- public void bindView(@NonNull V view) {
- this.view = new WeakReference<>(view);
- if (setupDone()) {
- updateView();
- }
- }
-
- public void unbindView() {
- this.view = null;
- }
-
- protected V view() {
- if (view == null) {
- return null;
- } else {
- return view.get();
- }
- }
-
- protected abstract void updateView();
-
- protected boolean setupDone() {
- return view() != null && model != null;
- }
-}
diff --git a/app/src/main/java/com/remind101/archexample/presenters/CounterPresenter.java b/app/src/main/java/com/remind101/archexample/presenters/CounterPresenter.java
deleted file mode 100644
index 6d92e8b..0000000
--- a/app/src/main/java/com/remind101/archexample/presenters/CounterPresenter.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.remind101.archexample.presenters;
-
-import com.remind101.archexample.CounterDatabase;
-import com.remind101.archexample.models.Counter;
-import com.remind101.archexample.views.CounterView;
-
-public class CounterPresenter extends BasePresenter {
- private static final int MIN_VALUE = 0;
- private static final int MAX_VALUE = 99;
-
- @Override
- protected void updateView() {
- view().setCounterName(model.getName());
- int value = model.getValue();
- view().setCounterValue(value);
- view().setMinusButtonEnabled(value > MIN_VALUE);
- view().setPlusButtonEnabled(value < MAX_VALUE);
- }
-
- public void onMinusButtonClicked() {
- if (setupDone() && model.getValue() > MIN_VALUE) {
- model.setValue(model.getValue() - 1);
- CounterDatabase.getInstance().saveCounter(model);
- updateView();
- }
- }
-
- public void onPlusButtonClicked() {
- if (setupDone() && model.getValue() < MAX_VALUE) {
- model.setValue(model.getValue() + 1);
- CounterDatabase.getInstance().saveCounter(model);
- updateView();
- }
- }
-
- public void onCounterClicked() {
- if (setupDone()) {
- view().goToDetailView(model);
- }
- }
-}
diff --git a/app/src/main/java/com/remind101/archexample/views/MainView.java b/app/src/main/java/com/remind101/archexample/presenters/IMainView.java
similarity index 50%
rename from app/src/main/java/com/remind101/archexample/views/MainView.java
rename to app/src/main/java/com/remind101/archexample/presenters/IMainView.java
index 748ce34..17ca554 100644
--- a/app/src/main/java/com/remind101/archexample/views/MainView.java
+++ b/app/src/main/java/com/remind101/archexample/presenters/IMainView.java
@@ -1,13 +1,13 @@
-package com.remind101.archexample.views;
+package com.remind101.archexample.presenters;
+import com.arellomobile.mvp.MvpView;
import com.remind101.archexample.models.Counter;
import java.util.List;
-public interface MainView {
+public interface IMainView extends MvpView {
void showCounters(List counters);
-
void showLoading();
-
void showEmpty();
-}
+ void showMenu(boolean state);
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/remind101/archexample/presenters/MainPresenter.java b/app/src/main/java/com/remind101/archexample/presenters/MainPresenter.java
index c2a0c78..df63928 100644
--- a/app/src/main/java/com/remind101/archexample/presenters/MainPresenter.java
+++ b/app/src/main/java/com/remind101/archexample/presenters/MainPresenter.java
@@ -1,51 +1,78 @@
-package com.remind101.archexample.presenters;
+package com.remind101.archexample.moxy.presenter;
import android.os.AsyncTask;
import android.os.SystemClock;
-import android.support.annotation.NonNull;
+import com.arellomobile.mvp.InjectViewState;
+import com.arellomobile.mvp.MvpPresenter;
import com.remind101.archexample.CounterDatabase;
import com.remind101.archexample.models.Counter;
-import com.remind101.archexample.views.MainView;
+import com.remind101.archexample.presenters.IMainView;
import java.util.List;
-public class MainPresenter extends BasePresenter, MainView> {
+@InjectViewState
+public class MainPresenter extends MvpPresenter {
+
private boolean isLoadingData = false;
+ private List model;
@Override
- protected void updateView() {
- // Business logic is in the presenter
- if (model.size() == 0) {
- view().showEmpty();
- } else {
- view().showCounters(model);
+ protected void onFirstViewAttach() {
+ super.onFirstViewAttach();
+
+ // Let's not reload data if it's already here
+ if (model == null && !isLoadingData) {
+ getViewState().showLoading();
+ loadData();
}
}
@Override
- public void bindView(@NonNull MainView view) {
- super.bindView(view);
+ public void attachView(IMainView view) {
+ super.attachView(view);
- // Let's not reload data if it's already here
if (model == null && !isLoadingData) {
- view().showLoading();
+ getViewState().showLoading();
loadData();
}
}
+ private void setModel(List model) {
+
+ this.model = model;
+ if (setupDone()) {
+ updateView();
+ }
+ }
+
+ private boolean setupDone() {
+ return model != null;
+ }
+
+ private void updateView() {
+ if (model.size() == 0) {
+ getViewState().showEmpty();
+ } else {
+ getViewState().showCounters(model);
+ }
+ }
+
private void loadData() {
isLoadingData = true;
new LoadDataTask().execute();
}
+ // ID нового Counter генерится здесь как очередной порядковый номер в массиве.
+ // По этому ID Counter также сохраняется к кэше. Это для того, чтобы поддерживать
+ // совпадение индекса в списке, индекса элемента в RecyclerView и ID в кэше.
public void onAddCounterClicked() {
Counter counter = new Counter();
- counter.setName("New Counter");
+ counter.setId(model.size());
+ counter.setName("New Counter " + model.size());
counter.setValue(0);
- // Update view immediately
- model.add(counter);
+ model.add(model.size(), counter);
CounterDatabase.getInstance().saveCounter(counter);
updateView();
}
@@ -53,6 +80,13 @@ public void onAddCounterClicked() {
// It's OK for this class not to be static and to keep a reference to the Presenter, as this
// is retained during orientation changes and is lightweight (has no activity/view reference)
private class LoadDataTask extends AsyncTask {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ getViewState().showMenu(false);
+ }
+
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(3000);
@@ -63,6 +97,7 @@ protected Void doInBackground(Void... params) {
protected void onPostExecute(Void aVoid) {
setModel(CounterDatabase.getInstance().getAllCounters());
isLoadingData = false;
+ getViewState().showMenu(true);
}
}
}
diff --git a/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterAdapter.java b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterAdapter.java
new file mode 100644
index 0000000..0ac294f
--- /dev/null
+++ b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterAdapter.java
@@ -0,0 +1,58 @@
+package com.remind101.archexample.recyclerview_with_moxy;
+
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.arellomobile.mvp.MvpDelegate;
+import com.remind101.archexample.R;
+import com.remind101.archexample.models.Counter;
+import com.remind101.archexample.recyclerview_with_moxy.CounterViewHolder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class CounterAdapter extends RecyclerView.Adapter {
+
+ protected final List models;
+
+ private MvpDelegate mParentDelegate;
+
+ public CounterAdapter(MvpDelegate mParentDelegate) {
+ this.mParentDelegate = mParentDelegate;
+ models = new ArrayList<>();
+ }
+
+ // Списку требуется создать новый элемент
+ @NonNull
+ public CounterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ return new CounterViewHolder(
+ mParentDelegate,
+ LayoutInflater
+ .from(parent.getContext()).inflate(R.layout.counter_row,
+ parent,
+ false));
+ }
+
+ // Списку нужно заполнить данными элемент в позиции position. в Holder
+ // передаем только position. По этому знаению его Presenter найдет
+ // нужный Counter
+ @Override
+ public void onBindViewHolder(CounterViewHolder holder, int position) {
+ holder.bindPosition(position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return models.size();
+ }
+
+ // Обновить все содержимое списка
+ public void clearAndAddAll(Collection data) {
+ models.clear();
+ models.addAll(data);
+ notifyDataSetChanged();
+ }
+}
diff --git a/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterPresenter.java b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterPresenter.java
new file mode 100644
index 0000000..4c04197
--- /dev/null
+++ b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterPresenter.java
@@ -0,0 +1,67 @@
+package com.remind101.archexample.recyclerview_with_moxy;
+
+import com.arellomobile.mvp.InjectViewState;
+import com.arellomobile.mvp.MvpPresenter;
+import com.remind101.archexample.CounterDatabase;
+import com.remind101.archexample.models.Counter;
+
+@InjectViewState
+public class CounterPresenter extends MvpPresenter {
+ private static final int MIN_VALUE = 0;
+ private static final int MAX_VALUE = 5;
+ private Counter model;
+
+ private void updateView(int value) {
+ try {
+ getViewState().setMinusButtonEnabled(value > MIN_VALUE);
+ getViewState().setPlusButtonEnabled(value < MAX_VALUE);
+ getViewState().setCounterValue(value);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onMinusButtonClicked(int position) {
+ try {
+ model = CounterDatabase.getInstance().getCounter(position);
+ int value = model.getValue();
+
+ if(value > MIN_VALUE) {
+ model.setValue(--value);
+ CounterDatabase.getInstance().saveCounter(model);
+ updateView(value);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void onPlusButtonClicked(int position) {
+ try {
+ model = CounterDatabase.getInstance().getCounter(position);
+ int value = model.getValue();
+
+ if(value < MAX_VALUE) {
+ model.setValue(++value);
+ CounterDatabase.getInstance().saveCounter(model);
+ updateView(value);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Moxy-методы onFirstViewAttach() и attachView(View view)
+ * вызываются раньше и position для них не актуальна, поэтому
+ * там её не нужно использовать
+ */
+
+ public void onBindPosition(int position) {
+
+ model = CounterDatabase.getInstance().getCounter(position);
+ getViewState().setCounterName(model.getName());
+ updateView(model.getValue());
+ }
+}
diff --git a/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterView.java b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterView.java
new file mode 100644
index 0000000..0e5ddf3
--- /dev/null
+++ b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterView.java
@@ -0,0 +1,19 @@
+package com.remind101.archexample.recyclerview_with_moxy;
+
+import com.arellomobile.mvp.MvpView;
+import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy;
+import com.arellomobile.mvp.viewstate.strategy.SingleStateStrategy;
+import com.arellomobile.mvp.viewstate.strategy.SkipStrategy;
+import com.arellomobile.mvp.viewstate.strategy.StateStrategyType;
+
+public interface CounterView extends MvpView {
+ @StateStrategyType(SkipStrategy.class)
+ void setCounterName(String name);
+ @StateStrategyType(SkipStrategy.class)
+ void setCounterValue(int value);
+
+ @StateStrategyType(SkipStrategy.class)
+ void setMinusButtonEnabled(boolean enabled);
+ @StateStrategyType(SkipStrategy.class)
+ void setPlusButtonEnabled(boolean enabled);
+}
diff --git a/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterViewHolder.java b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterViewHolder.java
new file mode 100644
index 0000000..3e140d6
--- /dev/null
+++ b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/CounterViewHolder.java
@@ -0,0 +1,94 @@
+package com.remind101.archexample.recyclerview_with_moxy;
+
+import android.graphics.Color;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.arellomobile.mvp.MvpDelegate;
+import com.arellomobile.mvp.presenter.InjectPresenter;
+import com.arellomobile.mvp.presenter.PresenterType;
+import com.arellomobile.mvp.presenter.ProvidePresenterTag;
+import com.remind101.archexample.R;
+
+public class CounterViewHolder extends MvpViewHolder implements CounterView {
+ private final View listItemView;
+ private final TextView counterName;
+ private final TextView counterValue;
+ private final ImageView minusButton;
+ private final ImageView plusButton;
+
+ private int position = 0;
+
+ @InjectPresenter(type = PresenterType.GLOBAL)
+ public CounterPresenter presenter;
+
+ /**
+ * Требуется для того чтобы сгенерировать уникальный тэг для презентера данного MvpView,
+ * то есть данного холдера. Если этого не сделать, то у всех холдеров будет один и тот же инстанс
+ * презентера и он будет ОДНОВРЕМЕННО обновлять их все ОДИНАКОВЫМИ данными ))
+ *
+ * https://github.com/Arello-Mobile/Moxy/wiki/Provides-Presenter-and-its-Tag
+ */
+ @ProvidePresenterTag(presenterClass = CounterPresenter.class, type = PresenterType.GLOBAL)
+ String provideRepositoryPresenterTag() {
+ return Long.toString((long)hashCode() + System.currentTimeMillis());
+ }
+
+ public CounterViewHolder(MvpDelegate mParentDelegate, View itemView) {
+ super(mParentDelegate, itemView);
+
+ listItemView = itemView;
+ counterName = itemView.findViewById(R.id.counter_name);
+ counterValue = itemView.findViewById(R.id.counter_value);
+ minusButton = itemView.findViewById(R.id.minus_button);
+ plusButton = itemView.findViewById(R.id.plus_button);
+
+ getMvpDelegate().onCreate();
+ getMvpDelegate().onAttach();
+ }
+
+ @Override
+ public void setCounterName(String name) {
+ counterName.setText(name);
+ }
+
+ @Override
+ public void setCounterValue(int value) {
+ counterValue.setText(String.valueOf(value));
+ }
+
+ @Override
+ public void setMinusButtonEnabled(boolean enabled) {
+ minusButton.getDrawable().setTint(enabled ? Color.BLACK : Color.GRAY);
+ minusButton.setClickable(enabled);
+ }
+
+ @Override
+ public void setPlusButtonEnabled(boolean enabled) {
+ plusButton.getDrawable().setTint(enabled ? Color.BLACK : Color.GRAY);
+ plusButton.setClickable(enabled);
+ }
+
+ public void bindPosition(int position) {
+// destroyMvpDelegate();
+// createMvpDelegate();
+
+ this.position = position;
+ presenter.onBindPosition(position);
+
+ minusButton.setOnClickListener( view -> {
+ presenter.onMinusButtonClicked(position);
+ });
+
+ plusButton.setOnClickListener( view -> {
+ presenter.onPlusButtonClicked(position);
+ });
+ }
+
+ // Critical! Return this item unique id
+ @Override
+ protected String getMvpChildId() {
+ return listItemView == null ? null : Integer.toString(listItemView.getId());
+ }
+}
diff --git a/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/MvpViewHolder.java b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/MvpViewHolder.java
new file mode 100644
index 0000000..6223602
--- /dev/null
+++ b/app/src/main/java/com/remind101/archexample/recyclerview_with_moxy/MvpViewHolder.java
@@ -0,0 +1,54 @@
+/**
+ * Materials:
+ * https://gist.github.com/senneco/ef2910a5b53aacdb053ebca21b10ef77
+ *
+ * and
+ *
+ * https://gist.github.com/AlexeyKorshun/abb20751b23d274aac86a63108094866
+ */
+
+package com.remind101.archexample.recyclerview_with_moxy;
+
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.arellomobile.mvp.MvpDelegate;
+
+public abstract class MvpViewHolder extends RecyclerView.ViewHolder {
+
+ protected MvpDelegate mMvpDelegate;
+ protected MvpDelegate mParentDelegate;
+
+ public MvpViewHolder(MvpDelegate> parentDelegate, final View itemView) {
+ super(itemView);
+ mParentDelegate = parentDelegate;
+ }
+
+ @Nullable
+ protected MvpDelegate getMvpDelegate() {
+ if (mMvpDelegate == null && getMvpChildId() != null) {
+ mMvpDelegate = new MvpDelegate(this);
+ mMvpDelegate.setParentDelegate(mParentDelegate, getMvpChildId());
+ }
+ return mMvpDelegate;
+ }
+
+ protected void destroyMvpDelegate() {
+ if (getMvpDelegate() != null) {
+ getMvpDelegate().onSaveInstanceState();
+ getMvpDelegate().onDetach();
+ mMvpDelegate = null;
+ }
+ }
+
+ protected void createMvpDelegate() {
+ if (getMvpDelegate() != null) {
+ getMvpDelegate().onCreate();
+ getMvpDelegate().onAttach();
+ getMvpDelegate().onSaveInstanceState();
+ }
+ }
+
+ protected abstract String getMvpChildId();
+}
diff --git a/app/src/main/java/com/remind101/archexample/views/CounterView.java b/app/src/main/java/com/remind101/archexample/views/CounterView.java
deleted file mode 100644
index a539d75..0000000
--- a/app/src/main/java/com/remind101/archexample/views/CounterView.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.remind101.archexample.views;
-
-import com.remind101.archexample.models.Counter;
-
-public interface CounterView {
- void setCounterName(String name);
-
- void setCounterValue(int value);
-
- void setMinusButtonEnabled(boolean enabled);
-
- void setPlusButtonEnabled(boolean enabled);
-
- void goToDetailView(Counter counter);
-}
diff --git a/app/src/main/java/com/remind101/archexample/views/CounterViewHolder.java b/app/src/main/java/com/remind101/archexample/views/CounterViewHolder.java
deleted file mode 100644
index 8de4d5c..0000000
--- a/app/src/main/java/com/remind101/archexample/views/CounterViewHolder.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package com.remind101.archexample.views;
-
-import android.graphics.Color;
-import android.support.annotation.Nullable;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.remind101.archexample.MvpViewHolder;
-import com.remind101.archexample.R;
-import com.remind101.archexample.models.Counter;
-import com.remind101.archexample.presenters.CounterPresenter;
-
-public class CounterViewHolder extends MvpViewHolder implements CounterView {
- private final TextView counterName;
- private final TextView counterValue;
- private final ImageView minusButton;
- private final ImageView plusButton;
- @Nullable private OnCounterClickListener listener;
-
- public CounterViewHolder(View itemView) {
- super(itemView);
- counterName = (TextView) itemView.findViewById(R.id.counter_name);
- counterValue = (TextView) itemView.findViewById(R.id.counter_value);
- minusButton = (ImageView) itemView.findViewById(R.id.minus_button);
- plusButton = (ImageView) itemView.findViewById(R.id.plus_button);
-
- minusButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- presenter.onMinusButtonClicked();
- }
- });
- plusButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- presenter.onPlusButtonClicked();
- }
- });
- itemView.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- presenter.onCounterClicked();
- }
- });
- }
-
- public void setListener(@Nullable OnCounterClickListener listener) {
- this.listener = listener;
- }
-
- @Override
- public void setCounterName(String name) {
- counterName.setText(name);
- }
-
- @Override
- public void setCounterValue(int value) {
- counterValue.setText(String.valueOf(value));
- }
-
- @Override
- public void setMinusButtonEnabled(boolean enabled) {
- minusButton.getDrawable().setTint(enabled ? Color.BLACK : Color.GRAY);
- minusButton.setClickable(enabled);
- }
-
- @Override
- public void setPlusButtonEnabled(boolean enabled) {
- plusButton.getDrawable().setTint(enabled ? Color.BLACK : Color.GRAY);
- plusButton.setClickable(enabled);
- }
-
- @Override
- public void goToDetailView(Counter counter) {
- if (listener != null) {
- listener.onCounterClick(counter);
- }
- }
-
- public interface OnCounterClickListener {
- void onCounterClick(Counter counter);
- }
-}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..1f6bb29
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..0d025f9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..84f1951
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
index cde69bc..898f3ed 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
index c133a0c..64ba76f 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index bfa42f0..e5ed465 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index 324e72c..b0907ca 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index aee44e1..2c18de9 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
deleted file mode 100644
index 63fc816..0000000
--- a/app/src/main/res/values-w820dp/dimens.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- 64dp
-
diff --git a/app/src/test/java/com/remind101/archexample/ExampleUnitTest.java b/app/src/test/java/com/remind101/archexample/ExampleUnitTest.java
new file mode 100644
index 0000000..7114fc1
--- /dev/null
+++ b/app/src/test/java/com/remind101/archexample/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.remind101.archexample;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/com/remind101/archexample/presenters/CounterPresenterTest.java b/app/src/test/java/com/remind101/archexample/presenters/CounterPresenterTest.java
deleted file mode 100644
index dee387a..0000000
--- a/app/src/test/java/com/remind101/archexample/presenters/CounterPresenterTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package com.remind101.archexample.presenters;
-
-import com.remind101.archexample.models.Counter;
-import com.remind101.archexample.views.CounterView;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-
-public class CounterPresenterTest {
- private CounterPresenter presenter;
- private CounterView view;
- private Counter counter;
-
- @Before
- public void setup() {
- presenter = spy(new CounterPresenter());
- view = mock(CounterView.class);
- presenter.bindView(view);
- counter = new Counter();
- counter.setId(4);
- counter.setName("My Counter");
- counter.setValue(18);
- }
-
- @Test
- public void testUpdateView_setsName() {
- presenter.setModel(counter);
-
- verify(view).setCounterName(eq("My Counter"));
- }
-
- @Test
- public void testUpdateView_setsValue() {
- presenter.setModel(counter);
-
- verify(view).setCounterValue(eq(18));
- }
-
- @Test
- public void testUpdateView_whenCounterGreaterThan0_setsMinusButtonEnabled() {
- presenter.setModel(counter);
-
- verify(view).setMinusButtonEnabled(eq(true));
- }
-
- @Test
- public void testUpdateView_whenCounterEqual0_setsMinusButtonDisabled() {
- counter.setValue(0);
- presenter.setModel(counter);
-
- verify(view).setMinusButtonEnabled(eq(false));
- }
-
- @Test
- public void testUpdateView_whenCounterLowerThan99_setsPlusButtonEnabled() {
- presenter.setModel(counter);
-
- verify(view).setPlusButtonEnabled(eq(true));
- }
-
- @Test
- public void testUpdateView_whenCounterEqual99_setsMinusButtonDisabled() {
- counter.setValue(99);
- presenter.setModel(counter);
-
- verify(view).setPlusButtonEnabled(eq(false));
- }
-
- @Test
- public void testOnMinusButtonClicked_whenCounterGreaterThan0_decrementsValue() {
- counter.setValue(16);
- presenter.setModel(counter);
- reset(view);
-
- presenter.onMinusButtonClicked();
- assertEquals(counter.getValue(), 15);
- }
-
- @Test
- public void testOnMinusButtonClicked_whenCounterEquals0_doesNotDoAnything() {
- counter.setValue(0);
- presenter.setModel(counter);
- reset(view);
-
- presenter.onMinusButtonClicked();
- assertEquals(counter.getValue(), 0);
- }
-
- @Test
- public void testOnPlusButtonClicked_whenCounterLowerThan99_incrementsValue() {
- counter.setValue(16);
- presenter.setModel(counter);
- reset(view);
-
- presenter.onPlusButtonClicked();
- assertEquals(counter.getValue(), 17);
- }
-
- @Test
- public void testOnPlusButtonClicked_whenCounterEquals99_doesNotDoAnything() {
- counter.setValue(99);
- presenter.setModel(counter);
- reset(view);
-
- presenter.onPlusButtonClicked();
- assertEquals(counter.getValue(), 99);
- }
-
- @Test
- public void testOnCounterClicked_opensDetailView() {
- presenter.setModel(counter);
- reset(view);
-
- presenter.onCounterClicked();
- verify(view).goToDetailView(eq(counter));
- }
-}
diff --git a/build.gradle b/build.gradle
index e0b366a..66145ac 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,11 +2,14 @@
buildscript {
repositories {
+ google()
jcenter()
+ mavenCentral()
+
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.5.0'
-
+ classpath 'com.android.tools.build:gradle:3.4.1'
+
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
@@ -14,7 +17,9 @@ buildscript {
allprojects {
repositories {
+ google()
jcenter()
+
}
}
diff --git a/gradle.properties b/gradle.properties
index 1d3591c..82618ce 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,15 @@
# Project-wide Gradle settings.
-
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
-
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
-
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-# Default value: -Xmx10248m -XX:MaxPermSize=256m
-# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-
+org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
\ No newline at end of file
+# org.gradle.parallel=true
+
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..2d9c501
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Oct 22 18:35:20 MSK 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/gradlew b/gradlew
index 91a7e26..cccdd3d 100755
--- a/gradlew
+++ b/gradlew
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
##############################################################################
##
@@ -6,20 +6,38 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
-warn ( ) {
+warn () {
echo "$*"
}
-die ( ) {
+die () {
echo
echo "$*"
echo
@@ -30,6 +48,7 @@ die ( ) {
cygwin=false
msys=false
darwin=false
+nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -40,31 +59,11 @@ case "`uname`" in
MINGW* )
msys=true
;;
+ NONSTOP* )
+ nonstop=true
+ ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -114,6 +113,7 @@ fi
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -154,11 +154,19 @@ if $cygwin ; then
esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index aec9973..e95643d 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,10 +46,9 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@@ -60,11 +59,6 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line