From 9a2a62291471cf428e930cb48d0fb2e8a3e301ec Mon Sep 17 00:00:00 2001 From: WrichikBasu <56736644+WrichikBasu@users.noreply.github.com> Date: Sun, 1 Nov 2020 21:10:56 +0530 Subject: [PATCH 1/2] Implemented ViewModel for RingtonePicker - A ViewModel has been implemented for Activity_RintonePicker and misuse of static variables has been rectified. - Service_RingAlarm and Service_SnoozeAlarm now work in the same process as the main app. This will help in sharing data. ConstantsAndStatics#killServices has been modified accordingly. --- app/src/main/AndroidManifest.xml | 8 +- .../shakealarmclock/Activity_AlarmsList.java | 13 +- .../Activity_IntentManager.java | 19 +- .../Activity_RingtonePicker.java | 167 +++++++++--------- .../shakealarmclock/Activity_Settings.java | 21 +-- .../shakealarmclock/ConstantsAndStatics.java | 120 ++++++------- .../Fragment_AlarmDetails_Main.java | 10 +- .../shakealarmclock/Service_RingAlarm.java | 29 ++- .../shakealarmclock/Service_SnoozeAlarm.java | 34 ++-- .../ViewModel_RingtonePicker.java | 115 ++++++++++++ .../layout/activity_settings_scrollview.xml | 6 +- app/src/main/res/values/strings.xml | 3 + 12 files changed, 312 insertions(+), 233 deletions(-) create mode 100644 app/src/main/java/in/basulabs/shakealarmclock/ViewModel_RingtonePicker.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 01dcba4..324432c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,8 @@ + + + android:exported="false"/> + android:exported="false"/> - * This activity has no display and is transparent. It handles incoming intents and finishes itself in {@link - * #onCreate(Bundle)}. + * This activity has no display and is transparent. It handles incoming intents and finishes itself in {@link #onCreate(Bundle)}. *

