Java tutorial
/* * Copyright (C) 2015-2016 Worker Project * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package me.raatiniemi.worker.presentation.settings.view; import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.util.Log; import android.view.MenuItem; import android.view.View; import org.greenrobot.eventbus.EventBus; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import me.raatiniemi.worker.R; import me.raatiniemi.worker.data.service.data.BackupService; import me.raatiniemi.worker.data.service.data.RestoreService; import me.raatiniemi.worker.presentation.settings.model.Backup; import me.raatiniemi.worker.presentation.settings.presenter.SettingsPresenter; import me.raatiniemi.worker.presentation.util.PermissionUtil; import me.raatiniemi.worker.presentation.util.Settings; import me.raatiniemi.worker.presentation.view.activity.MvpActivity; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class SettingsActivity extends MvpActivity<SettingsPresenter> implements SettingsView, ActivityCompat.OnRequestPermissionsResultCallback { /** * Tag for logging. */ private static final String TAG = "SettingsActivity"; /** * Key for the project preference. */ private static final String SETTINGS_PROJECT_KEY = "settings_project"; /** * Key for confirm clock out preference. */ private static final String SETTINGS_PROJECT_CONFIRM_CLOCK_OUT_KEY = "settings_project_confirm_clock_out"; /** * Key for time summary preference. */ private static final String SETTINGS_PROJECT_TIME_SUMMARY_KEY = "settings_project_time_summary"; /** * Key for ongoing notification preference. */ private static final String SETTINGS_PROJECT_ONGOING_NOTIFICATION_ENABLE_KEY = "settings_project_ongoing_notification_enable"; /** * Key for the ongoing notification chronometer preference. */ private static final String SETTINGS_PROJECT_ONGOING_NOTIFICATION_CHRONOMETER_KEY = "settings_project_ongoing_notification_chronometer"; /** * Key for the data preference. */ private static final String SETTINGS_DATA_KEY = "settings_data"; /** * Key for the data backup preference. */ private static final String SETTINGS_DATA_BACKUP_KEY = "settings_data_backup"; /** * Key for the data restore preference. */ private static final String SETTINGS_DATA_RESTORE_KEY = "settings_data_restore"; /** * Code for requesting permission for reading external storage. */ private static final int REQUEST_READ_EXTERNAL_STORAGE = 1; /** * Code for requesting permission for writing to external storage. */ private static final int REQUEST_WRITE_EXTERNAL_STORAGE = 2; public static Intent newIntent(Context context) { return new Intent(context, SettingsActivity.class); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); if (null == savedInstanceState) { getFragmentManager().beginTransaction().replace(R.id.fragment_container, new SettingsFragment()) .commit(); } getPresenter().attachView(this); } @Override protected SettingsPresenter createPresenter() { return new SettingsPresenter(this, EventBus.getDefault()); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // Both of the permission requests require that the `DataFragment` is // available, no ned to go any further if the fragment is not available. DataFragment fragment = getDataFragment(); if (null == fragment) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); return; } switch (requestCode) { case REQUEST_READ_EXTERNAL_STORAGE: // Whether we've been granted read permission or not, a call to // the `checkLatestBackup` will handle both scenarios. fragment.checkLatestBackup(); break; case REQUEST_WRITE_EXTERNAL_STORAGE: // Only if we've been granted write permission should we backup. // We should not display the permission message again unless the // user attempt to backup. if (PermissionUtil.verifyPermissions(grantResults)) { fragment.runBackup(); } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); break; } } /** * Switch the currently displayed preference screen. * * @param key Key for the new preference screen. */ private void switchPreferenceScreen(String key) { Fragment fragment; switch (key) { case SETTINGS_PROJECT_KEY: fragment = new ProjectFragment(); break; case SETTINGS_DATA_KEY: fragment = new DataFragment(); break; default: Log.w(TAG, "Switch to preference screen '" + key + "' is not implemented"); displayPreferenceScreenNotImplementedMessage(); return; } getFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, key).addToBackStack(key) .commit(); } private void displayPreferenceScreenNotImplementedMessage() { View contentView = findViewById(android.R.id.content); if (null == contentView) { return; } Snackbar.make(contentView, R.string.error_message_preference_screen_not_implemented, Snackbar.LENGTH_SHORT) .show(); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Override the toolbar back button to behave as the back button. if (android.R.id.home == item.getItemId()) { onBackPressed(); return true; } return super.onOptionsItemSelected(item); } @Override public void onBackPressed() { FragmentManager fragmentManager = getFragmentManager(); // Depending on which fragment is contained within the // container, the back button will behave differently. Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container); Class<SettingsFragment> settings = SettingsFragment.class; if (!settings.equals(fragment.getClass())) { fragmentManager.popBackStack(); } else { super.onBackPressed(); } } /** * Get preference fragment by tag. * * @param tag Tag for the fragment. * @param <T> Type of the fragment. * @return Preference fragment, or null if unable to retrieve fragment. */ @Nullable @SuppressWarnings("unchecked") private <T extends BasePreferenceFragment> T getPreferenceFragment(String tag) { T fragment = null; try { fragment = (T) getFragmentManager().findFragmentByTag(tag); if (null == fragment) { // Should only be an informational log message since // the activity is working with multiple fragments // and the user can navigate up or down before the // background operations are finished. Log.i(TAG, "Unable to find fragment with tag: " + tag); } } catch (ClassCastException e) { Log.w(TAG, "Unable to cast preference fragment", e); } return fragment; } /** * Get the data fragment. * * @return Data fragment, or null if unable to get fragment. */ @Nullable private DataFragment getDataFragment() { return getPreferenceFragment(SETTINGS_DATA_KEY); } @Override public void setLatestBackup(@Nullable Backup backup) { DataFragment fragment = getDataFragment(); if (null == fragment) { Log.d(TAG, "DataFragment is not available"); return; } fragment.setBackupSummary(backup); fragment.setRestoreSummary(backup); } @Override public void showChangeTimeSummaryStartingPointToWeekSuccessMessage() { View contentView = findViewById(android.R.id.content); if (null == contentView) { return; } Snackbar.make(contentView, R.string.message_change_time_summary_starting_point_week, Snackbar.LENGTH_LONG) .show(); } @Override public void showChangeTimeSummaryStartingPointToMonthSuccessMessage() { View contentView = findViewById(android.R.id.content); if (null == contentView) { return; } Snackbar.make(contentView, R.string.message_change_time_summary_starting_point_month, Snackbar.LENGTH_LONG) .show(); } @Override public void showChangeTimeSummaryStartingPointErrorMessage() { View contentView = findViewById(android.R.id.content); if (null == contentView) { return; } Snackbar.make(contentView, R.string.error_message_change_time_summary_starting_point, Snackbar.LENGTH_LONG) .show(); } public abstract static class BasePreferenceFragment extends PreferenceFragment { @Override public void onResume() { super.onResume(); // Set the title for the preference fragment. getActivity().setTitle(getTitle()); } SettingsActivity getSettingsActivity() { return (SettingsActivity) getActivity(); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, @NonNull Preference preference) { super.onPreferenceTreeClick(preferenceScreen, preference); if (preference instanceof PreferenceScreen) { getSettingsActivity().switchPreferenceScreen(preference.getKey()); } else { Log.d(TAG, "Preference '" + preference.getTitle() + "' is not implemented"); Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.error_message_preference_not_implemented, Snackbar.LENGTH_SHORT).show(); } return false; } /** * Get the resource id for the preference fragment title. * * @return Resource id for the preference fragment title. */ public abstract int getTitle(); } public static class SettingsFragment extends BasePreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings); } @Override public int getTitle() { return R.string.activity_settings_title; } } public static class ProjectFragment extends BasePreferenceFragment implements Preference.OnPreferenceChangeListener { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings_project); try { // Set the preference value for the clock out confirmation. CheckBoxPreference confirmClockOut = (CheckBoxPreference) findPreference( SETTINGS_PROJECT_CONFIRM_CLOCK_OUT_KEY); confirmClockOut.setChecked(Settings.shouldConfirmClockOut(getActivity())); } catch (ClassCastException e) { Log.w(TAG, "Unable to get value for 'confirm_clock_out'", e); } try { int startingPointForTimeSummary = Settings.getStartingPointForTimeSummary(getActivity()); ListPreference timeSummary = (ListPreference) findPreference(SETTINGS_PROJECT_TIME_SUMMARY_KEY); timeSummary.setValue(String.valueOf(startingPointForTimeSummary)); timeSummary.setOnPreferenceChangeListener(this); } catch (ClassCastException e) { Log.w(TAG, "Unable to set listener for 'time_summary'", e); } try { CheckBoxPreference ongoingNotification = (CheckBoxPreference) findPreference( SETTINGS_PROJECT_ONGOING_NOTIFICATION_ENABLE_KEY); ongoingNotification.setChecked(Settings.isOngoingNotificationEnabled(getActivity())); } catch (ClassCastException e) { Log.w(TAG, "Unable to get value for 'ongoing_notification'", e); } try { CheckBoxPreference ongoingNotificationChronometer = (CheckBoxPreference) findPreference( SETTINGS_PROJECT_ONGOING_NOTIFICATION_CHRONOMETER_KEY); ongoingNotificationChronometer .setChecked(Settings.isOngoingNotificationChronometerEnabled(getActivity())); } catch (ClassCastException e) { Log.w(TAG, "Unable to get value for 'ongoing_notification_chronometer'", e); } } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, @NonNull Preference preference) { if (SETTINGS_PROJECT_CONFIRM_CLOCK_OUT_KEY.equals(preference.getKey())) { try { // Set the clock out confirmation preference. boolean checked = ((CheckBoxPreference) preference).isChecked(); Settings.setConfirmClockOut(getActivity(), checked); return true; } catch (ClassCastException e) { Log.w(TAG, "Unable to set value for 'confirm_clock_out'", e); } } if (SETTINGS_PROJECT_TIME_SUMMARY_KEY.equals(preference.getKey())) { return true; } if (SETTINGS_PROJECT_ONGOING_NOTIFICATION_ENABLE_KEY.equals(preference.getKey())) { try { // Set the clock out confirmation preference. boolean checked = ((CheckBoxPreference) preference).isChecked(); if (checked) { Settings.enableOngoingNotification(getActivity()); return true; } Settings.disableOngoingNotification(getActivity()); return true; } catch (ClassCastException e) { Log.w(TAG, "Unable to set value for 'ongoing_notification'", e); } } if (SETTINGS_PROJECT_ONGOING_NOTIFICATION_CHRONOMETER_KEY.equals(preference.getKey())) { try { // Set the clock out confirmation preference. boolean checked = ((CheckBoxPreference) preference).isChecked(); if (checked) { Settings.enableOngoingNotificationChronometer(getActivity()); return true; } Settings.disableOngoingNotificationChronometer(getActivity()); return true; } catch (ClassCastException e) { Log.w(TAG, "Unable to set value for 'ongoing_notification_chronometer'", e); } } return super.onPreferenceTreeClick(preferenceScreen, preference); } @Override public int getTitle() { return R.string.activity_settings_project; } @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (SETTINGS_PROJECT_TIME_SUMMARY_KEY.equals(preference.getKey())) { changeTimeSummaryStartingPoint(newValue); return true; } return false; } private void changeTimeSummaryStartingPoint(Object newStartingPoint) { int startingPoint = Integer.parseInt((String) newStartingPoint); getSettingsActivity().getPresenter().changeTimeSummaryStartingPoint(startingPoint); } } public static class DataFragment extends BasePreferenceFragment { private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.settings_data); // Check for the latest backup. checkLatestBackup(); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, @NonNull Preference preference) { // Check if we support the user action, if not, send it to the // parent which will handle it. switch (preference.getKey()) { case SETTINGS_DATA_BACKUP_KEY: runBackup(); break; case SETTINGS_DATA_RESTORE_KEY: runRestore(); break; default: return super.onPreferenceTreeClick(preferenceScreen, preference); } return false; } @Override public int getTitle() { return R.string.activity_settings_data; } /** * Initiate the backup action. */ private void runBackup() { // We should only attempt to backup if permission to write // to the external storage have been granted. if (PermissionUtil.havePermission(getActivity(), WRITE_EXTERNAL_STORAGE)) { Log.d(TAG, "Permission for writing to external storage is granted"); Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.message_backing_up_data, Snackbar.LENGTH_SHORT).show(); BackupService.startBackup(getActivity()); return; } // We have not been granted permission to write to the external storage. Display // the permission message and allow the user to initiate the permission request. Log.d(TAG, "Permission for writing to external storage is not granted"); Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.message_permission_write_backup, Snackbar.LENGTH_INDEFINITE) .setAction(android.R.string.ok, new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(getActivity(), new String[] { WRITE_EXTERNAL_STORAGE }, REQUEST_WRITE_EXTERNAL_STORAGE); } }).show(); } /** * Initiate the restore action. */ private void runRestore() { new AlertDialog.Builder(getActivity()).setTitle(R.string.activity_settings_restore_confirm_title) .setMessage(R.string.activity_settings_restore_confirm_message) .setPositiveButton(android.R.string.yes, (dialog, which) -> { Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.message_restoring_data, Snackbar.LENGTH_SHORT).show(); RestoreService.startRestore(getActivity()); }).setNegativeButton(android.R.string.no, null).show(); } /** * Get the latest backup, if permission have been granted. */ private void checkLatestBackup() { // We should only attempt to check the latest backup if permission // to read the external storage have been granted. if (PermissionUtil.havePermission(getActivity(), READ_EXTERNAL_STORAGE)) { // Tell the SettingsActivity to fetch the latest backup. Log.d(TAG, "Permission for reading external storage is granted"); getSettingsActivity().getPresenter().getLatestBackup(); // No need to go any further. return; } // We have not been granted permission to read the external storage. Display the // permission message and allow the user to initiate the permission request. Log.d(TAG, "Permission for reading external storage is not granted"); Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.message_permission_read_backup, Snackbar.LENGTH_INDEFINITE).setAction(android.R.string.ok, new View.OnClickListener() { @Override public void onClick(View v) { ActivityCompat.requestPermissions(getActivity(), new String[] { READ_EXTERNAL_STORAGE }, REQUEST_READ_EXTERNAL_STORAGE); } }).show(); } /** * Set the backup summary based on the latest backup. * * @param backup Latest available backup. */ void setBackupSummary(@Nullable Backup backup) { Preference preference = findPreference(SETTINGS_DATA_BACKUP_KEY); if (null == preference) { Log.w(TAG, "Unable to find preference with key: " + SETTINGS_DATA_BACKUP_KEY); return; } String text = getString(R.string.activity_settings_backup_unable_to_find); if (null != backup) { text = getString(R.string.activity_settings_backup_none_available); Date date = backup.getDate(); if (null != date) { text = getString(R.string.activity_settings_backup_performed_at, format.format(date)); } } preference.setSummary(text); } /** * Set the restore summary based on the latest backup. * * @param backup Latest available backup. */ void setRestoreSummary(@Nullable Backup backup) { Preference preference = findPreference(SETTINGS_DATA_RESTORE_KEY); if (null == preference) { Log.w(TAG, "Unable to find preference with key: " + SETTINGS_DATA_RESTORE_KEY); return; } String text = getString(R.string.activity_settings_restore_unable_to_find); boolean enable = false; if (null != backup) { text = getString(R.string.activity_settings_restore_none_available); Date date = backup.getDate(); if (null != date) { text = getString(R.string.activity_settings_restore_from, format.format(date)); enable = true; } } preference.setSummary(text); preference.setEnabled(enable); } } }