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