Verified Commit e80fd014 authored by HashikD's avatar HashikD
Browse files

Moved MainActivity to MVP

parent dafed99d
......@@ -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" />
......
......@@ -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;
}
}
......@@ -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) {
......
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();
});
}
}
}
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);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment