commit e80fd0146dc6ec8e6c2101e5b2f272a3239e0b52
Author: Hashik Donthineni <HashikDonthineni(a)gmail.com>
Date: Sat Jul 25 16:31:29 2020 +0530
Moved MainActivity to MVP
---
app/src/main/AndroidManifest.xml | 4 +-
.../org/torproject/snowflake/MainActivity.java | 187 ++++++---------------
.../snowflake/fragments/MainFragment.java | 6 +-
.../snowflake/models/MainActivityModel.java | 164 ++++++++++++++++++
.../presenters/MainActivityPresenter.java | 102 +++++++++++
5 files changed, 322 insertions(+), 141 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 67e8537..2ea8221 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,7 +23,9 @@
android:enabled="true"
android:exported="false" />
- <activity android:name=".MainActivity">
+ <activity
+ android:name=".MainActivity"
+ android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/app/src/main/java/org/torproject/snowflake/MainActivity.java b/app/src/main/java/org/torproject/snowflake/MainActivity.java
index 69948a5..8610b44 100644
--- a/app/src/main/java/org/torproject/snowflake/MainActivity.java
+++ b/app/src/main/java/org/torproject/snowflake/MainActivity.java
@@ -4,12 +4,10 @@ import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
-import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
@@ -20,28 +18,18 @@ import org.torproject.snowflake.constants.FragmentConstants;
import org.torproject.snowflake.fragments.AppSettingsFragment;
import org.torproject.snowflake.fragments.MainFragment;
import org.torproject.snowflake.interfaces.MainFragmentCallback;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-
-import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
-import io.reactivex.rxjava3.core.Single;
-import io.reactivex.rxjava3.disposables.Disposable;
-import io.reactivex.rxjava3.schedulers.Schedulers;
+import org.torproject.snowflake.presenters.MainActivityPresenter;
/**
* MainActivity is the main UI of the application.
*/
-public class MainActivity extends AppCompatActivity implements MainFragmentCallback {
+public class MainActivity extends AppCompatActivity implements MainFragmentCallback, MainActivityPresenter.View {
private static final String TAG = "MainActivity";
- public int servedCount;
int currentFragment;
- private SharedPreferences sharedPreferences;
+ MainActivityPresenter presenter;
private Button settingsButton;
- private Disposable disposable;
- private SharedPreferences.OnSharedPreferenceChangeListener listener;
+ //Indicates if model finished checking the date and reset served count if need be.
+ boolean isCheckDateFinished;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -49,31 +37,23 @@ public class MainActivity extends AppCompatActivity implements MainFragmentCallb
setContentView(R.layout.activity_main);
setSupportActionBar(findViewById(R.id.toolbar));
settingsButton = findViewById(R.id.settings_button);
- sharedPreferences = GlobalApplication.getAppPreferences();
- servedCount = 0;
-
- //Launching another thread to check, reset served date if need be.
- disposable = Single.fromCallable(this::checkServedDate)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe((status) -> { //Runs on main thread
- //By this point the servedCount must be reset or left as is after checking the dates.
- servedCount = sharedPreferences.getInt(getString(R.string.users_served_key), 0);
+ presenter = new MainActivityPresenter(this);
+ isCheckDateFinished = false;
- setListenerForCount();
- updateCountInFragment();
- });
+ //Checks date asynchronously and sets or re-sets it and the users served.
+ // After checking presenter calls the update count.
+ presenter.checkDate();
//Creating notification channel if app is being run for the first time
- if (sharedPreferences.getBoolean(getString(R.string.initial_run_boolean_key), true)) {
+ if (presenter.getInitialRunBoolean()) {
createNotificationChannel();
//Setting initial run to false.
- sharedPreferences.edit().putBoolean(getString(R.string.initial_run_boolean_key), false).apply();
+ presenter.setInitialRunBoolean(false);
}
- settingsButton.setOnClickListener(new View.OnClickListener() {
+ settingsButton.setOnClickListener(new android.view.View.OnClickListener() {
@Override
- public void onClick(View v) {
+ public void onClick(android.view.View v) {
//Starting Settings Activity.
startFragment(AppSettingsFragment.newInstance());
}
@@ -83,40 +63,6 @@ public class MainActivity extends AppCompatActivity implements MainFragmentCallb
startFragment(MainFragment.newInstance());
}
- /**
- * Updates the users served count in the text-view of MainFragment if it's in the foreground or else It'll ignore.
- */
- private void updateCountInFragment() {
- Log.d(TAG, "updateCountInFragment: Updating count");
-
- Fragment mainFragment = getSupportFragmentManager().findFragmentByTag(Integer.toString(FragmentConstants.MAIN_FRAGMENT));
- //If the fragment is in foreground update the count. Or else ignore.
- if (mainFragment != null) {
- ((MainFragment) mainFragment).showServed();
- }
- }
-
- /**
- * Used to update the count without restarting the app to update the users served count.
- * Listener is set on the file to check for changes.
- */
- private void setListenerForCount() {
- Log.d(TAG, "setListenerForCount: Setting listener");
-
- // Do NOT make the variable local. SP listener listens on WeakHashMap.
- // It'll get garbage collected as soon as code leaves the scope. Hence listener won't work.
- listener = (prefs, key) -> {
- Log.d(TAG, "setListenerForCount: Listener: Key = " + key);
-
- if (key.equals(getString(R.string.users_served_key))) {
- servedCount = sharedPreferences.getInt(key, 0);
- updateCountInFragment();
- }
- };
-
- sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
- }
-
/**
* Used to replace the fragment in the "fragment_container"
*
@@ -137,6 +83,11 @@ public class MainActivity extends AppCompatActivity implements MainFragmentCallb
fragment, Integer.toString(currentFragment)).commit();
}
+ @Override
+ public boolean isServiceRunning() {
+ return presenter.isServiceRunning();
+ }
+
/**
* Turn service on/off.
*
@@ -152,23 +103,6 @@ public class MainActivity extends AppCompatActivity implements MainFragmentCallb
}
}
- /**
- * Test to see if the MyPersistentService is running or not.
- *
- * @return boolean whether the service is running or not.
- */
- public boolean isServiceRunning() {
- return sharedPreferences.getBoolean(getString(R.string.is_service_running_bool_key), false);
- }
-
- /**
- * @return Total served users count in the past 24hrs.
- */
- @Override
- public int getServed() {
- //By default 0 is returned until the thread finishes executing checkServedDate function.
- return servedCount;
- }
/**
* Used to create a new notification channel if app is started for the first time on a device.
@@ -189,53 +123,6 @@ public class MainActivity extends AppCompatActivity implements MainFragmentCallb
}
}
- /**
- * Function to check and update the date and users served.
- * Resets served count if the past served date is greater than 24hrs.
- *
- * @return True if the date parsing is done right without errors.
- */
- public boolean checkServedDate() {
- Log.d(TAG, "checkServedDate: ");
- SharedPreferences.Editor editor = sharedPreferences.edit();
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
-
- try {
- String stringCurrentDate = simpleDateFormat.format(Calendar.getInstance().getTime());
- String stringRecordedDate = sharedPreferences.getString(getString(R.string.served_date_key), "");
-
- //No value for key. Set the date value to current date and users served to 0.
- if (stringRecordedDate.equals("")) {
- editor.putString(getString(R.string.served_date_key), stringCurrentDate);
- editor.putInt(getString(R.string.users_served_key), 0);
- } else {
- //Check if the current system date is greater than recorded date, if so reset the "served" flag.
- Date recordedDate = simpleDateFormat.parse(stringRecordedDate);
- Date currentDate = simpleDateFormat.parse(stringCurrentDate);
-
- Log.d(TAG, "checkServedDate: Current Date:" + currentDate.toString() + " Recorded Date:" + recordedDate.toString());
- int comparision = currentDate.compareTo(recordedDate);
-
- if (comparision == 0) {
- //Current date is same as recordedDate no need to reset. Since it's less than 24hrs.
- return true;
- } else {
- //Current date is bigger than recorded date. Reset the values. i.e comparision > 0
- editor.putString(getString(R.string.served_date_key), simpleDateFormat.format(currentDate));
- editor.putInt(getString(R.string.users_served_key), 0);
- }
- }
-
- editor.apply();
-
- } catch (ParseException e) {
- e.printStackTrace();
- Log.e(TAG, "checkServedDate: Invalid Date Parsing");
- return false;
- }
- return true;
- }
-
@Override
public void onBackPressed() {
@@ -248,10 +135,38 @@ public class MainActivity extends AppCompatActivity implements MainFragmentCallb
@Override
protected void onDestroy() {
- //Killing of thread
- disposable.dispose();
- //Unregistering the listener.
- sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
+ //Detach
+ presenter.onDestroy();
super.onDestroy();
}
+
+ @Override
+ protected void onResume() {
+ updateCountInFragment(presenter.getServedCount());
+ super.onResume();
+ }
+
+ /**
+ * Updates the users served count in the text-view of MainFragment if it's in the foreground or else It'll ignore.
+ */
+ @Override
+ public void updateCountInFragment(int servedCount) {
+ Log.d(TAG, "updateCountInFragment: Updating count");
+
+ isCheckDateFinished = true;
+
+ Fragment mainFragment = getSupportFragmentManager().findFragmentByTag(Integer.toString(FragmentConstants.MAIN_FRAGMENT));
+ //If the fragment is in foreground update the count. Or else ignore.
+ if (mainFragment != null) {
+ ((MainFragment) mainFragment).showServed(servedCount);
+ }
+ }
+
+ @Override
+ public int getServed() {
+ if(isCheckDateFinished)
+ return presenter.getServedCount();
+ else
+ return 0;
+ }
}
diff --git a/app/src/main/java/org/torproject/snowflake/fragments/MainFragment.java b/app/src/main/java/org/torproject/snowflake/fragments/MainFragment.java
index d45a216..7615d97 100644
--- a/app/src/main/java/org/torproject/snowflake/fragments/MainFragment.java
+++ b/app/src/main/java/org/torproject/snowflake/fragments/MainFragment.java
@@ -61,8 +61,7 @@ public class MainFragment extends Fragment {
callback.serviceToggle(ForegroundServiceConstants.ACTION_START);
});
- //Calling this in case we return back to this fragment from a different fragment.
- showServed();
+ showServed(callback.getServed());
// Inflate the layout for this fragment
return rootView;
@@ -74,8 +73,7 @@ public class MainFragment extends Fragment {
callback = (MainFragmentCallback) context;
}
- public void showServed() {
- int served = callback.getServed();
+ public void showServed(int served) {
Log.d(TAG, "showServed: " + served);
if (served > 0) {
diff --git a/app/src/main/java/org/torproject/snowflake/models/MainActivityModel.java b/app/src/main/java/org/torproject/snowflake/models/MainActivityModel.java
new file mode 100644
index 0000000..8510b9e
--- /dev/null
+++ b/app/src/main/java/org/torproject/snowflake/models/MainActivityModel.java
@@ -0,0 +1,164 @@
+package org.torproject.snowflake.models;
+
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import org.torproject.snowflake.GlobalApplication;
+import org.torproject.snowflake.presenters.MainActivityPresenter;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Single;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+/**
+ * Model for MainActivity to handle network calls, Shared preferences.
+ */
+public class MainActivityModel {
+ private static final String TAG = "MainActivityModel";
+ private static MainActivityModel instance = null;
+ private SharedPreferences sharedPreferences;
+ private MainActivityPresenter presenter;
+ private int servedCount;
+ private Disposable disposable;
+ private SharedPreferences.OnSharedPreferenceChangeListener listener;
+
+
+ private MainActivityModel(MainActivityPresenter presenter) {
+ sharedPreferences = GlobalApplication.getAppPreferences();
+ this.presenter = presenter;
+ servedCount = 0;
+ }
+
+ public static MainActivityModel getInstance(MainActivityPresenter presenter) {
+ if (instance == null) {
+ synchronized (MainActivityModel.class) {
+ instance = new MainActivityModel(presenter);
+ }
+ }
+ return instance;
+ }
+
+ public int getServedCount(String key) {
+ return sharedPreferences.getInt(key, 0);
+ }
+
+ public boolean getInitialRunBool(String key) {
+ return sharedPreferences.getBoolean(key, true);
+ }
+
+ public void setInitialRunBool(String key, boolean val) {
+ sharedPreferences.edit().putBoolean(key, val).apply();
+ }
+
+ public boolean isServiceRunning(String key) {
+ return sharedPreferences.getBoolean(key, false);
+ }
+
+ /**
+ * Used to update the count without restarting the app to update the users served count.
+ * Listener is set on the file to check for changes.
+ */
+ private void setListenerForCount() {
+ Log.d(TAG, "setListenerForCount: Setting listener");
+
+ // Do NOT make the variable local. SP listener listens on WeakHashMap.
+ // It'll get garbage collected as soon as code leaves the scope. Hence listener won't work.
+ listener = (prefs, key) -> {
+ Log.d(TAG, "setListenerForCount: Listener: Key = " + key);
+
+ if (key.equals("users_served")) {
+ servedCount = sharedPreferences.getInt(key, 0);
+ if (presenter != null)
+ presenter.updateServedCount(servedCount);
+ }
+ };
+
+ sharedPreferences.registerOnSharedPreferenceChangeListener(listener);
+ }
+
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy: ");
+ //Unregistering the listener.
+ sharedPreferences.unregisterOnSharedPreferenceChangeListener(listener);
+ //Disposing off call
+ disposable.dispose();
+ //Detaching presenter
+ presenter = null;
+ }
+
+ public String getDate(String dateKey) {
+ return sharedPreferences.getString(dateKey, "");
+ }
+
+ /**
+ * Setting the users served date and value.
+ */
+ public void setDateAndServed(String dateKey, String valueKey, String date, int value) {
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString(dateKey, date);
+ editor.putInt(valueKey, value);
+ editor.apply();
+ }
+
+ /**
+ * Function to check and update the date and users served.
+ * Resets served count if the past served date is greater than 24hrs.
+ *
+ * @return True if the date parsing is done right without errors.
+ */
+ private boolean checkServedDate() {
+ Log.d(TAG, "checkServedDate: ");
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MMM-yyyy");
+
+ try {
+ String stringCurrentDate = simpleDateFormat.format(Calendar.getInstance().getTime());
+ String stringRecordedDate = presenter.getDate();
+
+ //No value for key. Set the date value to current date and users served to 0.
+ if (stringRecordedDate.equals("")) {
+ setDateAndServed("date", "users_served", stringCurrentDate, 0);
+ } else {
+ //Check if the current system date is greater than recorded date, if so reset the "served" flag.
+ Date recordedDate = simpleDateFormat.parse(stringRecordedDate);
+ Date currentDate = simpleDateFormat.parse(stringCurrentDate);
+
+ Log.d(TAG, "checkServedDate: Current Date:" + currentDate.toString() + " Recorded Date:" + recordedDate.toString());
+ int comparision = currentDate.compareTo(recordedDate);
+
+ if (comparision == 0) {
+ //Current date is same as recordedDate no need to reset. Since it's less than 24hrs.
+ return true;
+ } else {
+ //Current date is bigger than recorded date. Reset the values. i.e comparision > 0
+ setDateAndServed("date", "users_served", simpleDateFormat.format(currentDate), 0);
+ }
+ }
+
+ } catch (ParseException e) {
+ e.printStackTrace();
+ Log.e(TAG, "checkServedDate: Invalid Date Parsing");
+ return false;
+ }
+ return true;
+ }
+
+ public void checkDateAsync() {
+ //Launching another thread to check, reset served date if need be.
+ if (presenter != null) {
+ disposable = Single.fromCallable(this::checkServedDate)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe((status) -> { //Runs on main thread
+ //By this point the servedCount must be reset or left as is after checking the dates.
+ presenter.updateServedCount(getServedCount("users_served"));
+ setListenerForCount();
+ });
+ }
+ }
+}
diff --git a/app/src/main/java/org/torproject/snowflake/presenters/MainActivityPresenter.java b/app/src/main/java/org/torproject/snowflake/presenters/MainActivityPresenter.java
new file mode 100644
index 0000000..7878f56
--- /dev/null
+++ b/app/src/main/java/org/torproject/snowflake/presenters/MainActivityPresenter.java
@@ -0,0 +1,102 @@
+package org.torproject.snowflake.presenters;
+
+import android.util.Log;
+
+import org.torproject.snowflake.MainActivity;
+import org.torproject.snowflake.R;
+import org.torproject.snowflake.models.MainActivityModel;
+
+/**
+ * Presenter for MainActivity.
+ */
+public class MainActivityPresenter {
+ private static final String TAG = "MainActivityPresenter";
+ View view;
+ MainActivityModel model;
+
+ public MainActivityPresenter(View view) {
+ //Attaching
+ this.view = view;
+ model = MainActivityModel.getInstance(this);
+ }
+
+ /**
+ * Cleaning
+ */
+ public void onDestroy() {
+ Log.d(TAG, "onDestroy: ");
+ //Calling on Destroy on model
+ model.onDestroy();
+ //Detaching
+ view = null;
+ }
+
+ public int getServedCount() {
+ Log.d(TAG, "getServedCount: ");
+ if (view != null) {
+ return model.getServedCount(((MainActivity) view).getString(R.string.users_served_key));
+ }
+ return 0;
+ }
+
+ public boolean getInitialRunBoolean() {
+ Log.d(TAG, "getInitialRunBoolean: ");
+ if (view != null) {
+ return model.getInitialRunBool(((MainActivity) view).getString(R.string.initial_run_boolean_key));
+ }
+ return false;
+ }
+
+ /**
+ * Setting the initial run boolean.
+ *
+ * @param val Set to False/True
+ */
+ public void setInitialRunBoolean(boolean val) {
+ Log.d(TAG, "setInitialRunBoolean: ");
+ if (view != null) {
+ model.setInitialRunBool(((MainActivity) view).getString(R.string.initial_run_boolean_key), val);
+ }
+ }
+
+ /**
+ * Test to see if the MyPersistentService is running or not.
+ *
+ * @return boolean whether the service is running or not.
+ */
+ public boolean isServiceRunning() {
+ Log.d(TAG, "isServiceRunning: ");
+ if (view != null) {
+ return model.isServiceRunning(((MainActivity) view).getString(R.string.is_service_running_bool_key));
+ }
+ return true;
+ }
+
+ public void updateServedCount(int count) {
+ Log.d(TAG, "updateServedCount: ");
+ if (view != null) {
+ view.updateCountInFragment(count);
+ }
+ }
+
+ /**
+ * Getting the served date.
+ */
+ public String getDate() {
+ return model.getDate(((MainActivity) view).getString(R.string.served_date_key));
+ }
+
+ public void checkDate() {
+ model.checkDateAsync();
+ }
+
+ public void setListenerForCount() {
+ }
+
+ /**
+ * View for the MainActivity
+ */
+ public interface View {
+ void updateCountInFragment(int i);
+ }
+}