From 7d8e9c84994d58ea8a9410ba6722a7eb24ba25bd Mon Sep 17 00:00:00 2001 From: WrichikBasu <56736644+WrichikBasu@users.noreply.github.com> Date: Fri, 23 Oct 2020 01:31:17 +0530 Subject: [PATCH] Replaced background service with WorkManager Background service "Service_AlarmActivater" has been replaced with "Worker_ActivateAlarms" which will be managed by WorkManager. Every 15 mins, this worker will be activated. Primary aim is to reduce battery useage. --- app/build.gradle | 19 +- app/src/main/AndroidManifest.xml | 20 +- .../Activity_AlarmDetails.java | 2 +- .../shakealarmclock/Activity_AlarmsList.java | 123 +++------- .../shakealarmclock/Activity_Settings.java | 2 +- .../AlarmBroadcastReceiver.java | 32 +-- .../shakealarmclock/ConstantsAndStatics.java | 186 ++++++++++----- .../Fragment_AlarmDetails_Main.java | 14 +- .../shakealarmclock/MyApplication.java | 3 - .../shakealarmclock/MyContentProvider.java | 47 ++++ .../MyWorkManagerInitializer.java | 20 ++ .../Service_AlarmActivater.java | 216 ------------------ .../shakealarmclock/Service_RingAlarm.java | 118 ++++------ .../shakealarmclock/Service_SnoozeAlarm.java | 63 ++--- .../shakealarmclock/Service_UpdateAlarm.java | 82 ++----- .../Worker_ActivateAlarms.java | 145 ++++++++++++ build.gradle | 9 - 17 files changed, 460 insertions(+), 641 deletions(-) create mode 100644 app/src/main/java/in/basulabs/shakealarmclock/MyContentProvider.java create mode 100644 app/src/main/java/in/basulabs/shakealarmclock/MyWorkManagerInitializer.java delete mode 100644 app/src/main/java/in/basulabs/shakealarmclock/Service_AlarmActivater.java create mode 100644 app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java diff --git a/app/build.gradle b/app/build.gradle index bb9660f..43db4e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,35 +8,27 @@ android { applicationId "in.basulabs.shakealarmclock" minSdkVersion 21 targetSdkVersion 30 - versionCode 2 - versionName "1.1" + versionCode 3 + versionName "1.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } compileOptions { - // Flag to enable support for the new language APIs coreLibraryDesugaringEnabled true - // Sets Java compatibility to Java 8 + sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } buildTypes { release { - minifyEnabled true - shrinkResources true + minifyEnabled false + shrinkResources false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } - /*sourceSets { - main { - java { - } - } - }*/ - lintOptions { disable 'ApplySharedPref' } @@ -46,6 +38,7 @@ dependencies { implementation 'androidx.legacy:legacy-support-v4:1.0.0' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation "androidx.work:work-runtime:2.4.0" implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.2' implementation "androidx.room:room-runtime:2.2.5" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4a5572a..1166912 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,20 +29,24 @@ android:theme="@style/AppTheme" tools:ignore="AllowBackup"> + + + + - - diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmDetails.java b/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmDetails.java index 406e230..764aec2 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmDetails.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmDetails.java @@ -65,7 +65,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { defaultTheme = ConstantsAndStatics.THEME_SYSTEM; } else { - defaultTheme = ConstantsAndStatics.THEME_TIME; + defaultTheme = ConstantsAndStatics.THEME_AUTO_TIME; } AppCompatDelegate.setDefaultNightMode( ConstantsAndStatics.getTheme(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme))); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmsList.java b/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmsList.java index e029d92..f5f52f7 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmsList.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Activity_AlarmsList.java @@ -14,7 +14,6 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -86,21 +85,10 @@ protected void onCreate(Bundle savedInstanceState) { viewModel.init(alarmDatabase); - int defaultTheme; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - defaultTheme = ConstantsAndStatics.THEME_SYSTEM; - } else { - defaultTheme = ConstantsAndStatics.THEME_TIME; - } - - checkForPlugin(); - - /*Log.e(this.getClass().toString(), "default theme: " + defaultTheme + - " received theme: " + ConstantsAndStatics - .getTheme(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme)));*/ + int defaultTheme = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? ConstantsAndStatics.THEME_SYSTEM : ConstantsAndStatics.THEME_AUTO_TIME; - AppCompatDelegate.setDefaultNightMode( - ConstantsAndStatics.getTheme(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme))); + AppCompatDelegate.setDefaultNightMode(ConstantsAndStatics + .getTheme(sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_THEME, defaultTheme))); myInstance = this; @@ -116,14 +104,11 @@ protected void onCreate(Bundle savedInstanceState) { viewStub = findViewById(R.id.viewStub); - Intent intent = new Intent(getApplicationContext(), Service_AlarmActivater.class); - getApplicationContext().startService(intent); - - SharedPreferences.Editor prefEditor = getSharedPreferences(ConstantsAndStatics.SHARED_PREF_FILE_NAME, - MODE_PRIVATE) - .edit() - .remove(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE) - .putBoolean(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE, true); + SharedPreferences.Editor prefEditor = + getSharedPreferences(ConstantsAndStatics.SHARED_PREF_FILE_NAME, MODE_PRIVATE) + .edit() + .remove(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE) + .putBoolean(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE, true); prefEditor.commit(); initAdapter(); @@ -156,12 +141,14 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { return super.onOptionsItemSelected(item); } + //-------------------------------------------------------------------------------------------------- @Override protected void onDestroy() { super.onDestroy(); myInstance = null; + ConstantsAndStatics.schedulePeriodicWork(this); } @@ -179,7 +166,6 @@ private void manageViewStub(int count) { public static void onDateChanged() { if (myInstance != null) { - //Log.e(myInstance.getClass().getSimpleName(), "Inside onDateChanged()"); myInstance.viewModel.forceInit(myInstance.alarmDatabase); myInstance.alarmAdapter = new AlarmAdapter(myInstance.viewModel.getAlarmDataArrayList(), myInstance, @@ -193,7 +179,6 @@ public static void onDateChanged() { @Override protected void onResume() { super.onResume(); - //Log.e(this.getClass().getSimpleName(), "Inside onResume()."); if (showPermissionDialog) { showPermissionDialog = false; showPermissionDialog(); @@ -219,8 +204,8 @@ private void checkAndRequestPermission() { if (! viewModel.getHasPermissionBeenRequested()) { viewModel.setHasPermissionBeenRequested(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (ContextCompat.checkSelfPermission(this, - Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED) { } else if (shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE)) { showPermissionDialog(); } else { @@ -261,17 +246,16 @@ private void initAdapter() { //-------------------------------------------------------------------------------------------------- /** - * Adds an alarm to the database, updates {@link #alarmAdapter}, and activates it via {@link - * AlarmManager}. + * Adds an alarm to the database, updates {@link #alarmAdapter}, and activates it via {@link AlarmManager}. * - * @param mode The mode. Can be either {@link #MODE_ADD_NEW_ALARM} or {@link - * #MODE_ACTIVATE_EXISTING_ALARM}. + * @param mode The mode. Can be either {@link #MODE_ADD_NEW_ALARM} or {@link #MODE_ACTIVATE_EXISTING_ALARM}. * @param alarmEntity The {@link AlarmEntity} object representing the alarm. * @param repeatDays The days in which the alarm is to repeat. Can be {@code null} is repeat is OFF. */ private void addOrActivateAlarm(int mode, AlarmEntity alarmEntity, @Nullable ArrayList repeatDays) { - //Log.e(this.getClass().getSimpleName(), "addOrActivateAlarm() method called."); + + ConstantsAndStatics.cancelScheduledPeriodicWork(this); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); @@ -331,14 +315,12 @@ private void addOrActivateAlarm(int mode, AlarmEntity alarmEntity, alarmID = result[0]; - //alarmAdapter.add(alarmDateTime, alarmEntity.isRepeatOn, repeatDays, alarmEntity.alarmType); alarmAdapter = new AlarmAdapter(viewModel.getAlarmDataArrayList(), this, this); alarmsRecyclerView.swapAdapter(alarmAdapter, false); alarmsRecyclerView.scrollToPosition(result[1]); } else { viewModel.toggleAlarmState(alarmDatabase, alarmEntity.alarmHour, alarmEntity.alarmMinutes, 1); - //alarmAdapter.toggleAlarmState(alarmEntity.alarmHour, alarmEntity.alarmMinutes, 1); alarmAdapter = new AlarmAdapter(viewModel.getAlarmDataArrayList(), this, this); alarmsRecyclerView.swapAdapter(alarmAdapter, false); alarmID = alarmEntity.alarmID; @@ -359,34 +341,10 @@ private void addOrActivateAlarm(int mode, AlarmEntity alarmEntity, ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime.withSecond(0), ZoneId.systemDefault()); - /*Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.YEAR, alarmDateTime.getYear()); - calendar.set(Calendar.MONTH, alarmDateTime.getMonthValue() - 1); - calendar.set(Calendar.DAY_OF_MONTH, alarmDateTime.getDayOfMonth()); - calendar.set(Calendar.HOUR_OF_DAY, alarmDateTime.getHour()); - calendar.set(Calendar.MINUTE, alarmDateTime.getMinute()); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0);*/ - alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, pendingIntent), pendingIntent); - /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), - pendingIntent); - } else { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); - }*/ - - Log.e(this.getClass().getSimpleName(), "alarmID = " + alarmID); - - /*PendingIntent pi = PendingIntent.getBroadcast(Activity_AlarmsList.this, alarmID, intent, - PendingIntent.FLAG_NO_CREATE); - if (pi == null) { - Log.e(this.getClass().getSimpleName(), "addOrActivateAlarm() pending intent not found."); - } else { - Log.e(this.getClass().getSimpleName(), "addOrActivateAlarm() pending intent found."); - }*/ + ConstantsAndStatics.schedulePeriodicWork(this); } //------------------------------------------------------------------------------------------------------ @@ -399,7 +357,8 @@ private void addOrActivateAlarm(int mode, AlarmEntity alarmEntity, * @param mins The alarm minutes. */ private void deleteOrDeactivateAlarm(int mode, int hour, int mins) { - //Log.e(this.getClass().getSimpleName(), "deleteOrDeactivateAlarm() called"); + + ConstantsAndStatics.cancelScheduledPeriodicWork(this); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); @@ -409,41 +368,25 @@ private void deleteOrDeactivateAlarm(int mode, int hour, int mins) { int alarmID = viewModel.getAlarmId(alarmDatabase, hour, mins); - //Log.e(this.getClass().getSimpleName(), "id = " + alarmID); - - PendingIntent pendingIntent = PendingIntent - .getBroadcast(Activity_AlarmsList.this, alarmID, intent, - PendingIntent.FLAG_NO_CREATE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(Activity_AlarmsList.this, alarmID, intent, + PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); - //Log.e(this.getClass().getSimpleName(), "Alarm cancelled."); - } /*else { - Log.e(this.getClass().getSimpleName(), "Alarm not found."); - }*/ + } + if (mode == MODE_DELETE_ALARM) { viewModel.removeAlarm(alarmDatabase, hour, mins); alarmAdapter = new AlarmAdapter(viewModel.getAlarmDataArrayList(), this, this); alarmsRecyclerView.swapAdapter(alarmAdapter, false); - //alarmAdapter.remove(hour, mins); } else { viewModel.toggleAlarmState(alarmDatabase, hour, mins, 0); - //alarmAdapter.toggleAlarmState(hour, mins, 0); - //No need to swap adapter again. } - - ////////////////////////////////////////////////////////// - // Kill any foreground service based on this alarm - ///////////////////////////////////////////////////////// - /*if (Service_RingAlarm.isThisServiceRunning) { - if (Service_RingAlarm.alarmHour == hour && Service_RingAlarm.alarmMinute == mins) { - Intent intent1 = new Intent(this, Service_RingAlarm.class); - stopService(intent1); - } - }*/ + // Kill any foreground service based on this alarm: ConstantsAndStatics.killServices(this, alarmID); + ConstantsAndStatics.schedulePeriodicWork(this); } //-------------------------------------------------------------------------------------------------- @@ -471,17 +414,17 @@ private void toggleAlarmState(int hour, int mins, final int newAlarmState) { @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent intent) { super.onActivityResult(requestCode, resultCode, intent); + if (requestCode == NEW_ALARM_REQUEST_CODE) { - /*if (resultCode == RESULT_CANCELED) { - Log.e(this.getClass().getSimpleName(), "No new alarm."); - }*/ if (resultCode == RESULT_OK) { + if (intent != null) { - //Log.e(this.getClass().getSimpleName(), "Received new alarm."); + Bundle data = Objects.requireNonNull(intent.getExtras()) .getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS); assert data != null; + AlarmEntity alarmEntity = new AlarmEntity(data.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_HOUR), data.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE), true, @@ -502,13 +445,9 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent i } } } else if (requestCode == EXISTING_ALARM_REQUEST_CODE) { - /*if (resultCode == RESULT_CANCELED) { - Log.e(this.getClass().getSimpleName(), "No changes made in existing alarm."); - }*/ if (resultCode == RESULT_OK) { if (intent != null) { - //Log.e(this.getClass().getSimpleName(), "Changes were made in existing alarm."); Bundle data = Objects.requireNonNull(intent.getExtras()) .getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS); assert data != null; @@ -545,7 +484,6 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent i @Override public void onOnOffButtonClick(int rowNumber, int hour, int mins, int currentAlarmState) { - //Log.e(this.getClass().getSimpleName(), "OnOff Btn clicked."); toggleAlarmState(hour, mins, currentAlarmState); } @@ -553,7 +491,6 @@ public void onOnOffButtonClick(int rowNumber, int hour, int mins, int currentAla @Override public void onDeleteButtonClicked(int rowNumber, int hour, int mins) { - //Log.e(this.getClass().getSimpleName(), "Delete button clicked."); deleteOrDeactivateAlarm(MODE_DELETE_ALARM, hour, mins); } @@ -561,7 +498,6 @@ public void onDeleteButtonClicked(int rowNumber, int hour, int mins) { @Override public void onItemClicked(int rowNumber, int hour, int mins) { - //Log.e(this.getClass().getSimpleName(), "Row clicked. Row number: " + rowNumber); Intent intent = new Intent(this, Activity_AlarmDetails.class); intent.setAction(ConstantsAndStatics.ACTION_EXISTING_ALARM); @@ -636,7 +572,6 @@ private boolean isInstalledOnInternalStorage() { //-------------------------------------------------------------------------------------------------- private void checkForPlugin() { - //Log.e(this.getClass().getSimpleName(), "Checking for plugin"); if (! isInstalledOnInternalStorage()) { try { getPackageManager().getApplicationInfo("in.basulabs.shakealarmclockplugin", 0); 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 b300d41..e05a4d9 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Activity_Settings.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Activity_Settings.java @@ -79,7 +79,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { defaultTheme = ConstantsAndStatics.THEME_SYSTEM; } else { - defaultTheme = ConstantsAndStatics.THEME_TIME; + defaultTheme = ConstantsAndStatics.THEME_AUTO_TIME; } applyTheme(); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/AlarmBroadcastReceiver.java b/app/src/main/java/in/basulabs/shakealarmclock/AlarmBroadcastReceiver.java index e45a730..aae8a62 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/AlarmBroadcastReceiver.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/AlarmBroadcastReceiver.java @@ -3,7 +3,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.PowerManager; import androidx.core.content.ContextCompat; @@ -26,32 +25,10 @@ public void onReceive(Context context, Intent intent) { Intent intent1 = new Intent(context, Service_RingAlarm.class); intent1.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, - Objects.requireNonNull(intent.getExtras()) - .getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS)); + Objects.requireNonNull(intent.getExtras()).getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS)); ContextCompat.startForegroundService(context, intent1); - /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(intent1); - } else { - context.startService(intent1); - }*/ - - } else if (Objects.equals(intent.getAction(), ConstantsAndStatics.ACTION_CREATE_BACKGROUND_SERVICE)) { - - SharedPreferences sharedPreferences = - context.getSharedPreferences(ConstantsAndStatics.SHARED_PREF_FILE_NAME, Context.MODE_PRIVATE); - - if (sharedPreferences.getBoolean(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE, true)) { - Intent intent1 = new Intent(context, Service_AlarmActivater.class); - context.startService(intent1); - - SharedPreferences.Editor editor = sharedPreferences.edit() - .remove(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE) - .putBoolean(ConstantsAndStatics.SHARED_PREF_KEY_WAS_APP_RECENTLY_ACTIVE, false); - editor.commit(); - } - - } else if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED) + } else if (Objects.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED) || intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED) || intent.getAction().equals(Intent.ACTION_TIME_CHANGED)) { @@ -62,11 +39,6 @@ public void onReceive(Context context, Intent intent) { Intent intent1 = new Intent(context, Service_UpdateAlarm.class); ContextCompat.startForegroundService(context, intent1); - /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - context.startForegroundService(intent1); - } else { - context.startService(intent1); - }*/ } } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java b/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java index 5025e65..4ee7326 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/ConstantsAndStatics.java @@ -2,10 +2,16 @@ import android.content.Context; import android.content.Intent; +import android.util.Log; import androidx.appcompat.app.AppCompatDelegate; +import androidx.work.Configuration; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; import java.time.LocalTime; +import java.util.concurrent.TimeUnit; /** * A class containing all the constants required by this app. @@ -65,8 +71,7 @@ final class ConstantsAndStatics { static final String BUNDLE_KEY_IS_ALARM_ON = "in.basulabs.shakealarmclock.IsAlarmOn"; /** - * Bundle key for the alarm repeat days. The value is an ArrayList of Integer type. Monday is 1 and Sunday - * is 7. + * Bundle key for the alarm repeat days. The value is an ArrayList of Integer type. Monday is 1 and Sunday is 7. */ static final String BUNDLE_KEY_REPEAT_DAYS = "in.basulabs.shakealarmclock.ArrayListOfRepeatDays"; @@ -110,17 +115,20 @@ final class ConstantsAndStatics { */ static final String BUNDLE_KEY_ALARM_TONE_URI = "in.basulabs.shakealarmclock.ALARM_TONE_URI"; + /** + * Bundle key: Indicates whether the user has explicitly chosen a date for that alarm. + */ 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"; @@ -139,14 +147,18 @@ final class ConstantsAndStatics { */ static final String ACTION_EXISTING_ALARM = "in.basulabs.shakealarmclock.ACTION_EXISTING_ALARM"; + /** + * 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 @@ -156,9 +168,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 @@ -170,105 +181,154 @@ final class ConstantsAndStatics { */ static final String BUNDLE_KEY_ALARM_ID = "in.basulabs.shakealarmclock.OLD_ALARM_ID"; - /** - * Intent action passed as broadcast to {@link AlarmBroadcastReceiver} from {@link Service_AlarmActivater} - * so that the former can recreate the latter when the latter is killed. - */ - static final String ACTION_CREATE_BACKGROUND_SERVICE = "in.basulabs.shakealarmclock" + - ".CreateBackgroundService"; - /** * Broadcast action: {@link Activity_RingAlarm} should now be destroyed. */ - static final String ACTION_DESTROY_RING_ALARM_ACTIVITY = "in.basulabs.shakealarmclock" + - ".DESTROY_RING_ALARM_ACTIVITY"; + 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}. + * {@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"; + 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"; + 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. + */ static final int SNOOZE = 0; + /** + * Indicates that the ringing alarm should be dismissed completely. + */ static final int DISMISS = 1; static final int DO_NOTHING = 2; /** - * {@link android.content.SharedPreferences} key to store the default snooze state. The value is {@code - * boolean}. + * {@link android.content.SharedPreferences} key to store the default snooze state. The value is {@code boolean}. */ - static final String SHARED_PREF_KEY_DEFAULT_SNOOZE_IS_ON = "in.basulabs.shakealarmclock" + - ".DEFAULT_SNOOZE_STATE"; + static final String SHARED_PREF_KEY_DEFAULT_SNOOZE_IS_ON = "in.basulabs.shakealarmclock.DEFAULT_SNOOZE_STATE"; /** * {@link android.content.SharedPreferences} key to store the default snooze interval in minutes. */ - static final String SHARED_PREF_KEY_DEFAULT_SNOOZE_INTERVAL = "in.basulabs.shakealarmclock" + - ".DEFAULT_SNOOZE_INTERVAL"; + 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"; + 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"; + static final String SHARED_PREF_KEY_DEFAULT_ALARM_TONE_URI = "in.basulabs.shakealarmclock.DEFAULT_ALARM_TONE_URI"; /** * {@link android.content.SharedPreferences} key to store the default alarm volume. */ - static final String SHARED_PREF_KEY_DEFAULT_ALARM_VOLUME = "in.basulabs.shakealarmclock" + - ".DEFAULT_ALARM_VOLUME"; + 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. + */ + static final int THEME_AUTO_TIME = 0; + + /** + * Indicates that the theme of the app should be light. Corresponds to {@link androidx.appcompat.app.AppCompatDelegate#MODE_NIGHT_NO}. + */ + static final int THEME_LIGHT = 1; + + /** + * Indicates that the theme of the app should be light. Corresponds to {@link androidx.appcompat.app.AppCompatDelegate#MODE_NIGHT_YES}. + */ + static final int THEME_DARK = 2; - static final int THEME_TIME = 0, THEME_LIGHT = 1, THEME_DARK = 2, THEME_SYSTEM = 3; + /** + * 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_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"; static final String SHARED_PREF_KEY_AUTO_SET_TONE = "in.basulabs.shakealarmclock.AUTO_SET_TONE"; + /** + * Unique name for work. + */ + static final String WORK_NAME_ACTIVATE_ALARMS = "in.basulabs.WORK_ACTIVATE_ALARMS"; + + /** + * 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. + */ + static void schedulePeriodicWork(Context context) { + + try { + WorkManager.initialize(context, new Configuration.Builder().setMinimumLoggingLevel(Log.DEBUG).build()); + } catch (Exception ignored) {} + + PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(Worker_ActivateAlarms.class, + 15, TimeUnit.MINUTES).build(); + + WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME_ACTIVATE_ALARMS, + ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest); + } + + /** + * Cancels a scheduled work using {@link WorkManager#cancelUniqueWork(String)}. + * + * @param context The {@link Context} that is requesting the work to be cancelled. + */ + static void cancelScheduledPeriodicWork(Context context) { + + try { + WorkManager.initialize(context, new Configuration.Builder().setMinimumLoggingLevel(Log.DEBUG).build()); + } catch (Exception ignored) {} + + WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME_ACTIVATE_ALARMS); + } + /** * 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_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) { @@ -288,12 +348,12 @@ static int getTheme(int theme) { } } - static void killServices(Context context, int alarmID){ - if (Service_RingAlarm.isThisServiceRunning && Service_RingAlarm.alarmID == alarmID){ + static void killServices(Context context, int alarmID) { + if (Service_RingAlarm.isThisServiceRunning && Service_RingAlarm.alarmID == alarmID) { Intent intent = new Intent(context, Service_RingAlarm.class); context.stopService(intent); } - if (Service_SnoozeAlarm.isThisServiceRunning && Service_SnoozeAlarm.alarmID == alarmID){ + if (Service_SnoozeAlarm.isThisServiceRunning && Service_SnoozeAlarm.alarmID == alarmID) { Intent intent = new Intent(context, Service_SnoozeAlarm.class); context.stopService(intent); } 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 4082b0f..b276389 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 @@ -83,15 +83,7 @@ public void onAttach(@NonNull Context context) { @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - //Log.e(this.getClass().getSimpleName(), "INSIDE ONCREATE()"); savedInstanceStateIsNull = savedInstanceState == null; - /*if (savedInstanceState == null) { - //Log.e(this.getClass().getSimpleName(), "Saved instance is null."); - savedInstanceStateIsNull = true; - } else { - //Log.e(this.getClass().getSimpleName(), "Saved instance NOT null."); - savedInstanceStateIsNull = false; - }*/ } //-------------------------------------------------------------------------------------------------- @@ -101,8 +93,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - //Log.e(this.getClass().getSimpleName(), "Inside onCreateView; setView = " + savedInstanceStateIsNull); - View view = inflater.inflate(R.layout.frag_alarm_details_main, container, false); viewModel = new ViewModelProvider(requireActivity()).get(ViewModel_AlarmDetails.class); @@ -305,8 +295,7 @@ private void displaySnoozeOptions() { * Updates {@link #currentRepeatOptionsTV}. */ private void displayRepeatOptions() { - /*Log.e(this.getClass().getSimpleName(), - "contents = " + Arrays.toString(viewModel.getRepeatDays().toArray()));*/ + if (viewModel.getIsRepeatOn()) { StringBuilder str = new StringBuilder(); for (int i = 0; i < viewModel.getRepeatDays().size(); i++) { @@ -359,7 +348,6 @@ private void setDate() { public void onClick(View view) { switch (view.getId()) { case R.id.saveButton: - //Log.e(this.getClass().toString(), "save button clicked."); saveButtonClicked(); break; case R.id.cancelButton: diff --git a/app/src/main/java/in/basulabs/shakealarmclock/MyApplication.java b/app/src/main/java/in/basulabs/shakealarmclock/MyApplication.java index 4e70d12..2cd9138 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/MyApplication.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/MyApplication.java @@ -19,7 +19,6 @@ public class MyApplication extends Application { @Override public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), Intent.ACTION_DATE_CHANGED)) { - //Log.e(this.getClass().getSimpleName(), "Received broadcast."); Activity_AlarmsList.onDateChanged(); } } @@ -31,8 +30,6 @@ public void onReceive(Context context, Intent intent) { public void onCreate() { super.onCreate(); - //Log.e(this.getClass().getSimpleName(), "Inside onCreate()........"); - IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_DATE_CHANGED); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/MyContentProvider.java b/app/src/main/java/in/basulabs/shakealarmclock/MyContentProvider.java new file mode 100644 index 0000000..3f05ff1 --- /dev/null +++ b/app/src/main/java/in/basulabs/shakealarmclock/MyContentProvider.java @@ -0,0 +1,47 @@ +package in.basulabs.shakealarmclock; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public abstract class MyContentProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, + @Nullable String[] selectionArgs, @Nullable String sortOrder) { + return null; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, + @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + return 0; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return null; + } +} diff --git a/app/src/main/java/in/basulabs/shakealarmclock/MyWorkManagerInitializer.java b/app/src/main/java/in/basulabs/shakealarmclock/MyWorkManagerInitializer.java new file mode 100644 index 0000000..5266dff --- /dev/null +++ b/app/src/main/java/in/basulabs/shakealarmclock/MyWorkManagerInitializer.java @@ -0,0 +1,20 @@ +package in.basulabs.shakealarmclock; + +import android.util.Log; + +import androidx.work.Configuration; +import androidx.work.WorkManager; + +import java.util.Objects; + +public class MyWorkManagerInitializer extends MyContentProvider { + + @Override + public boolean onCreate() { + WorkManager.initialize(Objects.requireNonNull(getContext()), + new Configuration.Builder() + .setMinimumLoggingLevel(Log.DEBUG) + .build()); + return true; + } +} diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Service_AlarmActivater.java b/app/src/main/java/in/basulabs/shakealarmclock/Service_AlarmActivater.java deleted file mode 100644 index ff77f32..0000000 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_AlarmActivater.java +++ /dev/null @@ -1,216 +0,0 @@ -package in.basulabs.shakealarmclock; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; - -import androidx.annotation.NonNull; - -import java.time.DayOfWeek; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.temporal.TemporalAdjusters; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import static android.os.SystemClock.elapsedRealtime; - -public class Service_AlarmActivater extends Service { - - private AlarmDatabase alarmDatabase; - - public static boolean isThisServiceRunning; - public static int pid = - 1; - - //----------------------------------------------------------------------------------------------------- - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - - //Log.e(this.getClass().getSimpleName(), "service started."); - isThisServiceRunning = true; - - Service_AlarmActivater obj = this; - pid = android.os.Process.myPid(); - - alarmDatabase = AlarmDatabase.getInstance(obj); - - AtomicReference> alarmEntityList = new AtomicReference<>(); - - Thread thread = new Thread( - () -> alarmEntityList.set(alarmDatabase.alarmDAO().getActiveAlarms())); - - if (! Service_RingAlarm.isThisServiceRunning && ! Service_SnoozeAlarm.isThisServiceRunning - && ! Service_UpdateAlarm.isThisServiceRunning) { - thread.start(); - try { - thread.join(); - - if (alarmEntityList.get().size() > 0) { - activateAlarmsIfInactive(alarmEntityList.get()); - } - } catch (InterruptedException ignored) { - } - } - - return START_STICKY; - } - - //------------------------------------------------------------------------------------------------------ - - @Override - public void onTaskRemoved(Intent rootIntent) { - super.onTaskRemoved(rootIntent); - - //Log.e(this.getClass().getSimpleName(), "onTaskRemoved() called."); - - Intent restartServiceIntent = new Intent(this, AlarmBroadcastReceiver.class); - restartServiceIntent.setAction(ConstantsAndStatics.ACTION_CREATE_BACKGROUND_SERVICE); - restartServiceIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); - - PendingIntent restartServicePendingIntent = PendingIntent.getBroadcast( - getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT); - - AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, elapsedRealtime() + 3000, - restartServicePendingIntent); - } - - //------------------------------------------------------------------------------------------------------ - - private void activateAlarmsIfInactive(@NonNull List list) { - - final AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); - final Service_AlarmActivater obj = this; - - for (AlarmEntity alarmEntity : list) { - - Thread thread = new Thread(() -> { - - ArrayList repeatDays = new ArrayList<>( - alarmDatabase.alarmDAO().getAlarmRepeatDays(alarmEntity.alarmID)); - - Intent intent = new Intent(obj, AlarmBroadcastReceiver.class); - intent.setAction(ConstantsAndStatics.ACTION_DELIVER_ALARM); - intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); - - Bundle data = alarmEntity.getAlarmDetailsInABundle(); - data.putIntegerArrayList(ConstantsAndStatics.BUNDLE_KEY_REPEAT_DAYS, repeatDays); - intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, data); - - PendingIntent pendingIntent = PendingIntent - .getBroadcast(obj, alarmEntity.alarmID, intent, - PendingIntent.FLAG_NO_CREATE); - - if (pendingIntent == null) { - //Log.e(this.getClass().getSimpleName(), "pending intent not found."); - - LocalDateTime alarmDateTime; - LocalDate alarmDate = LocalDate - .of(alarmEntity.alarmYear, alarmEntity.alarmMonth, - alarmEntity.alarmDay); - LocalTime alarmTime = LocalTime - .of(alarmEntity.alarmHour, alarmEntity.alarmMinutes); - - if (alarmEntity.isRepeatOn && repeatDays.size() > 0) { - - Collections.sort(repeatDays); - - alarmDateTime = LocalDateTime.of(LocalDate.now(), alarmTime); - int dayOfWeek = alarmDateTime.getDayOfWeek().getValue(); - - for (int i = 0; i < repeatDays.size(); i++) { - if (repeatDays.get(i) == dayOfWeek) { - if (alarmTime.isAfter(LocalTime.now())) { - // Alarm possible today, nothing more to do, break out of loop. - break; - } - } else if (repeatDays.get(i) > dayOfWeek) { - // There is a day available in the same week for the alarm to ring; select that day and - // break from loop. - alarmDateTime = - alarmDateTime.with(TemporalAdjusters - .next(DayOfWeek.of(repeatDays.get(i)))); - break; - } - if (i == repeatDays.size() - 1) { - // No day possible in this week. Select the first available date from next week. - alarmDateTime = alarmDateTime - .with(TemporalAdjusters - .next(DayOfWeek.of(repeatDays.get(0)))); - } - } - - } else { - alarmDateTime = LocalDateTime.of(alarmDate, alarmTime); - if (! alarmDateTime.isAfter(LocalDateTime.now())) { - alarmDateTime.plusDays(1); - } - } - - ZonedDateTime zonedDateTime = ZonedDateTime - .of(alarmDateTime, ZoneId.systemDefault()); - - /*Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.YEAR, alarmDateTime.getYear()); - calendar.set(Calendar.MONTH, alarmDateTime.getMonthValue() - 1); - calendar.set(Calendar.DAY_OF_MONTH, alarmDateTime.getDayOfMonth()); - calendar.set(Calendar.HOUR_OF_DAY, alarmDateTime.getHour()); - calendar.set(Calendar.MINUTE, alarmDateTime.getMinute()); - calendar.set(Calendar.SECOND, 0);*/ - - PendingIntent pendingIntent1 = PendingIntent - .getBroadcast(obj, alarmEntity.alarmID, intent, 0); - - alarmManager.setAlarmClock( - new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, - pendingIntent1), pendingIntent1); - - /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, - calendar.getTimeInMillis(), - pendingIntent1); - } else { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), - pendingIntent1); - }*/ - - } /*else { - //Log.e(this.getClass().getSimpleName(), "pending intent found."); - }*/ - - }); - thread.start(); - try { - thread.join(); - } catch (InterruptedException ignored) { - } - } - - - } - - //--------------------------------------------------------------------------------------------------- - - @Override - public void onDestroy() { - super.onDestroy(); - isThisServiceRunning = false; - pid = - 1; - } - - //---------------------------------------------------------------------------------------------------- - - @Override - public IBinder onBind(Intent intent) { - return null; - } -} 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 f307856..dd0d307 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Service_RingAlarm.java @@ -25,7 +25,6 @@ import android.os.CountDownTimer; import android.os.Handler; import android.os.IBinder; -import android.os.Process; import android.os.VibrationEffect; import android.os.Vibrator; @@ -78,8 +77,7 @@ public class Service_RingAlarm extends Service implements SensorEventListener { private boolean isShakeActive; - public static final String BUNDLE_KEY_NO_OF_TIMES_SNOOZED = "in.basulabs.shakealarmclock" + - ".NO_OF_TIMES_SNOOZED"; + public static final String BUNDLE_KEY_NO_OF_TIMES_SNOOZED = "in.basulabs.shakealarmclock.NO_OF_TIMES_SNOOZED"; //-------------------------------------------------------------------------------------------------- @@ -87,11 +85,8 @@ public class Service_RingAlarm extends Service implements SensorEventListener { @Override public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), ConstantsAndStatics.ACTION_SNOOZE_ALARM)) { - //Log.e(this.getClass().toString(), "Received broadcast to snooze alarm."); snoozeAlarm(); - } else if (Objects - .equals(intent.getAction(), ConstantsAndStatics.ACTION_CANCEL_ALARM)) { - //Log.e(this.getClass().toString(), "Received broadcast to cancel alarm."); + } else if (Objects.equals(intent.getAction(), ConstantsAndStatics.ACTION_CANCEL_ALARM)) { dismissAlarm(); } } @@ -101,46 +96,32 @@ public void onReceive(Context context, Intent intent) { @Override public int onStartCommand(Intent intent, int flags, int startId) { - //Log.e(this.getClass().toString(), "Inside onStartCommand"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - startForeground(NOTIFICATION_ID, buildRingNotification(), - ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); + startForeground(NOTIFICATION_ID, buildRingNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); } else { startForeground(NOTIFICATION_ID, buildRingNotification()); } isThisServiceRunning = true; - if (Service_AlarmActivater.isThisServiceRunning) { - Intent intent1 = new Intent(this, Service_AlarmActivater.class); - stopService(intent1); - } - if (Service_AlarmActivater.pid != - 1) { - Process.killProcess(Service_AlarmActivater.pid); - } + ConstantsAndStatics.cancelScheduledPeriodicWork(this); - alarmDetails = Objects.requireNonNull(intent.getExtras()) - .getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS); + alarmDetails = Objects.requireNonNull(intent.getExtras()).getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS); - numberOfTimesTheAlarmHasBeenSnoozed = intent.getExtras() - .getInt(BUNDLE_KEY_NO_OF_TIMES_SNOOZED, 0); + numberOfTimesTheAlarmHasBeenSnoozed = intent.getExtras().getInt(BUNDLE_KEY_NO_OF_TIMES_SNOOZED, 0); assert alarmDetails != null; - sharedPreferences = getSharedPreferences(ConstantsAndStatics.SHARED_PREF_FILE_NAME, - MODE_PRIVATE); + sharedPreferences = getSharedPreferences(ConstantsAndStatics.SHARED_PREF_FILE_NAME, MODE_PRIVATE); - isShakeActive = sharedPreferences - .getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SHAKE_OPERATION, - ConstantsAndStatics.SNOOZE) != ConstantsAndStatics.DO_NOTHING; + isShakeActive = sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SHAKE_OPERATION, + ConstantsAndStatics.SNOOZE) != ConstantsAndStatics.DO_NOTHING; assert alarmDetails != null; alarmToneUri = alarmDetails.getParcelable(ConstantsAndStatics.BUNDLE_KEY_ALARM_TONE_URI); - //Log.e(this.getClass().toString(), "Received Uri: " + alarmToneUri.toString()); alarmID = alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID); - //Log.e(this.getClass().getSimpleName(), "alarmID = " + alarmID); ringTimer = new CountDownTimer(60000, 1000) { @@ -183,7 +164,6 @@ public void onFinish() { @Override public void onDestroy() { super.onDestroy(); - //Log.e(this.getClass().getSimpleName(), "onDestroy() called."); isThisServiceRunning = false; try { ringTimer.cancel(); @@ -209,8 +189,7 @@ public void onDestroy() { private void initialiseShakeSensor() { if (isShakeActive) { Sensor accelerometer = snsMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - snsMgr.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI, - new Handler()); + snsMgr.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI, new Handler()); lastShakeTime = System.currentTimeMillis(); } } @@ -225,8 +204,7 @@ private void createNotificationChannel() { int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel channel = new NotificationChannel(Integer.toString(NOTIFICATION_ID), "in.basulabs.shakealarmclock Notifications", importance); - NotificationManager notificationManager = (NotificationManager) getSystemService( - NOTIFICATION_SERVICE); + NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); channel.setSound(null, null); assert notificationManager != null; notificationManager.createNotificationChannel(channel); @@ -236,11 +214,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(); @@ -272,13 +250,12 @@ private Notification buildRingNotification() { */ private void ringAlarm() { - //Log.e(this.getClass().getSimpleName(), "Ring Alarm called."); 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() @@ -297,8 +274,8 @@ 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(); @@ -323,9 +300,7 @@ private void alarmVibration() { if (vibrator.hasVibrator()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (vibrator.hasAmplitudeControl()) { - vibrator.vibrate( - VibrationEffect - .createWaveform(vibrationPattern, vibrationAmplitudes, 0)); + vibrator.vibrate(VibrationEffect.createWaveform(vibrationPattern, vibrationAmplitudes, 0)); } } else { vibrator.vibrate(vibrationPattern, 0); @@ -336,8 +311,8 @@ 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() { @@ -352,8 +327,7 @@ private void snoozeAlarm() { Intent intent = new Intent(this, Service_SnoozeAlarm.class); intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); - intent.putExtra(BUNDLE_KEY_NO_OF_TIMES_SNOOZED, - numberOfTimesTheAlarmHasBeenSnoozed); + intent.putExtra(BUNDLE_KEY_NO_OF_TIMES_SNOOZED, numberOfTimesTheAlarmHasBeenSnoozed); ContextCompat.startForegroundService(this, intent); stopSelf(); @@ -377,8 +351,7 @@ private void dismissAlarm() { Thread thread_toggleAlarm = new Thread( () -> alarmDatabase.alarmDAO() - .toggleAlarm(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), - 0)); + .toggleAlarm(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), 0)); ////////////////////////////////////////////////////// // If repeat is on, set another alarm. Otherwise @@ -391,9 +364,8 @@ private void dismissAlarm() { } catch (InterruptedException ignored) { } } else { - LocalTime alarmTime = LocalTime - .of(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_HOUR), - alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE)); + LocalTime alarmTime = LocalTime.of(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_HOUR), + alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE)); ArrayList repeatDays = alarmDetails .getIntegerArrayList(ConstantsAndStatics.BUNDLE_KEY_REPEAT_DAYS); @@ -413,18 +385,18 @@ private void dismissAlarm() { } else if (repeatDays.get(i) > dayOfWeek) { // There is a day available in the same week for the alarm to ring; select that day and // break from loop. - alarmDateTime = alarmDateTime - .with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(i)))); + alarmDateTime = alarmDateTime.with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(i)))); break; } if (i == repeatDays.size() - 1) { // No day possible in this week. Select the first available date from next week. - alarmDateTime = alarmDateTime - .with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(0)))); + alarmDateTime = alarmDateTime.with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(0)))); } } setAlarm(alarmDateTime); } + + ConstantsAndStatics.schedulePeriodicWork(this); stopSelf(); } @@ -432,13 +404,12 @@ private void dismissAlarm() { //-------------------------------------------------------------------------------------------------- /** - * Stops the ringing alarm. Also sends a broadcast to {@link Activity_RingAlarm} to finish - * itsef. + * Stops the ringing alarm. Also sends a broadcast to {@link Activity_RingAlarm} to finish itsef. */ private void stopRinging() { try { ringTimer.cancel(); - //snoozeTimer.cancel(); + if ((alarmDetails .getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_TYPE) == ConstantsAndStatics.ALARM_TYPE_VIBRATE_ONLY) || (alarmDetails .getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_TYPE) == ConstantsAndStatics.ALARM_TYPE_SOUND_AND_VIBRATE)) { @@ -469,12 +440,10 @@ private void setAlarm(LocalDateTime alarmDateTime) { PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmID, 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, - pendingIntent), pendingIntent); + new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, pendingIntent), pendingIntent); } //--------------------------------------------------------------------------------------------------- @@ -487,8 +456,7 @@ private void cancelPendingIntent() { intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); - PendingIntent pendingIntent = PendingIntent - .getBroadcast(this, alarmID, intent, PendingIntent.FLAG_NO_CREATE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmID, intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); @@ -516,20 +484,16 @@ public void onSensorChanged(SensorEvent event) { float gZ = z / SensorManager.GRAVITY_EARTH; float gForce = (float) Math.sqrt(gX * gX + gY * gY + gZ * gZ); - //Log.e(this.getClass().getSimpleName(), "gForce: " + gForce); // gForce will be close to 1 when there is no movement. if (gForce >= 3.8f) { long currTime = System.currentTimeMillis(); if (Math.abs(currTime - lastShakeTime) > MINIMUM_MILLIS_BETWEEN_SHAKES) { - //Log.e(this.getClass().getSimpleName(), "Event detected, "); lastShakeTime = currTime; shakeVibration(); - if (sharedPreferences - .getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SHAKE_OPERATION, - ConstantsAndStatics.SNOOZE) == ConstantsAndStatics.SNOOZE - && alarmDetails - .getBoolean(ConstantsAndStatics.BUNDLE_KEY_IS_SNOOZE_ON)) { + if (sharedPreferences.getInt(ConstantsAndStatics.SHARED_PREF_KEY_DEFAULT_SHAKE_OPERATION, + ConstantsAndStatics.SNOOZE) == ConstantsAndStatics.SNOOZE + && alarmDetails.getBoolean(ConstantsAndStatics.BUNDLE_KEY_IS_SNOOZE_ON)) { snoozeAlarm(); } else { dismissAlarm(); @@ -543,14 +507,12 @@ public void onSensorChanged(SensorEvent event) { //-------------------------------------------------------------------------------------------------- /** - * Creates a vibration for a small period of time, indicating that the app has registered a - * shake event. + * Creates a vibration for a small period of time, indicating that the app has registered a shake event. */ private void shakeVibration() { if (vibrator.hasVibrator()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - vibrator.vibrate( - VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE)); + vibrator.vibrate(VibrationEffect.createOneShot(200, VibrationEffect.DEFAULT_AMPLITUDE)); } else { vibrator.vibrate(200); } 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 ef4a12b..4a53746 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; @@ -66,21 +65,13 @@ public int onStartCommand(Intent intent, int flags, int startId) { } isThisServiceRunning = true; - if (Service_AlarmActivater.isThisServiceRunning) { - Intent intent1 = new Intent(this, Service_AlarmActivater.class); - stopService(intent1); - } - if (Service_AlarmActivater.pid != - 1) { - Process.killProcess(Service_AlarmActivater.pid); - } + ConstantsAndStatics.cancelScheduledPeriodicWork(this); Bundle data = Objects.requireNonNull(intent.getExtras()); alarmDetails = data.getBundle(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS); assert alarmDetails != null; alarmID = alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID); - //Log.e(this.getClass().getSimpleName(), "alarmID = " + alarmID); - numberOfTimesTheAlarmhasBeenSnoozed = intent.getExtras().getInt(Service_RingAlarm.BUNDLE_KEY_NO_OF_TIMES_SNOOZED); @@ -98,13 +89,12 @@ 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 + 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) { } @@ -113,8 +103,7 @@ public void onTick(long l) { 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); + intent1.putExtra(Service_RingAlarm.BUNDLE_KEY_NO_OF_TIMES_SNOOZED, numberOfTimesTheAlarmhasBeenSnoozed); ContextCompat.startForegroundService(myInstance, intent1); myInstance.stopSelf(); } @@ -178,10 +167,8 @@ private void dismissAlarm() { AlarmDatabase alarmDatabase = AlarmDatabase.getInstance(this); AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE); - 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)); ///////////////////////////////////// // Dismiss the snoozed alarm @@ -190,10 +177,8 @@ private void dismissAlarm() { intent.setAction(ConstantsAndStatics.ACTION_DELIVER_ALARM); intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); - PendingIntent pendingIntent = PendingIntent - .getBroadcast(this, alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), - intent, - PendingIntent.FLAG_NO_CREATE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_ID), + intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); @@ -210,12 +195,10 @@ private void dismissAlarm() { } catch (InterruptedException ignored) { } } else { - LocalTime alarmTime = LocalTime - .of(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_HOUR), + LocalTime alarmTime = LocalTime.of(alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_HOUR), alarmDetails.getInt(ConstantsAndStatics.BUNDLE_KEY_ALARM_MINUTE)); - ArrayList repeatDays = alarmDetails - .getIntegerArrayList(ConstantsAndStatics.BUNDLE_KEY_REPEAT_DAYS); + ArrayList repeatDays = alarmDetails.getIntegerArrayList(ConstantsAndStatics.BUNDLE_KEY_REPEAT_DAYS); assert repeatDays != null; Collections.sort(repeatDays); @@ -232,31 +215,26 @@ private void dismissAlarm() { } else if (repeatDays.get(i) > dayOfWeek) { // There is a day available in the same week for the alarm to ring; select that day and // break from loop. - alarmDateTime = alarmDateTime - .with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(i)))); + alarmDateTime = alarmDateTime.with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(i)))); break; } if (i == repeatDays.size() - 1) { // No day possible in this week. Select the first available date from next week. - alarmDateTime = alarmDateTime - .with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(0)))); + alarmDateTime = alarmDateTime.with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(0)))); } } 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()); + ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime.withSecond(0), ZoneId.systemDefault()); - alarmManager.setAlarmClock( - new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, + alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, pendingIntent2), pendingIntent2); } + ConstantsAndStatics.schedulePeriodicWork(this); stopSelf(); } @@ -271,8 +249,7 @@ private void cancelPendingIntent() { intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, alarmDetails); - PendingIntent pendingIntent = PendingIntent - .getBroadcast(this, alarmID, intent, PendingIntent.FLAG_NO_CREATE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(this, alarmID, intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Service_UpdateAlarm.java b/app/src/main/java/in/basulabs/shakealarmclock/Service_UpdateAlarm.java index 8932b61..8bc3ea2 100644 --- a/app/src/main/java/in/basulabs/shakealarmclock/Service_UpdateAlarm.java +++ b/app/src/main/java/in/basulabs/shakealarmclock/Service_UpdateAlarm.java @@ -42,33 +42,16 @@ public int onStartCommand(Intent intent, int flags, int startId) { startForeground(NOTIFICATION_ID, buildNotification()); isThisServiceRunning = true; + ConstantsAndStatics.cancelScheduledPeriodicWork(this); + alarmDatabase = AlarmDatabase.getInstance(this); ArrayList alarmEntityArrayList = getActiveAlarms(); - //Log.e(this.getClass().toString(), "Started."); - if (alarmEntityArrayList != null && alarmEntityArrayList.size() > 0) { - //Log.e(this.getClass().toString(), "We have active alarms."); - - try { - if (Service_AlarmActivater.isThisServiceRunning) { - Intent intent1 = new Intent(this, Service_AlarmActivater.class); - stopService(intent1); - } - if (Service_AlarmActivater.pid != - 1) { - android.os.Process.killProcess(Service_AlarmActivater.pid); - } - - } catch (Exception ignored) { - } - cancelActiveAlarms(alarmEntityArrayList); activateAlarms(alarmEntityArrayList); - - Intent intent1 = new Intent(this, Service_AlarmActivater.class); - startService(intent1); } stopSelf(); @@ -82,6 +65,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { public void onDestroy() { super.onDestroy(); isThisServiceRunning = false; + ConstantsAndStatics.schedulePeriodicWork(this); } @@ -95,8 +79,7 @@ private void createNotificationChannel() { int importance = NotificationManager.IMPORTANCE_HIGH; NotificationChannel channel = new NotificationChannel(Integer.toString(NOTIFICATION_ID), "in.basulabs.shakealarmclock Notification", importance); - NotificationManager notificationManager = (NotificationManager) getSystemService( - NOTIFICATION_SERVICE); + NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); channel.setSound(null, null); assert notificationManager != null; notificationManager.createNotificationChannel(channel); @@ -118,8 +101,8 @@ private Notification buildNotification() { Intent contentIntent = new Intent(this, Activity_AlarmsList.class); contentIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); contentIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - PendingIntent contentPendingIntent = PendingIntent - .getActivity(this, 5701, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent contentPendingIntent = PendingIntent.getActivity(this, 5701, + contentIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, Integer.toString(NOTIFICATION_ID)) @@ -143,8 +126,7 @@ private Notification buildNotification() { @Nullable private ArrayList getActiveAlarms() { - AtomicReference> alarmEntityArrayList = new AtomicReference<>( - new ArrayList<>()); + AtomicReference> alarmEntityArrayList = new AtomicReference<>(new ArrayList<>()); Thread thread = new Thread( () -> alarmEntityArrayList.set(new ArrayList<>(alarmDatabase.alarmDAO().getActiveAlarms()))); @@ -172,38 +154,18 @@ private void cancelActiveAlarms(@NonNull ArrayList alarmEntityArray for (AlarmEntity alarmEntity : alarmEntityArrayList) { - /*if (Service_RingAlarm.isThisServiceRunning) { - if (Service_RingAlarm.alarmHour == alarmEntity.alarmHour && Service_RingAlarm.alarmMinute == alarmEntity.alarmMinutes) { - Intent intent1 = new Intent(this, Service_RingAlarm.class); - stopService(intent1); - } - }*/ - ConstantsAndStatics.killServices(this, alarmEntity.alarmID); - /*LocalDateTime localDateTime = LocalDateTime.of(alarmEntity.alarmYear, alarmEntity.alarmMonth, - alarmEntity.alarmDay, alarmEntity.alarmHour, alarmEntity.alarmMinutes); - - ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, - ZoneId.of(sharedPreferences.getString(ConstantsAndStatics.SHARED_PREF_KEY_ZONE_ID, - ZoneId.systemDefault().getId()))); - - zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.systemDefault());*/ - Intent intent = new Intent(Service_UpdateAlarm.this, AlarmBroadcastReceiver.class); intent.setAction(ConstantsAndStatics.ACTION_DELIVER_ALARM); intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); - PendingIntent pendingIntent = PendingIntent - .getBroadcast(Service_UpdateAlarm.this, alarmEntity.alarmID, intent, - PendingIntent.FLAG_NO_CREATE); + PendingIntent pendingIntent = PendingIntent.getBroadcast(Service_UpdateAlarm.this, + alarmEntity.alarmID, intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); - //Log.e(this.getClass().toString(), "Alarm cancelled."); - } /*else { - //Log.e(this.getClass().toString(), "Alarm not found."); - }*/ + } } } @@ -214,9 +176,8 @@ private ArrayList getRepeatDays(int alarmID) { AtomicReference> repeatDays = new AtomicReference<>(); - Thread thread = - new Thread(() -> repeatDays - .set(new ArrayList<>(alarmDatabase.alarmDAO().getAlarmRepeatDays(alarmID)))); + Thread thread = new Thread( + () -> repeatDays.set(new ArrayList<>(alarmDatabase.alarmDAO().getAlarmRepeatDays(alarmID)))); thread.start(); try { thread.join(); @@ -238,8 +199,7 @@ private void activateAlarms(@NonNull ArrayList alarmEntityArrayList ArrayList repeatDays = getRepeatDays(alarmEntity.alarmID); LocalDateTime alarmDateTime; - LocalDate alarmDate = LocalDate.of(alarmEntity.alarmYear, alarmEntity.alarmMonth, - alarmEntity.alarmDay); + LocalDate alarmDate = LocalDate.of(alarmEntity.alarmYear, alarmEntity.alarmMonth, alarmEntity.alarmDay); LocalTime alarmTime = LocalTime.of(alarmEntity.alarmHour, alarmEntity.alarmMinutes); if (alarmEntity.isRepeatOn && repeatDays != null && repeatDays.size() > 0) { @@ -294,24 +254,8 @@ private void activateAlarms(@NonNull ArrayList alarmEntityArrayList ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime, ZoneId.systemDefault()); - /*Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.YEAR, alarmDateTime.getYear()); - calendar.set(Calendar.MONTH, alarmDateTime.getMonthValue() - 1); - calendar.set(Calendar.DAY_OF_MONTH, alarmDateTime.getDayOfMonth()); - calendar.set(Calendar.HOUR_OF_DAY, alarmDateTime.getHour()); - calendar.set(Calendar.MINUTE, alarmDateTime.getMinute()); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0);*/ - alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, pendingIntent), pendingIntent); - /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), - pendingIntent); - } else { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent); - }*/ - } diff --git a/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java b/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java new file mode 100644 index 0000000..c38472a --- /dev/null +++ b/app/src/main/java/in/basulabs/shakealarmclock/Worker_ActivateAlarms.java @@ -0,0 +1,145 @@ +package in.basulabs.shakealarmclock; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAdjusters; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +public class Worker_ActivateAlarms extends Worker { + + Context context; + + private boolean stopExecuting; + + //---------------------------------------------------------------------------------------------------------- + + public Worker_ActivateAlarms(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + this.context = context; + } + + //---------------------------------------------------------------------------------------------------------- + + @NonNull + @Override + public Result doWork() { + + stopExecuting = false; + activateAlarmsIfInactive(); + + return Result.success(); + } + + //---------------------------------------------------------------------------------------------------------- + + /** + * Activates the alarms that are ON, but inactive because {@link AlarmManager} has cancelled them for no reason. + */ + private void activateAlarmsIfInactive() { + + final AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + final AlarmDatabase alarmDatabase = AlarmDatabase.getInstance(context); + + List list = alarmDatabase.alarmDAO().getActiveAlarms(); + + if (list != null && list.size() > 0) { + + for (AlarmEntity alarmEntity : list) { + + AtomicReference> repeatDaysAtomic = new AtomicReference<>(); + + alarmDatabase.alarmDAO().getAlarmRepeatDays(alarmEntity.alarmID); + + ArrayList repeatDays = repeatDaysAtomic.get(); + + Intent intent = new Intent(context, AlarmBroadcastReceiver.class); + intent.setAction(ConstantsAndStatics.ACTION_DELIVER_ALARM); + intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); + + Bundle data = alarmEntity.getAlarmDetailsInABundle(); + data.putIntegerArrayList(ConstantsAndStatics.BUNDLE_KEY_REPEAT_DAYS, repeatDays); + intent.putExtra(ConstantsAndStatics.BUNDLE_KEY_ALARM_DETAILS, data); + + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, alarmEntity.alarmID, intent, + PendingIntent.FLAG_NO_CREATE); + + if (pendingIntent == null) { + + LocalDateTime alarmDateTime; + LocalDate alarmDate = LocalDate.of(alarmEntity.alarmYear, alarmEntity.alarmMonth, alarmEntity.alarmDay); + LocalTime alarmTime = LocalTime.of(alarmEntity.alarmHour, alarmEntity.alarmMinutes); + + if (alarmEntity.isRepeatOn && repeatDays.size() > 0) { + + Collections.sort(repeatDays); + + alarmDateTime = LocalDateTime.of(LocalDate.now(), alarmTime); + int dayOfWeek = alarmDateTime.getDayOfWeek().getValue(); + + for (int i = 0; i < repeatDays.size(); i++) { + if (repeatDays.get(i) == dayOfWeek) { + if (alarmTime.isAfter(LocalTime.now())) { + // Alarm possible today, nothing more to do, break out of loop. + break; + } + } else if (repeatDays.get(i) > dayOfWeek) { + // There is a day available in the same week for the alarm to ring; select that day and + // break from loop. + alarmDateTime = + alarmDateTime.with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(i)))); + break; + } + if (i == repeatDays.size() - 1) { + // No day possible in this week. Select the first available date from next week. + alarmDateTime = alarmDateTime.with(TemporalAdjusters.next(DayOfWeek.of(repeatDays.get(0)))); + } + } + + } else { + alarmDateTime = LocalDateTime.of(alarmDate, alarmTime); + if (! alarmDateTime.isAfter(LocalDateTime.now())) { + alarmDateTime.plusDays(1); + } + } + + ZonedDateTime zonedDateTime = ZonedDateTime.of(alarmDateTime, ZoneId.systemDefault()); + + PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, alarmEntity.alarmID, intent, 0); + + alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(zonedDateTime.toEpochSecond() * 1000, + pendingIntent1), pendingIntent1); + + } + + if (stopExecuting && ! isStopped()) { + break; + } + } + } + } + + //---------------------------------------------------------------------------------------------------- + + @Override + public void onStopped() { + super.onStopped(); + stopExecuting = true; + } +} diff --git a/build.gradle b/build.gradle index c64a238..b974aec 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,3 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() @@ -6,9 +5,6 @@ buildscript { } dependencies { classpath "com.android.tools.build:gradle:4.0.1" - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files } } @@ -17,11 +13,6 @@ allprojects { google() jcenter() } - /*gradle.projectsEvaluated { - tasks.withType(JavaCompile) { - options.compilerArgs << "-Xlint:deprecation" - } - }*/ } task clean(type: Delete) {