[tor-commits] [snowflake-mobile/master] Moved MainActivity to MVP

cohosh at torproject.org cohosh at torproject.org
Thu Jul 30 14:47:12 UTC 2020


commit e80fd0146dc6ec8e6c2101e5b2f272a3239e0b52
Author: Hashik Donthineni <HashikDonthineni at 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);
+    }
+}





More information about the tor-commits mailing list