* The following activity actions are handled: *

    @@ -129,10 +128,18 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { case AlarmClock.ACTION_DISMISS_ALARM: - Intent intent2 = new Intent(this, Activity_AlarmsList.class); - intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent2); + if (Service_RingAlarm.isThisServiceRunning && Service_RingAlarm.alarmID != - 1) { + Intent intent1 = new Intent(this, Service_RingAlarm.class); + stopService(intent1); + } else if (Service_SnoozeAlarm.isThisServiceRunning && Service_SnoozeAlarm.alarmID != - 1) { + Intent intent1 = new Intent(this, Service_SnoozeAlarm.class); + stopService(intent1); + } else { + Intent intent2 = new Intent(this, Activity_AlarmsList.class) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent2); + } break; diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Activity_RingtonePicker.java b/app/src/main/java/in/basulabs/shakealarmclock/Activity_RingtonePicker.java index 31046e4..9bca3fa 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Activity_RingtonePicker.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Activity_RingtonePicker.java @@ -30,8 +30,8 @@ import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; -import java.util.ArrayList; import java.util.Objects; import static android.media.RingtoneManager.ACTION_RINGTONE_PICKER; @@ -47,15 +47,8 @@ import static android.media.RingtoneManager.TYPE_ALL; import static android.media.RingtoneManager.URI_COLUMN_INDEX; -public class Activity_RingtonePicker extends AppCompatActivity implements View.OnClickListener, - AlertDialog_PermissionReason.DialogListener { +public class Activity_RingtonePicker extends AppCompatActivity implements View.OnClickListener, AlertDialog_PermissionReason.DialogListener { - private static Uri defaultUri, existingUri, pickedUri; - private static boolean showDefault, showSilent, wasExistingUriGiven, playTone; - private static CharSequence title; - private static ArrayList toneUriList; - private static ArrayList toneNameList; - private static ArrayList toneIdList; private AudioAttributes audioAttributes; private Bundle savedInstanceState; @@ -64,13 +57,14 @@ public class Activity_RingtonePicker extends AppCompatActivity implements View.O private RadioGroup radioGroup; - private static final int DEFAULT_RADIO_BTN_ID = View.generateViewId(), SILENT_RADIO_BTN_ID = View - .generateViewId(); + private static final int DEFAULT_RADIO_BTN_ID = View.generateViewId(), SILENT_RADIO_BTN_ID = View.generateViewId(); private static final int FILE_REQUEST_CODE = 4937, PERMISSIONS_REQUEST_CODE = 3720; private SharedPreferences sharedPreferences; + private ViewModel_RingtonePicker viewModel; + //---------------------------------------------------------------------------------------------------- @Override @@ -78,6 +72,8 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ringtonepicker); + viewModel = new ViewModelProvider(this).get(ViewModel_RingtonePicker.class); + this.savedInstanceState = savedInstanceState; setSupportActionBar(findViewById(R.id.toolbar4)); @@ -102,10 +98,6 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { Cursor allTonesCursor; - toneNameList = new ArrayList<>(); - toneUriList = new ArrayList<>(); - toneIdList = new ArrayList<>(); - if (Objects.equals(getIntent().getAction(), ACTION_RINGTONE_PICKER)) { int type; @@ -123,62 +115,62 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { int id = allTonesCursor.getInt(ID_COLUMN_INDEX); String uri = allTonesCursor.getString(URI_COLUMN_INDEX); - toneUriList.add(Uri.parse(uri + "/" + id)); - toneNameList.add(allTonesCursor.getString(TITLE_COLUMN_INDEX)); - toneIdList.add(View.generateViewId()); + viewModel.getToneUriList().add(Uri.parse(uri + "/" + id)); + viewModel.getToneNameList().add(allTonesCursor.getString(TITLE_COLUMN_INDEX)); + viewModel.getToneIdList().add(View.generateViewId()); } while (allTonesCursor.moveToNext()); } }); thread.start(); if (intent.hasExtra(EXTRA_RINGTONE_SHOW_DEFAULT)) { - showDefault = Objects.requireNonNull(intent.getExtras()).getBoolean(EXTRA_RINGTONE_SHOW_DEFAULT); + viewModel.setShowDefault(Objects.requireNonNull(intent.getExtras()).getBoolean(EXTRA_RINGTONE_SHOW_DEFAULT)); } else { - showDefault = true; + viewModel.setShowDefault(true); } if (intent.hasExtra(EXTRA_RINGTONE_SHOW_SILENT)) { - showSilent = Objects.requireNonNull(intent.getExtras()).getBoolean(EXTRA_RINGTONE_SHOW_SILENT); + viewModel.setShowSilent(Objects.requireNonNull(intent.getExtras()).getBoolean(EXTRA_RINGTONE_SHOW_SILENT)); } else { - showSilent = false; + viewModel.setShowSilent(false); } - if (showDefault) { + if (viewModel.getShowDefault()) { if (intent.hasExtra(EXTRA_RINGTONE_DEFAULT_URI)) { - defaultUri = Objects.requireNonNull(intent.getExtras()).getParcelable(EXTRA_RINGTONE_DEFAULT_URI); + viewModel.setDefaultUri(Objects.requireNonNull(intent.getExtras()).getParcelable(EXTRA_RINGTONE_DEFAULT_URI)); } else { if (type == RingtoneManager.TYPE_ALARM) { - defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI; + viewModel.setDefaultUri(Settings.System.DEFAULT_ALARM_ALERT_URI); } else if (type == RingtoneManager.TYPE_NOTIFICATION) { - defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI; + viewModel.setDefaultUri(Settings.System.DEFAULT_NOTIFICATION_URI); } else if (type == RingtoneManager.TYPE_RINGTONE) { - defaultUri = Settings.System.DEFAULT_RINGTONE_URI; + viewModel.setDefaultUri(Settings.System.DEFAULT_RINGTONE_URI); } else { - defaultUri = RingtoneManager.getActualDefaultRingtoneUri(this, type); + viewModel.setDefaultUri(RingtoneManager.getActualDefaultRingtoneUri(this, type)); } } } else { - defaultUri = null; + viewModel.setDefaultUri(null); } if (intent.hasExtra(EXTRA_RINGTONE_EXISTING_URI)) { - existingUri = Objects.requireNonNull(intent.getExtras()).getParcelable(EXTRA_RINGTONE_EXISTING_URI); - wasExistingUriGiven = true; + viewModel.setExistingUri(Objects.requireNonNull(intent.getExtras()).getParcelable(EXTRA_RINGTONE_EXISTING_URI)); + viewModel.setWasExistingUriGiven(true); } else { - existingUri = null; - wasExistingUriGiven = false; + viewModel.setExistingUri(null); + viewModel.setWasExistingUriGiven(false); } if (intent.hasExtra(EXTRA_RINGTONE_TITLE)) { - title = (CharSequence) Objects.requireNonNull(intent.getExtras()).get(EXTRA_RINGTONE_TITLE); + viewModel.setTitle((CharSequence) Objects.requireNonNull(intent.getExtras()).get(EXTRA_RINGTONE_TITLE)); } else { - title = "Select tone:"; + viewModel.setTitle("Select tone:"); } if (intent.hasExtra(ConstantsAndStatics.EXTRA_PLAY_RINGTONE)) { - playTone = Objects.requireNonNull(intent.getExtras()).getBoolean(ConstantsAndStatics.EXTRA_PLAY_RINGTONE); + viewModel.setPlayTone(Objects.requireNonNull(intent.getExtras()).getBoolean(ConstantsAndStatics.EXTRA_PLAY_RINGTONE)); } else { - playTone = true; + viewModel.setPlayTone(true); } try { @@ -201,9 +193,9 @@ public boolean onCreateOptionsMenu(Menu menu) { // Get the action view used in your playTone item MenuItem toggleservice = menu.findItem(R.id.playTone); SwitchCompat actionView = (SwitchCompat) toggleservice.getActionView(); - actionView.setChecked(playTone); + actionView.setChecked(viewModel.getPlayTone()); actionView.setOnCheckedChangeListener((buttonView, isChecked) -> { - playTone = isChecked; + viewModel.setPlayTone(isChecked); if (! isChecked) { try { mediaPlayer.stop(); @@ -230,7 +222,9 @@ protected void onResume() { protected void onPause() { super.onPause(); try { - mediaPlayer.stop(); + if (mediaPlayer.isPlaying()) { + mediaPlayer.stop(); + } } catch (Exception ignored) { } } @@ -242,46 +236,46 @@ protected void onPause() { */ private void populateRadioGroup() { - Objects.requireNonNull(getSupportActionBar()).setTitle(title); + Objects.requireNonNull(getSupportActionBar()).setTitle(viewModel.getTitle()); - if (showDefault) { + if (viewModel.getShowDefault()) { createOneRadioButton(DEFAULT_RADIO_BTN_ID, getResources().getString(R.string.defaultTone)); } - if (showSilent) { + if (viewModel.getShowSilent()) { createOneRadioButton(SILENT_RADIO_BTN_ID, getResources().getString(R.string.silentTone)); } - for (int i = 0; i < toneIdList.size(); i++) { - createOneRadioButton(toneIdList.get(i), toneNameList.get(i)); + for (int i = 0; i < viewModel.getToneIdList().size(); i++) { + createOneRadioButton(viewModel.getToneIdList().get(i), viewModel.getToneNameList().get(i)); } - if (existingUri != null) { + if (viewModel.getExistingUri() != null) { //////////////////////////////////////////////////////////////////// // As existingUri is not null, we are required to pre-select // a specific RadioButton. /////////////////////////////////////////////////////////////////// - if (showDefault && existingUri.equals(defaultUri)) { + if (viewModel.getShowDefault() && viewModel.getExistingUri().equals(viewModel.getDefaultUri())) { /////////////////////////////////////////////////////////////////////////// // The existingUri is same as defaultUri, and showDefault is true. // So, we check the "Default" RadioButton. ////////////////////////////////////////////////////////////////////////// ((RadioButton) findViewById(DEFAULT_RADIO_BTN_ID)).setChecked(true); - setPickedUri(defaultUri); + setPickedUri(viewModel.getDefaultUri()); } else { // Find index of existingUri in toneUriList - int index = toneUriList.indexOf(existingUri); + int index = viewModel.getToneUriList().indexOf(viewModel.getExistingUri()); if (index != - 1) { // toneUriList has existingUri. Check the corresponding RadioButton. - ((RadioButton) findViewById(toneIdList.get(index))).setChecked(true); - setPickedUri(existingUri); + ((RadioButton) findViewById(viewModel.getToneIdList().get(index))).setChecked(true); + setPickedUri(viewModel.getExistingUri()); } else { /////////////////////////////////////////////////////////////////////// @@ -291,7 +285,7 @@ private void populateRadioGroup() { // If the file does not exist, we do not select any Radiogroup. /////////////////////////////////////////////////////////////////////// try (Cursor cursor = getContentResolver() - .query(existingUri, null, null, null, null)) { + .query(viewModel.getExistingUri(), null, null, null, null)) { if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) { // existingUri is a valid Uri. @@ -306,15 +300,15 @@ private void populateRadioGroup() { int toneId = View.generateViewId(); - toneNameList.add(fileNameWithExt); - toneUriList.add(existingUri); - toneIdList.add(toneId); + viewModel.getToneNameList().add(fileNameWithExt); + viewModel.getToneUriList().add(viewModel.getExistingUri()); + viewModel.getToneIdList().add(toneId); createOneRadioButton(toneId, fileNameWithExt); ((RadioButton) findViewById(toneId)).setChecked(true); - setPickedUri(existingUri); + setPickedUri(viewModel.getExistingUri()); } } @@ -322,13 +316,13 @@ private void populateRadioGroup() { } } else { - if (wasExistingUriGiven) { + if (viewModel.getWasExistingUriGiven()) { ////////////////////////////////////////////////////////////////////////// // existingUri was specifically passed as a null value. If showSilent // is true, we pre-select the "Silent" RadioButton. Otherwise // we do not select any specific RadioButton. ///////////////////////////////////////////////////////////////////////// - if (showSilent) { + if (viewModel.getShowSilent()) { ((RadioButton) findViewById(SILENT_RADIO_BTN_ID)).setChecked(true); } } @@ -340,7 +334,7 @@ private void populateRadioGroup() { private void setPickedUri(@Nullable Uri newUri) { if (savedInstanceState == null) { - pickedUri = newUri; + viewModel.setPickedUri(newUri); } } @@ -353,8 +347,7 @@ private void setPickedUri(@Nullable Uri newUri) { * @param text The text to be set in the {@link RadioButton}. */ private void createOneRadioButton(int id, String text) { - RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT); + RadioGroup.LayoutParams params = new RadioGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.setMargins(5, 24, 5, 24); RadioButton radioButton = new RadioButton(this); @@ -383,14 +376,14 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { @Override public void onClick(View view) { if (view.getId() == DEFAULT_RADIO_BTN_ID) { - pickedUri = defaultUri; + viewModel.setPickedUri(viewModel.getDefaultUri()); playChosenTone(); } else if (view.getId() == SILENT_RADIO_BTN_ID) { - pickedUri = null; + viewModel.setPickedUri(null); } else if (view.getId() == R.id.chooseCustomToneConstarintLayout) { openFileBrowser(); } else { - pickedUri = toneUriList.get(toneIdList.indexOf(view.getId())); + viewModel.setPickedUri(viewModel.getToneUriList().get(viewModel.getToneIdList().indexOf(view.getId()))); playChosenTone(); } } @@ -400,15 +393,15 @@ public void onClick(View view) { @Override public void onBackPressed() { - if (pickedUri == null) { - if (showSilent) { - Intent intent = new Intent().putExtra(EXTRA_RINGTONE_PICKED_URI, pickedUri); + if (viewModel.getPickedUri() == null) { + if (viewModel.getShowSilent()) { + Intent intent = new Intent().putExtra(EXTRA_RINGTONE_PICKED_URI, viewModel.getPickedUri()); setResult(RESULT_OK, intent); } else { setResult(RESULT_CANCELED); } } else { - Intent intent = new Intent().putExtra(EXTRA_RINGTONE_PICKED_URI, pickedUri); + Intent intent = new Intent().putExtra(EXTRA_RINGTONE_PICKED_URI, viewModel.getPickedUri()); setResult(RESULT_OK, intent); } finish(); @@ -465,8 +458,7 @@ private void checkAndRequestPermission() { .putBoolean(ConstantsAndStatics.SHARED_PREF_KEY_PERMISSION_WAS_ASKED_BEFORE, true) .commit(); - ActivityCompat.requestPermissions(this, - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_CODE); + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_CODE); } else { //////////////////////////////////////////////////////////////////////////////// @@ -484,9 +476,8 @@ private void checkAndRequestPermission() { * Shows an {@code AlertDialog} explaining why the permission is necessary. */ private void showPermissionExplanationDialog() { - DialogFragment dialogPermissionReason = - new AlertDialog_PermissionReason( - getResources().getString(R.string.permissionReasonExp_ringtonePicker)); + DialogFragment dialogPermissionReason = new AlertDialog_PermissionReason( + getResources().getString(R.string.permissionReasonExp_ringtonePicker)); dialogPermissionReason.setCancelable(false); dialogPermissionReason.show(getSupportFragmentManager(), ""); } @@ -494,8 +485,7 @@ private void showPermissionExplanationDialog() { //-------------------------------------------------------------------------------------------------- @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSIONS_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show(); @@ -520,10 +510,10 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten if (cursor != null) { - if (toneUriList.contains(toneUri)) { + if (viewModel.getToneUriList().contains(toneUri)) { - int index = toneUriList.indexOf(toneUri); - ((RadioButton) findViewById(toneIdList.get(index))).setChecked(true); + int index = viewModel.getToneUriList().indexOf(toneUri); + ((RadioButton) findViewById(viewModel.getToneIdList().get(index))).setChecked(true); } else { @@ -534,15 +524,15 @@ protected void onActivityResult(int requestCode, int resultCode, @Nullable Inten String fileNameWithoutExt = fileNameWithExt.substring(0, fileNameWithExt.indexOf(".")); int toneId = View.generateViewId(); - toneNameList.add(fileNameWithoutExt); - toneUriList.add(toneUri); - toneIdList.add(toneId); + viewModel.getToneNameList().add(fileNameWithoutExt); + viewModel.getToneUriList().add(toneUri); + viewModel.getToneIdList().add(toneId); createOneRadioButton(toneId, fileNameWithoutExt); ((RadioButton) findViewById(toneId)).setChecked(true); } - pickedUri = toneUri; + viewModel.setPickedUri(toneUri); playChosenTone(); } } @@ -570,8 +560,7 @@ public void onDialogPositiveClick(DialogFragment dialogFragment) { startActivity(intent); } else { - ActivityCompat.requestPermissions(this, - new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_CODE); + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_CODE); } } } @@ -589,19 +578,21 @@ public void onDialogNegativeClick(DialogFragment dialogFragment) { //---------------------------------------------------------------------------------------------------- private void playChosenTone() { - if (pickedUri != null && playTone) { + if (viewModel.getPickedUri() != null && viewModel.getPlayTone()) { try { mediaPlayer.reset(); - mediaPlayer.setDataSource(this, pickedUri); + mediaPlayer.setDataSource(this, viewModel.getPickedUri()); mediaPlayer.setLooping(false); mediaPlayer.setAudioAttributes(audioAttributes); - mediaPlayer.prepare(); - mediaPlayer.start(); + mediaPlayer.prepareAsync(); + mediaPlayer.setOnPreparedListener(MediaPlayer::start); } catch (Exception ignored) { } } } + //---------------------------------------------------------------------------------------------------- + @Override protected void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Activity_Settings.java b/app/src/main/java/in/basulabs/shakealarmclock/Activity_Settings.java index 25aeb80..ca65d71 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Activity_Settings.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Activity_Settings.java @@ -49,8 +49,7 @@ public class Activity_Settings extends AppCompatActivity implements AdapterView. private SwitchCompat snoozeStateSwitch; private CheckBox autoSelectToneCheckBox; - private static final String SAVE_INSTANCE_KEY_LAYOUT_EXPANDED = - "in.basulabs.shakealarmclock.Activity_Settings.LAYOUT_EXPANDED"; + private static final String SAVE_INSTANCE_KEY_LAYOUT_EXPANDED = "in.basulabs.shakealarmclock.Activity_Settings.LAYOUT_EXPANDED"; private int defaultTheme; @@ -68,7 +67,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { //////////////////////////////////// setSupportActionBar(findViewById(R.id.toolbar5)); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle("Settings"); + getSupportActionBar().setTitle(R.string.settings_title); ////////////////////////////////////////////////// // Get SharedPreferences and its editor: @@ -134,7 +133,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { /////////////////////////////////////////////////////////// // Initialise expandable layout for snooze options: ////////////////////////////////////////////////////////// - snoozeOptionsExpandableLayout = findViewById(R.id.expandable_layout); + snoozeOptionsExpandableLayout = findViewById(R.id.snoozeOptionsExpandableLayout); if (savedInstanceState == null) { snoozeOptionsExpandableLayout.setExpanded(false); } else { @@ -246,8 +245,7 @@ public void afterTextChanged(Editable editable) { // Auto select tone enable/disable: ////////////////////////////////////////// autoSelectToneCheckBox = findViewById(R.id.autoToneSelectionCheckBox); - autoSelectToneCheckBox.setChecked( - sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_AUTO_SET_TONE, true)); + autoSelectToneCheckBox.setChecked(sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_AUTO_SET_TONE, true)); autoSelectToneCheckBox.setOnCheckedChangeListener(this); } @@ -287,10 +285,9 @@ private void setToneTextView(@NonNull Uri uri) { if (uri.equals(Settings.System.DEFAULT_ALARM_ALERT_URI)) { toneTextView.setText(R.string.defaultAlarmToneText); - } else { - String fileNameWithExt = null; + String fileName = null; try (Cursor cursor = getContentResolver().query(uri, null, null, null, null)) { @@ -299,17 +296,17 @@ private void setToneTextView(@NonNull Uri uri) { int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (index != - 1) { - fileNameWithExt = cursor.getString(index); + fileName = cursor.getString(index); } else { - fileNameWithExt = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); + fileName = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); } } } catch (Exception ignored) { } } - if (fileNameWithExt != null) { - toneTextView.setText(fileNameWithExt); + if (fileName != null) { + toneTextView.setText(fileName); } else { toneTextView.setText(uri.getLastPathSegment()); } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java b/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java index a29b13a..a719155 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java @@ -2,7 +2,6 @@ import android.content.Context; import android.content.Intent; -import android.os.Process; import android.util.Log; import androidx.annotation.Nullable; @@ -27,8 +26,8 @@ final class ConstantsAndStatics { /** - * Bundle key for the Bundle that is passed with intent from {@link Activity_AlarmDetails} to {@link - * Activity_AlarmsList} containing the data set by the user. + * Bundle key for the Bundle that is passed with intent from {@link Activity_AlarmDetails} to {@link Activity_AlarmsList} containing the data set by the + * user. */ static final String BUNDLE_KEY_ALARM_DETAILS = "in.basulabs.shakealarmclock.BundleWithAlarmDetails"; @@ -43,8 +42,8 @@ final class ConstantsAndStatics { static final String BUNDLE_KEY_ALARM_MINUTE = "in.basulabs.shakealarmclock.MinsPickedByUser"; /** - * Bundle key for the alarm type. The value is one of {@link #ALARM_TYPE_SOUND_ONLY}, {@link - * #ALARM_TYPE_VIBRATE_ONLY} or {@link #ALARM_TYPE_SOUND_AND_VIBRATE}. + * Bundle key for the alarm type. The value is one of {@link #ALARM_TYPE_SOUND_ONLY}, {@link #ALARM_TYPE_VIBRATE_ONLY} or {@link + * #ALARM_TYPE_SOUND_AND_VIBRATE}. */ static final String BUNDLE_KEY_ALARM_TYPE = "in.basulabs.shakealarmclock.AlarmType"; @@ -129,14 +128,12 @@ final class ConstantsAndStatics { static final String BUNDLE_KEY_HAS_USER_CHOSEN_DATE = "in.basulabs.shakealarmclock.HAS_USER_CHOSEN_DATE"; /** - * Intent action delivered to {@link android.content.BroadcastReceiver} in {@link Service_RingAlarm} instructing it - * to snooze the alarm. + * Intent action delivered to {@link android.content.BroadcastReceiver} in {@link Service_RingAlarm} instructing it to snooze the alarm. */ static final String ACTION_SNOOZE_ALARM = "in.basulabs.shakealarmclock.Service_RingAlarm -- SNOOZE_ALARM"; /** - * Intent action delivered to {@link android.content.BroadcastReceiver} in {@link Service_RingAlarm} instructing it - * to cancel the alarm. + * Intent action delivered to {@link android.content.BroadcastReceiver} in {@link Service_RingAlarm} instructing it to cancel the alarm. */ static final String ACTION_CANCEL_ALARM = "in.basulabs.shakealarmclock.Service_RingAlarm -- CANCEL_ALARM"; @@ -155,23 +152,19 @@ final class ConstantsAndStatics { */ static final String ACTION_EXISTING_ALARM = "in.basulabs.shakealarmclock.ACTION_EXISTING_ALARM"; - static final String ACTION_NEW_ALARM_FROM_INTENT = - "in.basulabs.shakealarmclock.ACTION_NEW_ALARM_FROM_INTENT"; - - static final String ACTION_STOP_IMMEDIATELY = "in.basulabs.shakealarmclock.STOP_IMMEDIATELY"; + static final String ACTION_NEW_ALARM_FROM_INTENT = "in.basulabs.shakealarmclock.ACTION_NEW_ALARM_FROM_INTENT"; /** - * Indicates whether {@link Activity_RingtonePicker} should play the ringtone when the user clicks on a {@link - * android.widget.RadioButton}. Default: {@code true}. + * Indicates whether {@link Activity_RingtonePicker} should play the ringtone when the user clicks on a {@link android.widget.RadioButton}. Default: {@code + * true}. */ static final String EXTRA_PLAY_RINGTONE = "in.basulabs.shakealarmclock.EXTRA_PLAY_RINGTONE"; /** * Bundle key for the old alarm hour. *

    This is passed from {@link Activity_AlarmDetails} to - * {@link Activity_AlarmsList} if the user saves the edits made to an existing alarm. Using this and {@link - * #BUNDLE_KEY_OLD_ALARM_MINUTE}, {@link Activity_AlarmsList} deletes the old alarm and adds/activates the new - * alarm. + * {@link Activity_AlarmsList} if the user saves the edits made to an existing alarm. Using this and {@link #BUNDLE_KEY_OLD_ALARM_MINUTE}, {@link + * Activity_AlarmsList} deletes the old alarm and adds/activates the new alarm. *

    * * @see #BUNDLE_KEY_OLD_ALARM_MINUTE @@ -181,8 +174,8 @@ final class ConstantsAndStatics { /** * Bundle key for the old alarm minute. *

    This is passed from {@link Activity_AlarmDetails} to - * {@link Activity_AlarmsList} if the user saves the edits made to an existing alarm. Using this and {@link - * #BUNDLE_KEY_OLD_ALARM_HOUR}, {@link Activity_AlarmsList} deletes the old alarm and adds/activates the new alarm. + * {@link Activity_AlarmsList} if the user saves the edits made to an existing alarm. Using this and {@link #BUNDLE_KEY_OLD_ALARM_HOUR}, {@link + * Activity_AlarmsList} deletes the old alarm and adds/activates the new alarm. *

    * * @see #BUNDLE_KEY_OLD_ALARM_HOUR @@ -200,31 +193,20 @@ final class ConstantsAndStatics { static final String ACTION_DESTROY_RING_ALARM_ACTIVITY = "in.basulabs.shakealarmclock.DESTROY_RING_ALARM_ACTIVITY"; /** - * {@link android.content.SharedPreferences} Key indicating whether the app was recently active. The value is {@code - * boolean}. - */ - static final String SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE = - "in.basulabs.shakealarmclock.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE"; - - /** - * {@link android.content.SharedPreferences} key indicating whether the read storage permission was asked before. - * This is used to determine if the user had chosen "Don't ask again" before denying the permission. The value is - * {@code boolean}. + * {@link android.content.SharedPreferences} key indicating whether the read storage permission was asked before. This is used to determine if the user had + * chosen "Don't ask again" before denying the permission. The value is {@code boolean}. */ static final String SHARED_PREF_KEY_PERMISSION_WAS_ASKED_BEFORE = "in.basulabs.shakealarmclock.PERMISSION_WAS_ASKED_BEFORE"; /** - * {@link android.content.SharedPreferences} key to store the default shake operation. Can be either {@link - * #DISMISS} or {@link #SNOOZE}. + * {@link android.content.SharedPreferences} key to store the default shake operation. Can be either {@link #DISMISS} or {@link #SNOOZE}. */ static final String SHARED_PREF_KEY_DEFAULT_SHAKE_OPERATION = "in.basulabs.shakealarmclock.DEFAULT_SHAKE_OPERATION"; /** - * {@link android.content.SharedPreferences} key to store the default power button operation. Can be either {@link - * #DISMISS} or {@link #SNOOZE}. + * {@link android.content.SharedPreferences} key to store the default power button operation. Can be either {@link #DISMISS} or {@link #SNOOZE}. */ - static final String SHARED_PREF_KEY_DEFAULT_POWER_BTN_OPERATION = - "in.basulabs.shakealarmclock.DEFAULT_POWER_BTN_OPERATION"; + static final String SHARED_PREF_KEY_DEFAULT_POWER_BTN_OPERATION = "in.basulabs.shakealarmclock.DEFAULT_POWER_BTN_OPERATION"; /** * Indicates that the ringing alarm should be snoozed. @@ -249,15 +231,14 @@ final class ConstantsAndStatics { static final String SHARED_PREF_KEY_DEFAULT_SNOOZE_INTERVAL = "in.basulabs.shakealarmclock.DEFAULT_SNOOZE_INTERVAL"; /** - * {@link android.content.SharedPreferences} key to store the default snooze frequency, i.e. the number of times the - * alarm will ring before being cancelled automatically. + * {@link android.content.SharedPreferences} key to store the default snooze frequency, i.e. the number of times the alarm will ring before being cancelled + * automatically. */ static final String SHARED_PREF_KEY_DEFAULT_SNOOZE_FREQ = "in.basulabs.shakealarmclock.DEFAULT_SNOOZE_FREQUENCY"; /** - * {@link android.content.SharedPreferences} key to store the default alarm tone Uri. If the file is unavailable, it - * will be replaced by the default alarm tone during runtime. The value is {@code String}; should be converted to - * Uri using {@link android.net.Uri#parse(String)}. + * {@link android.content.SharedPreferences} key to store the default alarm tone Uri. If the file is unavailable, it will be replaced by the default alarm + * tone during runtime. The value is {@code String}; should be converted to Uri using {@link android.net.Uri#parse(String)}. */ static final String SHARED_PREF_KEY_DEFAULT_ALARM_TONE_URI = "in.basulabs.shakealarmclock.DEFAULT_ALARM_TONE_URI"; @@ -267,8 +248,7 @@ final class ConstantsAndStatics { static final String SHARED_PREF_KEY_DEFAULT_ALARM_VOLUME = "in.basulabs.shakealarmclock.DEFAULT_ALARM_VOLUME"; /** - * The app will set its theme according to time. From 10:00 PM to 6:00 AM, the theme will be dark, and light - * otherwise. + * The app will set its theme according to time. From 10:00 PM to 6:00 AM, the theme will be dark, and light otherwise. */ static final int THEME_AUTO_TIME = 0; @@ -283,14 +263,14 @@ final class ConstantsAndStatics { static final int THEME_DARK = 2; /** - * Indicates that the theme of the app should be light. Corresponds to {@link androidx.appcompat.app.AppCompatDelegate#MODE_NIGHT_FOLLOW_SYSTEM}. - * Available only on Android Q+. + * Indicates that the theme of the app should be light. Corresponds to {@link androidx.appcompat.app.AppCompatDelegate#MODE_NIGHT_FOLLOW_SYSTEM}. Available + * only on Android Q+. */ static final int THEME_SYSTEM = 3; /** - * {@link android.content.SharedPreferences} key to store the current theme. Can only have the values {@link - * #THEME_AUTO_TIME}, {@link #THEME_LIGHT}, {@link #THEME_DARK} or {@link #THEME_SYSTEM}. + * {@link android.content.SharedPreferences} key to store the current theme. Can only have the values {@link #THEME_AUTO_TIME}, {@link #THEME_LIGHT}, {@link + * #THEME_DARK} or {@link #THEME_SYSTEM}. */ static final String SHARED_PREF_KEY_THEME = "in.basulabs.shakealarmclock.THEME"; @@ -304,8 +284,8 @@ final class ConstantsAndStatics { //--------------------------------------------------------------------------------------------------------- /** - * Creates a {@link PeriodicWorkRequest} and enqueues a unique work using {@link - * WorkManager#enqueueUniquePeriodicWork(String, ExistingPeriodicWorkPolicy, PeriodicWorkRequest)}. + * Creates a {@link PeriodicWorkRequest} and enqueues a unique work using {@link WorkManager#enqueueUniquePeriodicWork(String, ExistingPeriodicWorkPolicy, + * PeriodicWorkRequest)}. * * @param context The {@link Context} that is scheduling the work. */ @@ -313,13 +293,12 @@ static void schedulePeriodicWork(Context context) { try { WorkManager.initialize(context, new Configuration.Builder().setMinimumLoggingLevel(Log.DEBUG).build()); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } - PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(Worker_ActivateAlarms.class, - 15, TimeUnit.MINUTES).build(); + PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(Worker_ActivateAlarms.class, 15, TimeUnit.MINUTES).build(); - WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME_ACTIVATE_ALARMS, - ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest); + WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME_ACTIVATE_ALARMS, ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest); } //--------------------------------------------------------------------------------------------------------- @@ -333,7 +312,8 @@ static void cancelScheduledPeriodicWork(Context context) { try { WorkManager.initialize(context, new Configuration.Builder().setMinimumLoggingLevel(Log.DEBUG).build()); - } catch (Exception ignored) {} + } catch (Exception ignored) { + } WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME_ACTIVATE_ALARMS); } @@ -343,11 +323,11 @@ static void cancelScheduledPeriodicWork(Context context) { /** * Get the theme that can be applied using {@link AppCompatDelegate#setDefaultNightMode(int)}. * - * @param theme The theme value as stored in {@link android.content.SharedPreferences}. Can only have the values - * {@link #THEME_AUTO_TIME}, {@link #THEME_LIGHT}, {@link #THEME_DARK} or {@link #THEME_SYSTEM}. + * @param theme The theme value as stored in {@link android.content.SharedPreferences}. Can only have the values {@link #THEME_AUTO_TIME}, {@link + * #THEME_LIGHT}, {@link #THEME_DARK} or {@link #THEME_SYSTEM}. * - * @return Can have the values {@link AppCompatDelegate#MODE_NIGHT_YES}, {@link AppCompatDelegate#MODE_NIGHT_NO} or - * {@link AppCompatDelegate#MODE_NIGHT_FOLLOW_SYSTEM}. + * @return Can have the values {@link AppCompatDelegate#MODE_NIGHT_YES}, {@link AppCompatDelegate#MODE_NIGHT_NO} or {@link + * AppCompatDelegate#MODE_NIGHT_FOLLOW_SYSTEM}. */ static int getTheme(int theme) { switch (theme) { @@ -358,8 +338,7 @@ static int getTheme(int theme) { case THEME_DARK: return AppCompatDelegate.MODE_NIGHT_YES; default: - if (LocalTime.now().isAfter(LocalTime.of(21, 59)) - || LocalTime.now().isBefore(LocalTime.of(6, 0))) { + if (LocalTime.now().isAfter(LocalTime.of(21, 59)) || LocalTime.now().isBefore(LocalTime.of(6, 0))) { return AppCompatDelegate.MODE_NIGHT_YES; } else { return AppCompatDelegate.MODE_NIGHT_NO; @@ -370,10 +349,14 @@ static int getTheme(int theme) { //--------------------------------------------------------------------------------------------------------- static void killServices(Context context, int alarmID) { - Intent intent = new Intent() - .putExtra(BUNDLE_KEY_ALARM_ID, alarmID) - .setAction(ACTION_STOP_IMMEDIATELY); - context.sendBroadcast(intent); + + if (Service_RingAlarm.isThisServiceRunning && Service_RingAlarm.alarmID == alarmID) { + Intent intent1 = new Intent(context, Service_RingAlarm.class); + context.stopService(intent1); + } else if (Service_SnoozeAlarm.isThisServiceRunning && Service_SnoozeAlarm.alarmID == alarmID) { + Intent intent1 = new Intent(context, Service_SnoozeAlarm.class); + context.stopService(intent1); + } } //--------------------------------------------------------------------------------------------------------- @@ -386,11 +369,10 @@ static void killServices(Context context, int alarmID) { * @param isRepeatOn Whether repeat is on or off. * @param repeatDays The days when the alarm should be repeated. Should follow {@link DayOfWeek} enum. * - * @return A {@link LocalDateTime} object representing when the alarm should ring. This should be transformed into a - * {@link java.time.ZonedDateTime} object and then passed to {@link android.app.AlarmManager}. + * @return A {@link LocalDateTime} object representing when the alarm should ring. This should be transformed into a {@link java.time.ZonedDateTime} object + * and then passed to {@link android.app.AlarmManager}. */ - static LocalDateTime getAlarmDateTime(LocalDate alarmDate, LocalTime alarmTime, boolean isRepeatOn, - @Nullable ArrayList repeatDays) { + static LocalDateTime getAlarmDateTime(LocalDate alarmDate, LocalTime alarmTime, boolean isRepeatOn, @Nullable ArrayList repeatDays) { LocalDateTime alarmDateTime; diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Fragment_AlarmDetails_Main.java b/app/src/main/java/in/basulabs/shakealarmclock/Fragment_AlarmDetails_Main.java index 36e78fd..5db0b80 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Fragment_AlarmDetails_Main.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Fragment_AlarmDetails_Main.java @@ -323,7 +323,7 @@ private void displayAlarmTone() { } else { - String fileNameWithExt = null; + String fileName = null; try (Cursor cursor = requireContext().getContentResolver() .query(viewModel.getAlarmToneUri(), null, null, null, null)) { @@ -333,17 +333,17 @@ private void displayAlarmTone() { int index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (index != - 1) { - fileNameWithExt = cursor.getString(index); + fileName = cursor.getString(index); } else { - fileNameWithExt = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); + fileName = cursor.getString(RingtoneManager.TITLE_COLUMN_INDEX); } } } catch (Exception ignored) { } } - if (fileNameWithExt != null) { - alarmToneTV.setText(fileNameWithExt); + if (fileName != null) { + alarmToneTV.setText(fileName); } else { alarmToneTV.setText(viewModel.getAlarmToneUri().getLastPathSegment()); } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java b/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java index d22377a..be19678 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java @@ -25,10 +25,8 @@ import android.os.CountDownTimer; import android.os.Handler; import android.os.IBinder; -import android.os.Process; import android.os.VibrationEffect; import android.os.Vibrator; -import android.util.Log; import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; @@ -72,7 +70,9 @@ public class Service_RingAlarm extends Service implements SensorEventListener { private Uri alarmToneUri; - private int alarmID; + public static int alarmID = - 1; + + public static boolean isThisServiceRunning = false; private SharedPreferences sharedPreferences; @@ -89,12 +89,6 @@ public void onReceive(Context context, Intent intent) { snoozeAlarm(); } else if (Objects.equals(intent.getAction(), ConstantsAndStatics.ACTION_CANCEL_ALARM)) { dismissAlarm(); - } else if (intent.getAction().equals(ConstantsAndStatics.ACTION_STOP_IMMEDIATELY)){ - if (Objects.requireNonNull(intent.getExtras()).getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID) == alarmID){ - stopForeground(true); - stopSelf(); - Process.killProcess(Process.myPid()); - } } } }; @@ -109,6 +103,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { } else { startForeground(NOTIFICATION_ID, buildRingNotification()); } + isThisServiceRunning = true; ConstantsAndStatics.cancelScheduledPeriodicWork(this); @@ -127,7 +122,6 @@ public int onStartCommand(Intent intent, int flags, int startId) { alarmToneUri = alarmDetails.getParcelable(ConstantsAndStatics.BUNDLE_KEY_ALARM_TONE_URI); alarmID = alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID); - Log.e(this.getClass().getSimpleName(), "alarmID = " + alarmID); ringTimer = new CountDownTimer(60000, 1000) { @@ -158,7 +152,6 @@ public void onFinish() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConstantsAndStatics.ACTION_SNOOZE_ALARM); intentFilter.addAction(ConstantsAndStatics.ACTION_CANCEL_ALARM); - intentFilter.addAction(ConstantsAndStatics.ACTION_STOP_IMMEDIATELY); registerReceiver(broadcastReceiver, intentFilter); ringAlarm(); @@ -185,6 +178,8 @@ public void onDestroy() { } audioManager.setStreamVolume(AudioManager.STREAM_ALARM, initialAlarmStreamVolume, 0); unregisterReceiver(broadcastReceiver); + isThisServiceRunning = false; + alarmID = - 1; } //-------------------------------------------------------------------------------------------------- @@ -220,11 +215,11 @@ private void createNotificationChannel() { //-------------------------------------------------------------------------------------------------- /** - * Creates a notification that can be shown when the alarm is ringing. Has a full screen intent to {@link - * Activity_RingAlarm}. The content intent points to {@link Activity_AlarmsList}. + * Creates a notification that can be shown when the alarm is ringing. Has a full screen intent to {@link Activity_RingAlarm}. The content intent points to + * {@link Activity_AlarmsList}. * - * @return A {@link Notification} that can be used with {@link #startForeground(int, Notification)} or displayed - * with {@link NotificationManager#notify(int, Notification)}. + * @return A {@link Notification} that can be used with {@link #startForeground(int, Notification)} or displayed with {@link NotificationManager#notify(int, + * Notification)}. */ private Notification buildRingNotification() { createNotificationChannel(); @@ -316,8 +311,7 @@ private void alarmVibration() { //-------------------------------------------------------------------------------------------------- /** - * Snoozes the alarm. If snooze is off, or the snoze frequency has been reached, the alarm will be cancelled by - * calling {@link #dismissAlarm()}. + * Snoozes the alarm. If snooze is off, or the snoze frequency has been reached, the alarm will be cancelled by calling {@link #dismissAlarm()}. */ private void snoozeAlarm() { @@ -541,4 +535,5 @@ private void shakeVibration() { @Override public void onAccuracyChanged(Sensor sensor, int i) { } + } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java b/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java index dd68592..9dfe26e 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java @@ -15,7 +15,6 @@ import android.os.Bundle; import android.os.CountDownTimer; import android.os.IBinder; -import android.os.Process; import androidx.core.app.NotificationCompat; import androidx.core.content.ContextCompat; @@ -34,13 +33,15 @@ public class Service_SnoozeAlarm extends Service { - private int alarmID; + public static int alarmID; private Bundle alarmDetails; private static final int NOTIFICATION_ID = 651; private int numberOfTimesTheAlarmhasBeenSnoozed; private CountDownTimer snoozeTimer; + public static boolean isThisServiceRunning = false; + //-------------------------------------------------------------------------------------------------- private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @@ -48,12 +49,6 @@ public class Service_SnoozeAlarm extends Service { public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), ConstantsAndStatics.ACTION_CANCEL_ALARM)) { dismissAlarm(); - } else if (Objects.equals(intent.getAction(), ConstantsAndStatics.ACTION_STOP_IMMEDIATELY)){ - if (Objects.requireNonNull(intent.getExtras()).getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID) == alarmID){ - stopForeground(true); - stopSelf(); - Process.killProcess(Process.myPid()); - } } } }; @@ -64,11 +59,11 @@ public void onReceive(Context context, Intent intent) { public int onStartCommand(Intent intent, int flags, int startId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - startForeground(NOTIFICATION_ID, buildSnoozeNotification(), - ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); + startForeground(NOTIFICATION_ID, buildSnoozeNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); } else { startForeground(NOTIFICATION_ID, buildSnoozeNotification()); } + isThisServiceRunning = true; ConstantsAndStatics.cancelScheduledPeriodicWork(this); @@ -82,7 +77,6 @@ public int onStartCommand(Intent intent, int flags, int startId) { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConstantsAndStatics.ACTION_CANCEL_ALARM); - intentFilter.addAction(ConstantsAndStatics.ACTION_STOP_IMMEDIATELY); registerReceiver(broadcastReceiver, intentFilter); Service_SnoozeAlarm myInstance = this; @@ -96,10 +90,10 @@ public int onStartCommand(Intent intent, int flags, int startId) { ZoneId.systemDefault()); ZonedDateTime newAlarmDateTime = alarmDateTime.plusMinutes(numberOfTimesTheAlarmhasBeenSnoozed * alarmDetails - .getInt(ConstantsAndStatics.BUNDLE_KEY_SNOOZE_TIME_IN_MINS)); + .getInt(ConstantsAndStatics.BUNDLE_KEY_SNOOZE_TIME_IN_MINS)); snoozeTimer = new CountDownTimer(Math.abs(Duration.between(ZonedDateTime.now(), - newAlarmDateTime).toMillis()), 500) { + newAlarmDateTime).toMillis()), 500) { @Override public void onTick(long l) { @@ -174,7 +168,7 @@ private void dismissAlarm() { AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); Thread thread_toggleAlarm = new Thread(() -> - alarmDatabase.alarmDAO().toggleAlarm(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID),0)); + alarmDatabase.alarmDAO().toggleAlarm(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), 0)); ///////////////////////////////////// // Dismiss the snoozed alarm @@ -184,7 +178,7 @@ private void dismissAlarm() { intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), - intent, PendingIntent.FLAG_NO_CREATE); + intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); @@ -202,7 +196,7 @@ private void dismissAlarm() { } } else { LocalTime alarmTime = LocalTime.of(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_HOUR), - alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE)); + alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE)); ArrayList repeatDays = alarmDetails.getIntegerArrayList(ConstantsAndStatics.BUNDLE_KEY_REPEAT_DAYS); @@ -233,12 +227,12 @@ private void dismissAlarm() { intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, - alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), intent, 0); + alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), intent, 0); - ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime.withSecond(0), ZoneId.systemDefault()); + ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime.withSecond(0), ZoneId.systemDefault()); alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, - pendingIntent2), pendingIntent2); + pendingIntent2), pendingIntent2); } ConstantsAndStatics.schedulePeriodicWork(this); stopForeground(true); @@ -276,6 +270,8 @@ public IBinder onBind(Intent intent) { public void onDestroy() { super.onDestroy(); unregisterReceiver(broadcastReceiver); + isThisServiceRunning = false; + alarmID = -1; } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/ViewModel_RingtonePicker.java b/app/src/main/java/in/basulabs/shakealarmclock/ViewModel_RingtonePicker.java new file mode 100644 index 0000000..4cc58fd --- /dev/null +++ b/app/src/main/java/in/basulabs/shakealarmclock/ViewModel_RingtonePicker.java @@ -0,0 +1,115 @@ +package in.basulabs.shakealarmclock; + +import android.net.Uri; + +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +import java.util.ArrayList; + +public class ViewModel_RingtonePicker extends ViewModel { + + private MutableLiveData defaultUri = new MutableLiveData<>(); + private MutableLiveData existingUri = new MutableLiveData<>(); + private MutableLiveData pickedUri = new MutableLiveData<>(); + + private MutableLiveData showDefault = new MutableLiveData<>(); + private MutableLiveData showSilent = new MutableLiveData<>(); + private MutableLiveData wasExistingUriGiven = new MutableLiveData<>(); + private MutableLiveData playTone = new MutableLiveData<>(); + + private MutableLiveData title = new MutableLiveData<>(); + + private MutableLiveData> toneUriList = new MutableLiveData<>(new ArrayList<>()); + private MutableLiveData> toneNameList = new MutableLiveData<>(new ArrayList<>()); + private MutableLiveData> toneIdList = new MutableLiveData<>(new ArrayList<>()); + + public Uri getDefaultUri() { + return defaultUri.getValue(); + } + + public void setDefaultUri(Uri defaultUri) { + this.defaultUri.setValue(defaultUri); + } + + public Uri getExistingUri() { + return existingUri.getValue(); + } + + public void setExistingUri(Uri existingUri) { + this.existingUri.setValue(existingUri); + } + + public Uri getPickedUri() { + return pickedUri.getValue(); + } + + public void setPickedUri(Uri pickedUri) { + this.pickedUri.setValue(pickedUri); + } + + public Boolean getShowDefault() { + return showDefault.getValue(); + } + + public void setShowDefault(boolean showDefault) { + this.showDefault.setValue(showDefault); + } + + public Boolean getShowSilent() { + return showSilent.getValue(); + } + + public void setShowSilent(boolean showSilent) { + this.showSilent.setValue(showSilent); + } + + public Boolean getWasExistingUriGiven() { + return wasExistingUriGiven.getValue(); + } + + public void setWasExistingUriGiven(boolean wasExistingUriGiven) { + this.wasExistingUriGiven.setValue(wasExistingUriGiven); + } + + public Boolean getPlayTone() { + return playTone.getValue(); + } + + public void setPlayTone(boolean playTone) { + this.playTone.setValue(playTone); + } + + public CharSequence getTitle() { + return title.getValue(); + } + + public void setTitle(CharSequence title) { + this.title.setValue(title); + } + + public ArrayList getToneUriList() { + return toneUriList.getValue(); + } + + public void setToneUriList(ArrayList toneUriList) { + this.toneUriList.setValue(toneUriList); + } + + public ArrayList getToneNameList() { + return toneNameList.getValue(); + } + + public void setToneNameList(ArrayList toneNameList) { + this.toneNameList.setValue(toneNameList); + } + + public ArrayList getToneIdList() { + return toneIdList.getValue(); + } + + public void setToneIdList(ArrayList toneIdList) { + this.toneIdList.setValue(toneIdList); + } + +} diff --git a/app/src/main/res/layout/activity_settings_scrollview.xml b/app/src/main/res/layout/activity_settings_scrollview.xml index d5e3a01..91a586a 100644 --- a/app/src/main/res/layout/activity_settings_scrollview.xml +++ b/app/src/main/res/layout/activity_settings_scrollview.xml @@ -34,13 +34,13 @@ app:layout_constraintTop_toBottomOf="@+id/textView10" /> + app:layout_constraintTop_toBottomOf="@+id/snoozeOptionsExpandableLayout" /> + Settings If I shake the phone when an alarm rings, If I press the power button when an alarm rings, @@ -131,6 +132,8 @@ Activating alarms. This may take a while… + + Default alarm tone \ No newline at end of file From cfb60427d95250c3baae73f23e565f84210f9704 Mon Sep 17 00:00:00 2001 From: WrichikBasu <56736644+WrichikBasu@users.noreply.github.com> Date: Mon, 2 Nov 2020 20:16:25 +0530 Subject: [PATCH 2/2] Support for changing sensitivity of shake detector Sensitivity of shake detector can now be changed by going to settings. --- app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 1 + .../shakealarmclock/Activity_AlarmsList.java | 6 +- .../shakealarmclock/Activity_Settings.java | 282 ++++++++++++++---- .../AlertDialog_DiscardChanges.java | 3 +- .../AlertDialog_PermissionReason.java | 8 +- .../AlertDialog_TestShakeSensitivity.java | 54 ++++ .../shakealarmclock/ConstantsAndStatics.java | 6 +- .../shakealarmclock/Service_RingAlarm.java | 46 +-- .../shakealarmclock/Service_SnoozeAlarm.java | 36 +-- .../Worker_ActivateAlarms.java | 2 +- .../activity_settings_expand_shakeoptions.xml | 90 ++++++ .../layout/activity_settings_scrollview.xml | 67 ++++- app/src/main/res/values/strings.xml | 14 + 14 files changed, 496 insertions(+), 123 deletions(-) create mode 100644 app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_TestShakeSensitivity.java create mode 100644 app/src/main/res/layout/activity_settings_expand_shakeoptions.xml diff --git a/app/build.gradle b/app/build.gradle index eb149ed..038f2c1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "in.basulabs.shakealarmclock" minSdkVersion 21 targetSdkVersion 30 - versionCode 8 - versionName "1.2.5" + versionCode 10 + versionName "1.2.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 324432c..f136e9a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ + arrayAdapter3; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - arrayAdapter3 = ArrayAdapter.createFromResource(this, R.array.themeOptions_all, - android.R.layout.simple_spinner_item); + arrayAdapter3 = ArrayAdapter.createFromResource(this, R.array.themeOptions_all, android.R.layout.simple_spinner_item); } else { - arrayAdapter3 = ArrayAdapter.createFromResource(this, R.array.themeOptions_noSystem, - android.R.layout.simple_spinner_item); + arrayAdapter3 = ArrayAdapter.createFromResource(this, R.array.themeOptions_noSystem, android.R.layout.simple_spinner_item); } arrayAdapter3.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); themeSpinner.setAdapter(arrayAdapter3); - themeSpinner.setSelection(sharedPreferences - .getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, ConstantsAndStatics.THEME_SYSTEM)); - themeSpinner.setSelection(sharedPreferences - .getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme)); + themeSpinner.setSelection(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, ConstantsAndStatics.THEME_SYSTEM)); + themeSpinner.setSelection(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme)); themeSpinner.setOnItemSelectedListener(this); /////////////////////////////////////////////////////////// @@ -137,9 +153,9 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState == null) { snoozeOptionsExpandableLayout.setExpanded(false); } else { - snoozeOptionsExpandableLayout.setExpanded(savedInstanceState.getBoolean(SAVE_INSTANCE_KEY_LAYOUT_EXPANDED)); + snoozeOptionsExpandableLayout.setExpanded(savedInstanceState.getBoolean(SAVE_INSTANCE_SNOOZE_LAYOUT_EXPANDED)); } - snoozeOptionsExpandableLayout.setOnExpansionUpdateListener(this); + snoozeOptionsExpandableLayout.setOnExpansionUpdateListener((expansionFraction, state) -> setSnoozeImageView(state)); ConstraintLayout snoozeOptionsConstraintLayout = findViewById(R.id.settings_snoozeOptionsConstraintLayout); snoozeOptionsConstraintLayout.setOnClickListener(this); @@ -148,8 +164,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { // Initialise snooze ON/OFF switch: ///////////////////////////////////////////// snoozeStateSwitch = findViewById(R.id.settingsSnoozeOnOffSwitch); - snoozeStateSwitch.setChecked( - sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_IS_ON, true)); + snoozeStateSwitch.setChecked(sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_IS_ON, true)); setSwitchText(); snoozeStateSwitch.setOnCheckedChangeListener(this); @@ -158,20 +173,13 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { snoozeIntvEditText = findViewById(R.id.settings_snoozeIntervalEditText); snoozeFreqEditText = findViewById(R.id.settings_snoozeFreqEditText2); - snoozeIntvEditText.setText(String.valueOf( - sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_INTERVAL, 5))); - snoozeFreqEditText.setText(String.valueOf( - sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_FREQ, 3))); + snoozeIntvEditText.setText(String.valueOf(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_INTERVAL, 5))); + snoozeFreqEditText.setText(String.valueOf(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_FREQ, 3))); - activateOrDeactivateSnoozeOptions( - sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_IS_ON, true)); + activateOrDeactivateSnoozeOptions(sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SNOOZE_IS_ON, true)); snoozeImageView = findViewById(R.id.snoozeExpColapImage); - if (snoozeOptionsExpandableLayout.isExpanded()) { - snoozeImageView.setImageResource(R.drawable.ic_collapse); - } else { - snoozeImageView.setImageResource(R.drawable.ic_expand); - } + setSnoozeImageView(snoozeOptionsExpandableLayout.getState()); snoozeFreqEditText.addTextChangedListener(new TextWatcher() { @Override @@ -248,6 +256,69 @@ public void afterTextChanged(Editable editable) { autoSelectToneCheckBox.setChecked(sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_AUTO_SET_TONE, true)); autoSelectToneCheckBox.setOnCheckedChangeListener(this); + //////////////////////////////////////////////////////// + // Initialise shakeSencitivityExpandableLayout: + /////////////////////////////////////////////////////// + shakeSensitivityExpandableLayout = findViewById(R.id.shakeSensitivityExpandableLayout); + if (savedInstanceState == null) { + shakeSensitivityExpandableLayout.setExpanded(false, false); + } else { + shakeSensitivityExpandableLayout.setExpanded(savedInstanceState.getBoolean(SAVE_INSTANCE_SHAKE_LAYOUT_EXPANDED)); + } + shakeSensitivityExpandableLayout.setOnExpansionUpdateListener((expansionFraction, state) -> setShakeImageView(state)); + + //////////////////////////////////////////////////////// + // Initialise shakeSensitivityConstarintLayout: + /////////////////////////////////////////////////////// + ConstraintLayout shakeOptionsConstraintLayout = findViewById(R.id.shakeSensitivityConstarintLayout); + shakeOptionsConstraintLayout.setOnClickListener(this); + + //////////////////////////////////////////////////////// + // Initialise shakeImageView: + /////////////////////////////////////////////////////// + shakeImageView = findViewById(R.id.shakeExpColImage); + setShakeImageView(shakeSensitivityExpandableLayout.getState()); + + //////////////////////////////////////////////////////// + // Initialise sensitivitySeekBar: + /////////////////////////////////////////////////////// + SeekBar shakeSensitivitySeekBar = findViewById(R.id.shakeSensitivitySeekBar); + + double min = 1.5, max = 6.5, stepSize = 0.2; + int steps = (int) ((max - min) / stepSize); + + shakeSensitivitySeekBar.setMax(steps); + shakeSensitivitySeekBar.setProgress(getStepValue(sharedPreferences.getFloat(ConstantsAndStatics.SHARED_PREF_KEY_SHAKE_SENSITIVITY, + ConstantsAndStatics.DEFAULT_SHAKE_SENSITIVITY))); + shakeSensitivitySeekBar.setOnSeekBarChangeListener(this); + + TextView testShakeTextView = findViewById(R.id.testShakeSenstivityTextView); + testShakeTextView.setOnClickListener(this); + + sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); + acclerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + + if (savedInstanceState == null) { + isShakeTestGoingOn = false; + } else { + isShakeTestGoingOn = savedInstanceState.getBoolean(SAVE_INSTANCE_IS_SHAKE_ONGOING); + lastShakeTime = System.currentTimeMillis(); + } + + } + + @Override + protected void onResume() { + super.onResume(); + if (isShakeTestGoingOn) { + sensorManager.registerListener(this, acclerometer, SensorManager.SENSOR_DELAY_UI, new Handler()); + } + } + + @Override + protected void onPause() { + super.onPause(); + sensorManager.unregisterListener(this, acclerometer); } //----------------------------------------------------------------------------------------------------- @@ -255,7 +326,9 @@ public void afterTextChanged(Editable editable) { @Override protected void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); - outState.putBoolean(SAVE_INSTANCE_KEY_LAYOUT_EXPANDED, snoozeOptionsExpandableLayout.isExpanded()); + outState.putBoolean(SAVE_INSTANCE_SNOOZE_LAYOUT_EXPANDED, snoozeOptionsExpandableLayout.isExpanded()); + outState.putBoolean(SAVE_INSTANCE_SHAKE_LAYOUT_EXPANDED, shakeSensitivityExpandableLayout.isExpanded()); + outState.putBoolean(SAVE_INSTANCE_IS_SHAKE_ONGOING, isShakeTestGoingOn); } @@ -359,6 +432,26 @@ private void setVolumeImageView(int volume) { } } + //---------------------------------------------------------------------------------------------------- + + private void setShakeImageView(int state) { + if (state == ExpandableLayout.State.EXPANDED) { + shakeImageView.setImageResource(R.drawable.ic_collapse); + } else if (state == ExpandableLayout.State.COLLAPSED) { + shakeImageView.setImageResource(R.drawable.ic_expand); + } + } + + //---------------------------------------------------------------------------------------------------- + + private void setSnoozeImageView(int state) { + if (state == ExpandableLayout.State.EXPANDED) { + snoozeImageView.setImageResource(R.drawable.ic_collapse); + } else if (state == ExpandableLayout.State.COLLAPSED) { + snoozeImageView.setImageResource(R.drawable.ic_expand); + } + } + //----------------------------------------------------------------------------------------------------- @Override @@ -417,19 +510,7 @@ public void onNothingSelected(AdapterView adapterView) { * Applies the appropriate theme. Gets the theme using {@link ConstantsAndStatics#getTheme(int)}. */ private void applyTheme() { - AppCompatDelegate.setDefaultNightMode(ConstantsAndStatics.getTheme( - sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme))); - } - - //----------------------------------------------------------------------------------------------------- - - @Override - public void onExpansionUpdate(float expansionFraction, int state) { - if (state == ExpandableLayout.State.EXPANDED) { - snoozeImageView.setImageResource(R.drawable.ic_collapse); - } else if (state == ExpandableLayout.State.COLLAPSED) { - snoozeImageView.setImageResource(R.drawable.ic_expand); - } + AppCompatDelegate.setDefaultNightMode(ConstantsAndStatics.getTheme(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme))); } //----------------------------------------------------------------------------------------------------- @@ -501,6 +582,15 @@ public void onClick(View view) { .putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true) .putExtra(ConstantsAndStatics.EXTRA_PLAY_RINGTONE, false); startActivityForResult(intent, RINGTONE_REQUEST_CODE); + } else if (view.getId() == R.id.shakeSensitivityConstarintLayout) { + shakeSensitivityExpandableLayout.toggle(); + } else if (view.getId() == R.id.testShakeSenstivityTextView) { + sensorManager.registerListener(this, acclerometer, SensorManager.SENSOR_DELAY_UI, new Handler()); + lastShakeTime = System.currentTimeMillis(); + DialogFragment dialogFragment = new AlertDialog_TestShakeSensitivity(); + dialogFragment.setCancelable(false); + dialogFragment.show(getSupportFragmentManager(), ""); + isShakeTestGoingOn = true; } } @@ -543,9 +633,99 @@ public void onStartTrackingTouch(SeekBar seekBar) { @Override public void onStopTrackingTouch(SeekBar seekBar) { - prefEditor.remove(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_ALARM_VOLUME) - .putInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_ALARM_VOLUME, seekBar.getProgress()) - .commit(); + if (seekBar.getId() == R.id.settings_volumeSeekbar) { + prefEditor.remove(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_ALARM_VOLUME) + .putInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_ALARM_VOLUME, seekBar.getProgress()) + .commit(); + } else if (seekBar.getId() == R.id.shakeSensitivitySeekBar) { + Log.e(getClass().getSimpleName(), "Sensitivity value = " + getSensitivityValue(seekBar.getProgress())); + prefEditor.remove(ConstantsAndStatics.SHARED_PREF_KEY_SHAKE_SENSITIVITY) + .putFloat(ConstantsAndStatics.SHARED_PREF_KEY_SHAKE_SENSITIVITY, getSensitivityValue(seekBar.getProgress())) + .commit(); + } + } + + //----------------------------------------------------------------------------------------------------- + + /** + * Calculates the steps of seekbar from sensitivity value. + * + * @param sensitivity The value of sensitivity, i.e. the minimum gForce required to trigger the detector. + * + * @return The corresponding progress value that can be set in the seekbar. + */ + private int getStepValue(float sensitivity) { + return (int) ((sensitivity - 1.5f) / .2f); + } + + //----------------------------------------------------------------------------------------------------- + + /** + * Given a progress value from the seekbar, this function calculates the sensitivity of shake detector. + * + * @param step The value returned by {@link SeekBar#getProgress()}}. + * + * @return The sensitivity corresponding to the step. + */ + private float getSensitivityValue(int step) { + BigDecimal bigDecimal = new BigDecimal(1.5f + step * .2f).setScale(1, RoundingMode.FLOOR); + return bigDecimal.floatValue(); + } + + //----------------------------------------------------------------------------------------------------- + + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + float x = event.values[0]; + float y = event.values[1]; + float z = event.values[2]; + + float gX = x / SensorManager.GRAVITY_EARTH; + float gY = y / SensorManager.GRAVITY_EARTH; + float gZ = z / SensorManager.GRAVITY_EARTH; + + float gForce = (float) Math.sqrt(gX * gX + gY * gY + gZ * gZ); + // gForce will be close to 1 when there is no movement. + + if (gForce >= sharedPreferences.getFloat(ConstantsAndStatics.SHARED_PREF_KEY_SHAKE_SENSITIVITY, ConstantsAndStatics.DEFAULT_SHAKE_SENSITIVITY)) { + long currTime = System.currentTimeMillis(); + if (Math.abs(currTime - lastShakeTime) > MINIMUM_MILLIS_BETWEEN_SHAKES) { + lastShakeTime = currTime; + shakeVibration(); + } + } + } + } + + //----------------------------------------------------------------------------------------------------- + + /** + * Creates a vibration for a small period of time, indicating that the app has registered a shake event. + */ + private void shakeVibration() { + Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); + if (vibrator.hasVibrator()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE)); + } else { + vibrator.vibrate(200); + } + } + } + + //----------------------------------------------------------------------------------------------------- + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + //----------------------------------------------------------------------------------------------------- + + @Override + public void onDialogNegativeClick(DialogFragment dialogFragment) { + sensorManager.unregisterListener(this, acclerometer); + isShakeTestGoingOn = false; } } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_DiscardChanges.java b/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_DiscardChanges.java index ea1b6f0..4660840 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_DiscardChanges.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_DiscardChanges.java @@ -23,8 +23,7 @@ public void onAttach(@NonNull Context context) { if (context instanceof DialogListener) { listener = (DialogListener) context; } else { - throw new ClassCastException(context.getClass() + " must implement AlertDialog_DiscardChanges" + - ".DialogListener"); + throw new ClassCastException(context.getClass() + " must implement AlertDialog_DiscardChanges.DialogListener"); } } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_PermissionReason.java b/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_PermissionReason.java index a747560..5095a0f 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_PermissionReason.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_PermissionReason.java @@ -13,7 +13,7 @@ public class AlertDialog_PermissionReason extends DialogFragment { private DialogListener listener; - private String message; + private final String message; public AlertDialog_PermissionReason(String message) { this.message = message; @@ -31,8 +31,7 @@ public void onAttach(@NonNull Context context) { if (context instanceof DialogListener) { listener = (DialogListener) context; } else { - throw new ClassCastException(context.getClass() + " must implement AlertDialog_PermissionReason" + - ".DialogListener"); + throw new ClassCastException(context.getClass() + " must implement AlertDialog_PermissionReason.DialogListener"); } } @@ -43,8 +42,7 @@ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); builder.setMessage(message) .setPositiveButton(getResources().getString(R.string.cancelDialog_positive), - (dialogInterface, i) -> listener - .onDialogPositiveClick(AlertDialog_PermissionReason.this)) + (dialogInterface, i) -> listener.onDialogPositiveClick(AlertDialog_PermissionReason.this)) .setNegativeButton(getResources().getString(R.string.cancelDialog_negative), (dialogInterface, i) -> { listener.onDialogNegativeClick(AlertDialog_PermissionReason.this); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_TestShakeSensitivity.java b/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_TestShakeSensitivity.java new file mode 100644 index 0000000..08e101f --- /dev/null +++ b/app/src/main/java/in/basulabs/shakealarmclock/AlertDialog_TestShakeSensitivity.java @@ -0,0 +1,54 @@ +package in.basulabs.shakealarmclock; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; + +public class AlertDialog_TestShakeSensitivity extends DialogFragment { + + private AlertDialog_TestShakeSensitivity.DialogListener listener; + + public interface DialogListener { + + void onDialogNegativeClick(DialogFragment dialogFragment); + + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + if (context instanceof AlertDialog_TestShakeSensitivity.DialogListener) { + listener = (AlertDialog_TestShakeSensitivity.DialogListener) context; + } else { + throw new ClassCastException(context.getClass() + " must implement AlertDialog_TestShakeSensitivity.DialogListener"); + } + } + + @NonNull + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + + AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity()); + builder.setTitle(R.string.title_shakeDetectionTest) + .setMessage(getResources().getString(R.string.message_shakeDetectionTest)) + .setNegativeButton(getResources().getString(R.string.negative_shakeDetectionTest), (dialogInterface, i) -> { + listener.onDialogNegativeClick(AlertDialog_TestShakeSensitivity.this); + dismiss(); + }) + .setCancelable(false); + + return builder.create(); + } + + @Override + public void onDetach() { + super.onDetach(); + listener = null; + } + +} diff --git a/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java b/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java index a719155..dede3e8 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java @@ -206,7 +206,11 @@ final class ConstantsAndStatics { /** * {@link android.content.SharedPreferences} key to store the default power button operation. Can be either {@link #DISMISS} or {@link #SNOOZE}. */ - static final String SHARED_PREF_KEY_DEFAULT_POWER_BTN_OPERATION = "in.basulabs.shakealarmclock.DEFAULT_POWER_BTN_OPERATION"; + static final String SHARED_PREF_KEY_DEFAULT_POWER_BTN_OPERATION = "in.basulabs.shakealarmclock.DEFAULT_POWER_BTN_OPERATION"; + + static final String SHARED_PREF_KEY_SHAKE_SENSITIVITY = "in.basulabs.shakealarmclock.SHAKE_SENSITIVITY"; + + static final float DEFAULT_SHAKE_SENSITIVITY = 3.2f; /** * Indicates that the ringing alarm should be snoozed. diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java b/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java index be19678..b33cc7b 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java @@ -203,8 +203,7 @@ private void initialiseShakeSensor() { private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int importance = NotificationManager.IMPORTANCE_HIGH; - NotificationChannel channel = new NotificationChannel(Integer.toString(NOTIFICATION_ID), - "in.basulabs.shakealarmclock Notifications", importance); + NotificationChannel channel = new NotificationChannel(Integer.toString(NOTIFICATION_ID),"in.basulabs.shakealarmclock Notifications", importance); NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); channel.setSound(null, null); assert notificationManager != null; @@ -228,8 +227,7 @@ private Notification buildRingNotification() { fullScreenIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 3054, - fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent fullScreenPendingIntent = PendingIntent.getActivity(this, 3054, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, Integer.toString(NOTIFICATION_ID)) @@ -254,8 +252,7 @@ private void ringAlarm() { notificationManager.notify(NOTIFICATION_ID, buildRingNotification()); initialiseShakeSensor(); - if (! (alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_TYPE) - == ConstantsAndStatics.ALARM_TYPE_VIBRATE_ONLY)) { + if (! (alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_TYPE) == ConstantsAndStatics.ALARM_TYPE_VIBRATE_ONLY)) { mediaPlayer = new MediaPlayer(); AudioAttributes attributes = new AudioAttributes.Builder() @@ -263,8 +260,7 @@ private void ringAlarm() { .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .build(); - audioManager.setStreamVolume(AudioManager.STREAM_ALARM, - alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_VOLUME), 0); + audioManager.setStreamVolume(AudioManager.STREAM_ALARM, alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_VOLUME), 0); try { mediaPlayer.setDataSource(this, alarmToneUri); @@ -274,8 +270,7 @@ private void ringAlarm() { } catch (IOException ignored) { } - if (alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_TYPE) - == ConstantsAndStatics.ALARM_TYPE_SOUND_AND_VIBRATE) { + if (alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_TYPE) == ConstantsAndStatics.ALARM_TYPE_SOUND_AND_VIBRATE) { alarmVibration(); } mediaPlayer.start(); @@ -319,8 +314,7 @@ private void snoozeAlarm() { if (alarmDetails.getBoolean(ConstantsAndStatics.BUNDLE_KEY_IS_SNOOZE_ON)) { - if (numberOfTimesTheAlarmHasBeenSnoozed < alarmDetails - .getInt(ConstantsAndStatics.BUNDLE_KEY_SNOOZE_FREQUENCY)) { + if (numberOfTimesTheAlarmHasBeenSnoozed < alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_SNOOZE_FREQUENCY)) { numberOfTimesTheAlarmHasBeenSnoozed++; @@ -349,9 +343,7 @@ private void dismissAlarm() { stopRinging(); cancelPendingIntent(); - Thread thread_toggleAlarm = new Thread( - () -> alarmDatabase.alarmDAO() - .toggleAlarm(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), 0)); + Thread thread_toggleAlarm = new Thread(() -> alarmDatabase.alarmDAO().toggleAlarm(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), 0)); ////////////////////////////////////////////////////// // If repeat is on, set another alarm. Otherwise @@ -433,17 +425,16 @@ private void stopRinging() { private void setAlarm(LocalDateTime alarmDateTime) { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(this, AlarmBroadcastReceiver.class); - intent.setAction(ConstantsAndStatics.ACTION_DELIVER_ALARM); - intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); - intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); + Intent intent = new Intent(this, AlarmBroadcastReceiver.class) + .setAction(ConstantsAndStatics.ACTION_DELIVER_ALARM) + .setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + .putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmID, intent, PendingIntent.FLAG_CANCEL_CURRENT); ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime.withSecond(0), ZoneId.systemDefault()); - alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, - pendingIntent), pendingIntent); + alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, pendingIntent), pendingIntent); } //--------------------------------------------------------------------------------------------------- @@ -486,7 +477,7 @@ public void onSensorChanged(SensorEvent event) { float gForce = (float) Math.sqrt(gX * gX + gY * gY + gZ * gZ); // gForce will be close to 1 when there is no movement. - if (gForce >= 3.8f) { + if (gForce >= sharedPreferences.getFloat(ConstantsAndStatics.SHARED_PREF_KEY_SHAKE_SENSITIVITY, ConstantsAndStatics.DEFAULT_SHAKE_SENSITIVITY)) { long currTime = System.currentTimeMillis(); if (Math.abs(currTime - lastShakeTime) > MINIMUM_MILLIS_BETWEEN_SHAKES) { lastShakeTime = currTime; @@ -516,17 +507,6 @@ private void shakeVibration() { } else { vibrator.vibrate(200); } - Thread thread = new Thread(() -> { - try { - Thread.sleep(200); - } catch (InterruptedException ignored) { - } - }); - thread.start(); - try { - thread.join(); - } catch (InterruptedException ignored) { - } } } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java b/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java index 9dfe26e..4b5bbc3 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Service_SnoozeAlarm.java @@ -72,8 +72,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { assert alarmDetails != null; alarmID = alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID); - numberOfTimesTheAlarmhasBeenSnoozed = - intent.getExtras().getInt(Service_RingAlarm.BUNDLE_KEY_NO_OF_TIMES_SNOOZED); + numberOfTimesTheAlarmhasBeenSnoozed = intent.getExtras().getInt(Service_RingAlarm.BUNDLE_KEY_NO_OF_TIMES_SNOOZED); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConstantsAndStatics.ACTION_CANCEL_ALARM); @@ -89,21 +88,20 @@ public int onStartCommand(Intent intent, int flags, int startId) { alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE), 0, 0), ZoneId.systemDefault()); - ZonedDateTime newAlarmDateTime = alarmDateTime.plusMinutes(numberOfTimesTheAlarmhasBeenSnoozed * alarmDetails - .getInt(ConstantsAndStatics.BUNDLE_KEY_SNOOZE_TIME_IN_MINS)); + ZonedDateTime newAlarmDateTime = alarmDateTime.plusMinutes(numberOfTimesTheAlarmhasBeenSnoozed * + alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_SNOOZE_TIME_IN_MINS)); - snoozeTimer = new CountDownTimer(Math.abs(Duration.between(ZonedDateTime.now(), - newAlarmDateTime).toMillis()), 500) { + snoozeTimer = new CountDownTimer(Math.abs(Duration.between(ZonedDateTime.now(), newAlarmDateTime).toMillis()), 500) { @Override - public void onTick(long l) { + public void onTick(long millisUntilFinished) { } @Override public void onFinish() { - Intent intent1 = new Intent(myInstance, Service_RingAlarm.class); - intent1.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); - intent1.putExtra(Service_RingAlarm.BUNDLE_KEY_NO_OF_TIMES_SNOOZED, numberOfTimesTheAlarmhasBeenSnoozed); + Intent intent1 = new Intent(myInstance, Service_RingAlarm.class) + .putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails) + .putExtra(Service_RingAlarm.BUNDLE_KEY_NO_OF_TIMES_SNOOZED, numberOfTimesTheAlarmhasBeenSnoozed); ContextCompat.startForegroundService(myInstance, intent1); myInstance.stopSelf(); } @@ -122,10 +120,8 @@ public void onFinish() { private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { int importance = NotificationManager.IMPORTANCE_HIGH; - NotificationChannel channel = new NotificationChannel(Integer.toString(NOTIFICATION_ID), - "in.basulabs.shakealarmclock Notifications", importance); - NotificationManager notificationManager = (NotificationManager) getSystemService( - NOTIFICATION_SERVICE); + NotificationChannel channel = new NotificationChannel(Integer.toString(NOTIFICATION_ID),"in.basulabs.shakealarmclock Notifications", importance); + NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); channel.setSound(null, null); assert notificationManager != null; notificationManager.createNotificationChannel(channel); @@ -139,11 +135,9 @@ private Notification buildSnoozeNotification() { Intent intent = new Intent(); intent.setAction(ConstantsAndStatics.ACTION_CANCEL_ALARM); - PendingIntent contentPendingIntent = PendingIntent.getBroadcast(this, 5017, intent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent contentPendingIntent = PendingIntent.getBroadcast(this, 5017, intent, PendingIntent.FLAG_UPDATE_CURRENT); - NotificationCompat.Builder builder = new NotificationCompat.Builder(this, - Integer.toString(NOTIFICATION_ID)) + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, Integer.toString(NOTIFICATION_ID)) .setContentTitle(getResources().getString(R.string.app_name)) .setContentText(getResources().getString(R.string.notifContent_snooze)) .setPriority(NotificationCompat.PRIORITY_HIGH) @@ -226,13 +220,11 @@ private void dismissAlarm() { intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); - PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, - alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), intent, 0); + PendingIntent pendingIntent2 = PendingIntent.getBroadcast(this, alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), intent, 0); ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime.withSecond(0), ZoneId.systemDefault()); - alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, - pendingIntent2), pendingIntent2); + alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, pendingIntent2), pendingIntent2); } ConstantsAndStatics.schedulePeriodicWork(this); stopForeground(true); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java b/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java index c38472a..38d05a8 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java @@ -128,7 +128,7 @@ private void activateAlarmsIfInactive() { } - if (stopExecuting && ! isStopped()) { + if ((stopExecuting && ! isStopped()) || Service_RingAlarm.isThisServiceRunning || Service_SnoozeAlarm.isThisServiceRunning) { break; } } diff --git a/app/src/main/res/layout/activity_settings_expand_shakeoptions.xml b/app/src/main/res/layout/activity_settings_expand_shakeoptions.xml new file mode 100644 index 0000000..190047d --- /dev/null +++ b/app/src/main/res/layout/activity_settings_expand_shakeoptions.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings_scrollview.xml b/app/src/main/res/layout/activity_settings_scrollview.xml index 91a586a..0f331c0 100644 --- a/app/src/main/res/layout/activity_settings_scrollview.xml +++ b/app/src/main/res/layout/activity_settings_scrollview.xml @@ -40,7 +40,7 @@ android:layout_marginStart="8dp" android:layout_marginEnd="8dp" app:el_duration="500" - app:el_expanded="true" + app:el_expanded="false" app:el_parallax="0.5" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" @@ -95,7 +95,7 @@ android:background="?android:attr/listDivider" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/powerBtnOpSpinner" /> + app:layout_constraintTop_toBottomOf="@+id/shakeSensitivityExpandableLayout" /> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 52a64be..44b3fa9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -130,9 +130,23 @@ Default alarm volume: Automatically set new tones I select as default tones + Shake sensitivity settings\nTest the sensitivity of the shake + detector, and change it as you wish + Change sensitivity: + \u2191\nLeast\nsensitive + \u2191\nMost\nsensitive + Test shake sensitivity\nTap to enable the shake detector so that you can + choose the sensitivity that correctly suits you. + Activating alarms. This may take a while… + + Test sensitivity of shake detector + Shake your phone. A vibration will tell you that a shake event has + been detected. Change the sensitivity as per your choice. + Stop + Default alarm tone