From 8b7e89f43bd065d590c9b433d8ba3f1818164926 Mon Sep 17 00:00:00 2001 From: Daniele Date: Tue, 21 Sep 2021 09:25:28 +0200 Subject: [PATCH] WIP implementing MVVM --- .idea/misc.xml | 2 +- .../detail/GeofavoriteDetailActivity.java | 65 +++++++------ .../GeofavoriteDetailActivityViewModel.java | 52 ++++++++++ .../activity/main/MainActivity.java | 95 ++++++++----------- .../activity/main/MainActivityViewModel.java | 41 ++++++-- .../repository/GeofavoriteRepository.java | 95 +++++++++++++++++-- .../layout/activity_geofavorite_detail.xml | 7 ++ app/src/main/res/values/strings.xml | 1 + 8 files changed, 254 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivityViewModel.java diff --git a/.idea/misc.xml b/.idea/misc.xml index dd95f50..d3c8f54 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -10,7 +10,7 @@ - + diff --git a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivity.java b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivity.java index 0eacc7b..d106083 100644 --- a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivity.java +++ b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivity.java @@ -37,6 +37,8 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.app.ActivityCompat; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; import androidx.preference.PreferenceManager; import org.osmdroid.api.IMapController; @@ -48,9 +50,11 @@ import org.threeten.bp.format.DateTimeFormatter; import org.threeten.bp.format.FormatStyle; import java.util.Date; +import java.util.List; import it.danieleverducci.nextcloudmaps.R; import it.danieleverducci.nextcloudmaps.activity.main.MainActivity; +import it.danieleverducci.nextcloudmaps.activity.main.MainActivityViewModel; import it.danieleverducci.nextcloudmaps.api.ApiProvider; import it.danieleverducci.nextcloudmaps.databinding.ActivityGeofavoriteDetailBinding; import it.danieleverducci.nextcloudmaps.model.Geofavorite; @@ -68,6 +72,7 @@ public class GeofavoriteDetailActivity extends AppCompatActivity implements Loca private ViewHolder mViewHolder; private Geofavorite mGeofavorite; + private GeofavoriteDetailActivityViewModel mViewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -112,9 +117,31 @@ public class GeofavoriteDetailActivity extends AppCompatActivity implements Loca } }); - if (getIntent().hasExtra(ARG_GEOFAVORITE)) { + mViewModel = new ViewModelProvider(this).get(GeofavoriteDetailActivityViewModel.class); + mViewModel.init(); + mViewModel.getIsUpdating().observe(this, new Observer() { + @Override + public void onChanged(@Nullable Boolean updating) { + mViewHolder.setUpdating(updating); + } + }); + mViewModel.getIsFailed().observe(this, new Observer() { + @Override + public void onChanged(@Nullable Boolean failed) { + if(failed){ + Toast.makeText(GeofavoriteDetailActivity.this, R.string.error_saving_geofavorite, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(GeofavoriteDetailActivity.this, R.string.geofavorite_saved, Toast.LENGTH_SHORT).show(); + finish(); + } + } + }); + + if (getIntent().hasExtra(ARG_GEOFAVORITE) && getIntent().getIntExtra(ARG_GEOFAVORITE, 0) != 0) { // Opening geofavorite from list - mGeofavorite = (Geofavorite) getIntent().getSerializableExtra(ARG_GEOFAVORITE); + mGeofavorite = mViewModel.getGeofavorite( + getIntent().getIntExtra(ARG_GEOFAVORITE, 0) + ); mViewHolder.hideAccuracy(); } else { // New geofavorite @@ -169,35 +196,7 @@ public class GeofavoriteDetailActivity extends AppCompatActivity implements Loca return; } - Call call; - if (mGeofavorite.getId() == 0) { - // New geofavorite - call = ApiProvider.getAPI().createGeofavorite(mGeofavorite); - } else { - // Update existing geofavorite - call = ApiProvider.getAPI().updateGeofavorite(mGeofavorite.getId(), mGeofavorite); - } - call.enqueue(new Callback() { - @Override - public void onResponse(Call call, Response response) { - if (response.isSuccessful()) - finish(); - else - onGeofavoriteSaveFailed(); - } - - @Override - public void onFailure(Call call, Throwable t) { - onGeofavoriteSaveFailed(); - Log.e(TAG, "Unable to update geofavorite: " + t.getMessage()); - } - }); - } - - private void onGeofavoriteSaveFailed() { - runOnUiThread(() -> - Toast.makeText(GeofavoriteDetailActivity.this, R.string.error_saving_geofavorite, Toast.LENGTH_SHORT).show() - ); + mViewModel.saveGeofavorite(mGeofavorite); } /** @@ -334,6 +333,10 @@ public class GeofavoriteDetailActivity extends AppCompatActivity implements Loca item.setDateModified(System.currentTimeMillis() / 1000); } + public void setUpdating(boolean updating) { + binding.progress.setVisibility(updating ? View.VISIBLE : View.GONE); + } + public void setAccuracy(float accuracy) { binding.accuracyTv.setText(getString(R.string.accuracy).replace("{accuracy}", ((int)accuracy) + "")); // Color the accuracy background with a scale from red (MINIMUM_ACCEPTABLE_ACCURACY) to green (0 meters) diff --git a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivityViewModel.java b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivityViewModel.java new file mode 100644 index 0000000..80d38b2 --- /dev/null +++ b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/detail/GeofavoriteDetailActivityViewModel.java @@ -0,0 +1,52 @@ +package it.danieleverducci.nextcloudmaps.activity.detail; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import it.danieleverducci.nextcloudmaps.model.Geofavorite; +import it.danieleverducci.nextcloudmaps.repository.GeofavoriteRepository; + +public class GeofavoriteDetailActivityViewModel extends ViewModel implements GeofavoriteRepository.OnFinished { + private GeofavoriteRepository mRepo; + private MutableLiveData mIsUpdating = new MutableLiveData<>(); + private MutableLiveData mIsFailed = new MutableLiveData<>(); + + public void init() { + mRepo = GeofavoriteRepository.getInstance(); + mRepo.setOnFinishedListener(this); + } + + public Geofavorite getGeofavorite(int id) { + return mRepo.getGeofavorite(id); + } + + public void saveGeofavorite(Geofavorite geofav) { + mRepo.saveGeofavorite(geofav); + } + + + public LiveData getIsUpdating(){ + return mIsUpdating; + } + + public LiveData getIsFailed(){ + return mIsFailed; + } + + @Override + public void onLoading() { + mIsUpdating.postValue(true); + } + @Override + public void onSuccess() { + mIsUpdating.postValue(false); + mIsFailed.postValue(false); + } + + @Override + public void onFailure() { + mIsUpdating.postValue(false); + mIsFailed.postValue(true); + } +} diff --git a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivity.java b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivity.java index dccfce4..b595be2 100644 --- a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivity.java +++ b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivity.java @@ -67,9 +67,6 @@ import androidx.lifecycle.Observer; public class MainActivity extends AppCompatActivity implements OnSortingOrderListener { - private static final int INTENT_ADD = 100; - private static final int INTENT_EDIT = 200; - private static final String TAG = "MainActivity"; private static final String NAVIGATION_KEY_ADD_GEOFAVORITE = "add"; @@ -98,8 +95,6 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - mMainActivityViewModel = new ViewModelProvider(this).get(MainActivityViewModel.class); - mMainActivityViewModel.init(); preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); int sortRule = preferences.getInt(getString(R.string.setting_sort_by), SORT_BY_CREATED); @@ -135,8 +130,39 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis recyclerView.setAdapter(geofavoriteAdapter); geofavoriteAdapter.setSortRule(sortRule); + + mMainActivityViewModel = new ViewModelProvider(this).get(MainActivityViewModel.class); + mMainActivityViewModel.init(); + mMainActivityViewModel.getIsUpdating().observe(this, new Observer() { + @Override + public void onChanged(@Nullable Boolean aBoolean) { + if(aBoolean){ + swipeRefresh.setRefreshing(true); + } + else{ + swipeRefresh.setRefreshing(false); + } + } + }); + mMainActivityViewModel.getIsFailed().observe(this, new Observer() { + @Override + public void onChanged(@Nullable Boolean aBoolean) { + if(aBoolean){ + Toast.makeText(MainActivity.this, R.string.list_geofavorite_connection_error, Toast.LENGTH_LONG).show(); + } + } + }); + mMainActivityViewModel.getGeofavorites().observe(this, new Observer>() { + @Override + public void onChanged(List geofavorites) { + geofavoriteAdapter.setGeofavoriteList(geofavorites); + } + }); + mMainActivityViewModel.updateGeofavorites(); + swipeRefresh = findViewById(R.id.swipe_refresh); - swipeRefresh.setOnRefreshListener(() -> updateGeofavorites()); + swipeRefresh.setOnRefreshListener(() -> + mMainActivityViewModel.updateGeofavorites()); fab = findViewById(R.id.add); fab.setOnClickListener(view -> addGeofavorite()); @@ -188,14 +214,6 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis updateGridIcon(gridViewEnabled); } - @Override - protected void onStart() { - super.onStart(); - - // Update list - mMainActivityViewModel.getGeofavorites().observe(this, this); - } - private void setupNavigationMenu() { ArrayList navItems = new ArrayList<>(); @@ -238,19 +256,10 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis SortingOrderDialogFragment.newInstance(sortOrder).show(fragmentTransaction, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == INTENT_ADD && resultCode == RESULT_OK) { - mMainActivityViewModel.getGeofavorites().observe(this, this); - } else if (requestCode == INTENT_EDIT && resultCode == RESULT_OK) { - mMainActivityViewModel.getGeofavorites().observe(this, this); - } - } - private void addGeofavorite() { - startActivityForResult( - new Intent(this, GeofavoriteDetailActivity.class), INTENT_ADD); + startActivity( + new Intent(this, GeofavoriteDetailActivity.class) + ); } private void show_about() { @@ -263,13 +272,6 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis startActivity(intent); } - -// @Override -// public void onErrorLoading(String message) { -// Toast.makeText(MainActivity.this, R.string.list_geofavorite_connection_error, Toast.LENGTH_LONG).show(); -// Log.e(TAG, "Unable to obtain geofavorites list: " + message); -// } - @Override public void onSortingOrderChosen(int sortSelection) { geofavoriteAdapter.setSortRule(sortSelection); @@ -308,9 +310,8 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis .setTitle(R.string.dialog_delete_title) .setPositiveButton(R.string.dialog_delete_delete, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - // TODO presenter.deleteGeofavorite(item.getId()); + mMainActivityViewModel.deleteGeofavorite(item); dialog.dismiss(); - // Callback is onGeofavoriteDeleted } }) .setNegativeButton(R.string.dialog_delete_cancel, new DialogInterface.OnClickListener() { @@ -324,30 +325,8 @@ public class MainActivity extends AppCompatActivity implements OnSortingOrderLis private void showGeofavoriteDetailActivity(Geofavorite item) { Intent i = new Intent(this, GeofavoriteDetailActivity.class); - i.putExtra(GeofavoriteDetailActivity.ARG_GEOFAVORITE, item); + i.putExtra(GeofavoriteDetailActivity.ARG_GEOFAVORITE, item.getId()); startActivity(i); } - private void updateGeofavorites() { - mMainActivityViewModel.getGeofavorites().observe(this, new Observer>() { - @Override - public void onChanged(List geofavorites) { - geofavoriteAdapter.setGeofavoriteList(geofavorites); - } - }); - - // TODO: รจ possibile registrare un solo listener? - mMainActivityViewModel.getIsUpdating().observe(this, new Observer() { - @Override - public void onChanged(@Nullable Boolean aBoolean) { - if(aBoolean){ - swipeRefresh.setRefreshing(true); - } - else{ - swipeRefresh.setRefreshing(false); - } - } - }); - } - } \ No newline at end of file diff --git a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivityViewModel.java b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivityViewModel.java index e5b46fc..0dc11c7 100644 --- a/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivityViewModel.java +++ b/app/src/main/java/it/danieleverducci/nextcloudmaps/activity/main/MainActivityViewModel.java @@ -9,25 +9,50 @@ import java.util.List; import it.danieleverducci.nextcloudmaps.model.Geofavorite; import it.danieleverducci.nextcloudmaps.repository.GeofavoriteRepository; -public class MainActivityViewModel extends ViewModel { - - private MutableLiveData> mGeofavorites; +public class MainActivityViewModel extends ViewModel implements GeofavoriteRepository.OnFinished { private GeofavoriteRepository mRepo; private MutableLiveData mIsUpdating = new MutableLiveData<>(); + private MutableLiveData mIsFailed = new MutableLiveData<>(); public void init() { - if (mGeofavorites != null) - return; - mRepo = GeofavoriteRepository.getInstance(); - mGeofavorites = mRepo.getGeofavorites(); + mRepo.setOnFinishedListener(this); } public LiveData> getGeofavorites(){ - return mGeofavorites; + mRepo.updateGeofavorites(); + return mRepo.getGeofavorites(); + } + + public void updateGeofavorites() { + mRepo.updateGeofavorites(); + } + + public void deleteGeofavorite(Geofavorite geofav) { + mRepo.deleteGeofavorite(geofav); } public LiveData getIsUpdating(){ return mIsUpdating; } + + public LiveData getIsFailed(){ + return mIsFailed; + } + + @Override + public void onLoading() { + mIsUpdating.postValue(true); + } + @Override + public void onSuccess() { + mIsUpdating.postValue(false); + mIsFailed.postValue(false); + } + + @Override + public void onFailure() { + mIsUpdating.postValue(false); + mIsFailed.postValue(true); + } } diff --git a/app/src/main/java/it/danieleverducci/nextcloudmaps/repository/GeofavoriteRepository.java b/app/src/main/java/it/danieleverducci/nextcloudmaps/repository/GeofavoriteRepository.java index c3e9789..a4939fc 100644 --- a/app/src/main/java/it/danieleverducci/nextcloudmaps/repository/GeofavoriteRepository.java +++ b/app/src/main/java/it/danieleverducci/nextcloudmaps/repository/GeofavoriteRepository.java @@ -1,5 +1,7 @@ package it.danieleverducci.nextcloudmaps.repository; +import android.util.Log; + import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.MutableLiveData; @@ -19,7 +21,8 @@ import retrofit2.Response; public class GeofavoriteRepository { private static GeofavoriteRepository instance; - private ArrayList dataSet = new ArrayList<>(); + private MutableLiveData> mGeofavorites; + private OnFinished listener; public static GeofavoriteRepository getInstance() { if(instance == null){ @@ -29,13 +32,23 @@ public class GeofavoriteRepository { } public MutableLiveData> getGeofavorites(){ + if (mGeofavorites == null) { + mGeofavorites = new MutableLiveData<>(); + mGeofavorites.setValue(new ArrayList<>()); + } + return mGeofavorites; + } + + public void updateGeofavorites() { + if (listener != null) listener.onLoading(); // Obtain geofavorites Call> call = ApiProvider.getAPI().getGeofavorites(); call.enqueue(new Callback>() { @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { if (response.isSuccessful() && response.body() != null) { - dataSet.addAll(response.body()); + mGeofavorites.postValue(response.body()); + if (listener != null) listener.onSuccess(); } else { onFailure(call, new Throwable("Dataset is empty")); } @@ -43,13 +56,83 @@ public class GeofavoriteRepository { @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - // TODO + if (listener != null) listener.onFailure(); } }); + } - MutableLiveData> data = new MutableLiveData<>(); - data.setValue(dataSet); - return data; + public Geofavorite getGeofavorite(int id) { + for (Geofavorite g : mGeofavorites.getValue()) { + if (g.getId() == id) + return g; + } + return null; + } + + public void saveGeofavorite(Geofavorite geofav) { + Call call; + if (geofav.getId() == 0) { + // New geofavorite + call = ApiProvider.getAPI().createGeofavorite(geofav); + } else { + // Update existing geofavorite + call = ApiProvider.getAPI().updateGeofavorite(geofav.getId(), geofav); + } + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + if (response.isSuccessful()) { + List geofavs = mGeofavorites.getValue(); + if (geofav.getId() != 0) { + geofavs.remove(geofav); + } + geofavs.add(geofav); + mGeofavorites.postValue(geofavs); + if (listener != null) listener.onSuccess(); + } else if (listener != null) { + listener.onFailure(); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + if (listener != null) listener.onFailure(); + } + }); + } + + public void deleteGeofavorite(Geofavorite geofav) { + if (listener != null) listener.onLoading(); + // Delete Geofavorite + Call call = ApiProvider.getAPI().deleteGeofavorite(geofav.getId()); + call.enqueue(new Callback() { + @Override + public void onResponse(Call call, Response response) { + List geofavs = mGeofavorites.getValue(); + if (geofavs.remove(geofav)) { + mGeofavorites.postValue(geofavs); + if (listener != null) listener.onSuccess(); + } else { + // Should never happen + if (listener != null) listener.onFailure(); + } + } + + @Override + public void onFailure(Call call, Throwable t) { + if (listener != null) listener.onFailure(); + } + }); + } + + public void setOnFinishedListener(OnFinished listener) { + this.listener = listener; + } + + public interface OnFinished { + void onLoading(); + void onSuccess(); + void onFailure(); } diff --git a/app/src/main/res/layout/activity_geofavorite_detail.xml b/app/src/main/res/layout/activity_geofavorite_detail.xml index ba4be56..85daeb0 100644 --- a/app/src/main/res/layout/activity_geofavorite_detail.xml +++ b/app/src/main/res/layout/activity_geofavorite_detail.xml @@ -206,6 +206,13 @@ android:layout_height="wrap_content" android:textAlignment="textEnd" /> + +