diff --git a/.editorconfig b/.editorconfig index 9840e86fc..0b9e3adbc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,5 +2,5 @@ end_of_line = lf insert_final_newline = true indent_style = space -indent_size = 4 -max_line_length = 140 \ No newline at end of file +indent_size = 2 +max_line_length = 100 \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 43c9fef9f..78ec095cc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -115,6 +115,8 @@ android { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } + + testOptions { unitTests.isReturnDefaultValues = true } } tasks.withType().configureEach { diff --git a/app/src/androidTest/java/com/better/alarm/test/ListTest.kt b/app/src/androidTest/java/com/better/alarm/test/ListTest.kt index 07ca4e8a9..b4a449161 100644 --- a/app/src/androidTest/java/com/better/alarm/test/ListTest.kt +++ b/app/src/androidTest/java/com/better/alarm/test/ListTest.kt @@ -292,4 +292,69 @@ class ListTest : BaseTest() { .items() .isEmpty() } + + /** This is a test for https://github.com/yuriykulikov/AlarmClock/issues/361 */ + @Test + fun deleteOnDismiss() { + sleep() + onView(withId(R.id.fab)).perform(click()) + sleep() + onView(withText("Cancel")).perform(click()) + sleep() + + onView(withText("Delete after dismissed")).perform(click()) + onView(withText("OK")).perform(click()) + sleep() + sleep() + + assertThatList().items().hasSize(3) + + ListAsserts.assertThatList(R.id.list_fragment_list) + .filter(enabled()) + .items() + .hasSize(1) + + ListAsserts.assertThatList(R.id.list_fragment_list) + .filter { alarmValue -> alarmValue.daysOfWeek.isDeleteAfterDismiss } + .items() + .hasSize(1) + + onView(withText("Delete after dismissed")).check(matches(isDisplayed())) + + // when fired and dismissed + + val id = + ListAsserts.listObservable(R.id.list_fragment_list) + .filter { alarmValue -> alarmValue.daysOfWeek.isDeleteAfterDismiss } + .blockingFirst() + .id + + // simulate alarm fired + listActivity.scenario.onActivity { activity -> + activity.sendBroadcast( + Intent().apply { + action = AlarmSetter.ACTION_FIRED + setClass(activity, AlarmsReceiver::class.java) + putExtra(AlarmSetter.EXTRA_ID, id) + putExtra(AlarmSetter.EXTRA_TYPE, CalendarType.NORMAL.name) + }) + } + + Thread.sleep(1000) + + // simulate dismiss from the notification bar + listActivity.scenario.onActivity { activity -> + activity.sendBroadcast( + Intent().apply { + action = PresentationToModelIntents.ACTION_REQUEST_DISMISS + setClass(activity, AlarmsReceiver::class.java) + putExtra(AlarmSetter.EXTRA_ID, id) + }) + } + + Thread.sleep(1000) + + // alarm must be deleted from the list + ListAsserts.assertThatList(R.id.list_fragment_list).items().hasSize(2) + } } diff --git a/app/src/main/java/com/better/alarm/model/AlarmCore.kt b/app/src/main/java/com/better/alarm/model/AlarmCore.kt index dff2ad3c8..6b0afb75d 100644 --- a/app/src/main/java/com/better/alarm/model/AlarmCore.kt +++ b/app/src/main/java/com/better/alarm/model/AlarmCore.kt @@ -267,7 +267,8 @@ class AlarmCore( } else { stateMachine.transitionTo(normalSet) } - } else if (container.isDeleteOnDismiss) { + } else if (container.daysOfWeek.isDeleteAfterDismiss) { + log.debug { "Delete after dismiss!" } stateMachine.transitionTo(deletedState) } else { log.debug { "Repeating is not set, disabling the alarm" } diff --git a/app/src/main/java/com/better/alarm/model/AlarmValue.kt b/app/src/main/java/com/better/alarm/model/AlarmValue.kt index d4a5c28da..742900632 100644 --- a/app/src/main/java/com/better/alarm/model/AlarmValue.kt +++ b/app/src/main/java/com/better/alarm/model/AlarmValue.kt @@ -12,7 +12,6 @@ data class AlarmValue( val hour: Int, val minutes: Int, val isPrealarm: Boolean, - val isDeleteOnDismiss: Boolean, val alarmtone: Alarmtone, val isVibrate: Boolean, val label: String, @@ -58,13 +57,10 @@ data class AlarmValue( alarmtone = data.alarmtone, isVibrate = data.isVibrate, label = data.label, - daysOfWeek = data.daysOfWeek, - isDeleteOnDismiss = data.isDeleteOnDismiss) + daysOfWeek = data.daysOfWeek) fun withLabel(label: String) = copy(label = label) fun withHour(hour: Int) = copy(hour = hour) fun withDaysOfWeek(daysOfWeek: DaysOfWeek) = copy(daysOfWeek = daysOfWeek) fun withIsPrealarm(isPrealarm: Boolean) = copy(isPrealarm = isPrealarm) - fun withIsDeleteOnDismiss(isDeleteOnDismiss: Boolean) = - copy(isDeleteOnDismiss = isDeleteOnDismiss) } diff --git a/app/src/main/java/com/better/alarm/model/DaysOfWeek.kt b/app/src/main/java/com/better/alarm/model/DaysOfWeek.kt index dec1bab7d..b3c048a47 100644 --- a/app/src/main/java/com/better/alarm/model/DaysOfWeek.kt +++ b/app/src/main/java/com/better/alarm/model/DaysOfWeek.kt @@ -11,14 +11,18 @@ import java.util.Calendar * Sunday */ data class DaysOfWeek(val coded: Int) { + init { + require(coded == -1 || coded in 0..0x7f) { "Invalid coded value: $coded" } + } // Returns days of week encoded in an array of booleans. val booleanArray = BooleanArray(7) { index -> index.isSet() } - val isRepeatSet = coded != 0 + val isRepeatSet = coded > 0 + val isDeleteAfterDismiss: Boolean = coded == -1 fun toString(context: Context, showNever: Boolean): String { return when { - coded == 0 && showNever -> context.getText(R.string.never).toString() - coded == 0 -> "" + coded < 1 && showNever -> context.getText(R.string.never).toString() + coded < 1 -> "" // every day coded == 0x7f -> return context.getText(R.string.every_day).toString() // count selected days @@ -41,7 +45,10 @@ data class DaysOfWeek(val coded: Int) { } private fun Int.isSet(): Boolean { - return coded and (1 shl this) > 0 + return when (coded) { + -1 -> false + else -> coded and (1 shl this) > 0 + } } /** returns number of days from today until next alarm */ @@ -56,6 +63,7 @@ data class DaysOfWeek(val coded: Int) { } override fun toString(): String { + if (coded == -1) return "oneshot" return (if (0.isSet()) "m" else "_") + (if (1.isSet()) 't' else '_') + (if (2.isSet()) 'w' else '_') + @@ -66,6 +74,8 @@ data class DaysOfWeek(val coded: Int) { } companion object { + val deleteAfterDismiss = DaysOfWeek(-1) + private val DAY_MAP = intArrayOf( Calendar.MONDAY, diff --git a/app/src/main/java/com/better/alarm/persistance/AlarmDatabaseHelper.java b/app/src/main/java/com/better/alarm/persistance/AlarmDatabaseHelper.java index 9f73c79ec..ede1bc099 100644 --- a/app/src/main/java/com/better/alarm/persistance/AlarmDatabaseHelper.java +++ b/app/src/main/java/com/better/alarm/persistance/AlarmDatabaseHelper.java @@ -55,16 +55,15 @@ public void onCreate(SQLiteDatabase db) { + "message TEXT, " + "alert TEXT, " + "prealarm INTEGER, " - + "state STRING, " - + "deleteondismiss INTEGER);"); + + "state STRING);"); // @formatter:on // insert default alarms String insertMe = "INSERT INTO alarms " + "(hour, minutes, daysofweek, alarmtime, enabled, vibrate, " - + "message, alert, prealarm, state, deleteondismiss) VALUES "; - db.execSQL(insertMe + "(8, 30, 31, 0, 0, 1, '', '', 0, '', 0);"); - db.execSQL(insertMe + "(9, 00, 96, 0, 0, 1, '', '', 0, '', 0);"); + + "message, alert, prealarm, state) VALUES "; + db.execSQL(insertMe + "(8, 30, 31, 0, 0, 1, '', '', 0, '');"); + db.execSQL(insertMe + "(9, 00, 96, 0, 0, 1, '', '', 0, '');"); } @Override diff --git a/app/src/main/java/com/better/alarm/persistance/Columns.kt b/app/src/main/java/com/better/alarm/persistance/Columns.kt index 8be5cdd2d..ce19bc0b4 100644 --- a/app/src/main/java/com/better/alarm/persistance/Columns.kt +++ b/app/src/main/java/com/better/alarm/persistance/Columns.kt @@ -86,12 +86,6 @@ class Columns : BaseColumns { */ const val STATE = "state" - /** - * Delete on dismissal of alarm - * - * Type: BOOLEAN - */ - const val DELETE_ON_DISMISS = "deleteondismiss" /** The default sort order for this table */ const val DEFAULT_SORT_ORDER = "$HOUR, $MINUTES ASC" @@ -108,8 +102,7 @@ class Columns : BaseColumns { MESSAGE, ALERT, PREALARM, - STATE, - DELETE_ON_DISMISS) + STATE) /** * These save calls to cursor.getColumnIndexOrThrow() THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY @@ -126,6 +119,5 @@ class Columns : BaseColumns { const val ALARM_ALERT_INDEX = 8 const val ALARM_PREALARM_INDEX = 9 const val ALARM_STATE_INDEX = 10 - const val ALARM_DELETE_ON_DISMISS_INDEX = 11 } } diff --git a/app/src/main/java/com/better/alarm/persistance/PersistingContainerFactory.kt b/app/src/main/java/com/better/alarm/persistance/PersistingContainerFactory.kt index aff01b979..30a50b109 100644 --- a/app/src/main/java/com/better/alarm/persistance/PersistingContainerFactory.kt +++ b/app/src/main/java/com/better/alarm/persistance/PersistingContainerFactory.kt @@ -85,8 +85,7 @@ class PersistingContainerFactory(private val calendars: Calendars, private val m // ALARM_STATE_INDEX column state = c.getString(Columns.ALARM_STATE_INDEX) ?: (if (enabled) "NormalSetState" else "DisabledState"), - nextTime = calendars.now().apply { timeInMillis = c.getLong(Columns.ALARM_TIME_INDEX) }, - isDeleteOnDismiss = c.getInt(Columns.ALARM_DELETE_ON_DISMISS_INDEX) == 1) + nextTime = calendars.now().apply { timeInMillis = c.getLong(Columns.ALARM_TIME_INDEX) }) } companion object { @@ -106,8 +105,7 @@ class PersistingContainerFactory(private val calendars: Calendars, private val m label = "", alarmtone = Alarmtone.Default(), state = "", - nextTime = now, - isDeleteOnDismiss = false) + nextTime = now) // generate a new id val id = idMapper(defaultActiveRecord) @@ -128,7 +126,6 @@ class PersistingContainerFactory(private val calendars: Calendars, private val m put(Columns.PREALARM, isPrealarm) put(Columns.ALARM_TIME, nextTime.timeInMillis) put(Columns.STATE, state) - put(Columns.DELETE_ON_DISMISS, isDeleteOnDismiss) } } } diff --git a/app/src/main/java/com/better/alarm/presenter/AlarmDetailsFragment.kt b/app/src/main/java/com/better/alarm/presenter/AlarmDetailsFragment.kt index 7268be8db..821ebeff5 100644 --- a/app/src/main/java/com/better/alarm/presenter/AlarmDetailsFragment.kt +++ b/app/src/main/java/com/better/alarm/presenter/AlarmDetailsFragment.kt @@ -44,6 +44,7 @@ import com.better.alarm.logger.Logger import com.better.alarm.lollipop import com.better.alarm.model.AlarmValue import com.better.alarm.model.Alarmtone +import com.better.alarm.model.DaysOfWeek import com.better.alarm.model.ringtoneManagerString import com.better.alarm.util.Optional import com.better.alarm.util.modify @@ -188,12 +189,16 @@ class AlarmDetailsFragment : Fragment() { mDeleteOnDismissRow.setOnClickListener { modify("Delete on Dismiss") { value -> - value.copy(isDeleteOnDismiss = !value.isDeleteOnDismiss, isEnabled = true) + value.copy( + daysOfWeek = + if (value.daysOfWeek.isDeleteAfterDismiss) DaysOfWeek(0) + else DaysOfWeek.deleteAfterDismiss, + isEnabled = true) } } observeEditor { value -> - mDeleteOnDismissCheckBox.isChecked = value.isDeleteOnDismiss + mDeleteOnDismissCheckBox.isChecked = value.daysOfWeek.isDeleteAfterDismiss mDeleteOnDismissRow.visibility = if (value.daysOfWeek.isRepeatSet) View.GONE else View.VISIBLE } } diff --git a/app/src/main/java/com/better/alarm/presenter/AlarmsListActivity.kt b/app/src/main/java/com/better/alarm/presenter/AlarmsListActivity.kt index a3b22077d..dbd5d90ae 100644 --- a/app/src/main/java/com/better/alarm/presenter/AlarmsListActivity.kt +++ b/app/src/main/java/com/better/alarm/presenter/AlarmsListActivity.kt @@ -371,8 +371,7 @@ class AlarmsListActivity : AppCompatActivity() { label = savedInstanceState.getString("label") ?: "", isVibrate = true, state = savedInstanceState.getString("state") ?: "", - nextTime = Calendar.getInstance(), - isDeleteOnDismiss = savedInstanceState.getBoolean("isDeleteOnDismiss"))) + nextTime = Calendar.getInstance())) } else { Optional.absent() }) @@ -400,7 +399,6 @@ class AlarmsListActivity : AppCompatActivity() { putString("alarmtone", edited.alarmtone.persistedString) putBoolean("skipping", edited.skipping) putString("state", edited.state) - putBoolean("isDeleteOnDismiss", edited.isDeleteOnDismiss) } logger.debug { "Saved state $toWrite" } diff --git a/app/src/main/java/com/better/alarm/view/RepeatPreference.kt b/app/src/main/java/com/better/alarm/view/RepeatPreference.kt index 0ab98365e..6b8ab61f5 100644 --- a/app/src/main/java/com/better/alarm/view/RepeatPreference.kt +++ b/app/src/main/java/com/better/alarm/view/RepeatPreference.kt @@ -39,7 +39,7 @@ fun DaysOfWeek.showDialog(context: Context): Single { weekdays[Calendar.FRIDAY], weekdays[Calendar.SATURDAY], weekdays[Calendar.SUNDAY]) - var mutableDays = coded + var mutableDays = if (coded == -1) 0 else coded AlertDialog.Builder(context) .setMultiChoiceItems(entries, booleanArray) { _, which, isChecked -> mutableDays = diff --git a/app/src/main/res/layout/details_fragment_lower.xml b/app/src/main/res/layout/details_fragment_lower.xml index 608a5badb..4c0fd73bb 100644 --- a/app/src/main/res/layout/details_fragment_lower.xml +++ b/app/src/main/res/layout/details_fragment_lower.xml @@ -49,6 +49,41 @@ + + + + + + + + + + + + - - - - - - - - - - - - - - - Beim Verwerfen löschen + Nach Ausführung löschen Vorwecker Vorwecker-Dauer diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 572a52fc1..1658cd244 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -15,7 +15,7 @@ - Удалить при отклонении + Удалить после срабатывания Предварительный сигнал Длительность предварительного сигнала diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 94fb1639b..e657341b9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,7 +16,7 @@ - Delete on Dismiss + Delete after dismissed 1 10 diff --git a/app/src/test/java/com/better/alarm/AlarmCoreTest.kt b/app/src/test/java/com/better/alarm/AlarmCoreTest.kt index 20655141d..276e97820 100644 --- a/app/src/test/java/com/better/alarm/AlarmCoreTest.kt +++ b/app/src/test/java/com/better/alarm/AlarmCoreTest.kt @@ -313,4 +313,20 @@ class AlarmCoreTest { verify { stateNotifierMock.broadcastAlarmState(alarm.id, Intents.ALARM_SNOOZE_ACTION, any()) } assertThat(nextTimeInStoreWhenBroadcasting?.get(Calendar.MINUTE)).isEqualTo(15) } + + @Test + fun `Delete after dismiss alarm is deleted after dismiss`() { + val alarm = createAlarm() + act("Set on 1:00") { + alarm.edit { copy(daysOfWeek = DaysOfWeek.deleteAfterDismiss, isEnabled = true, hour = 1) } + } + act("Fired") { alarm.onAlarmFired() } + act("Dismiss") { alarm.dismiss() } + + verify { stateNotifierMock.broadcastAlarmState(alarm.id, Intents.ALARM_DISMISS_ACTION) } + // alarm was deleted + assertThat(store.alarms().blockingFirst().isEmpty()).isTrue() + // no other alarms is set + assertThat(alarmSetterMock.calendar).isNull() + } } diff --git a/app/src/test/java/com/better/alarm/model/DaysOfWeekTest.kt b/app/src/test/java/com/better/alarm/model/DaysOfWeekTest.kt index 5ced8f0cd..e0801412b 100644 --- a/app/src/test/java/com/better/alarm/model/DaysOfWeekTest.kt +++ b/app/src/test/java/com/better/alarm/model/DaysOfWeekTest.kt @@ -2,7 +2,7 @@ package com.better.alarm.model import android.content.Context import com.better.alarm.R -import java.util.Locale +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.Test import org.mockito.Mockito.mock @@ -13,7 +13,7 @@ class DaysOfWeekTest { init { `when`(context.getText(R.string.day_concat)).thenReturn(", ") - `when`(context.getText(R.string.never)).thenReturn("") + `when`(context.getText(R.string.never)).thenReturn("Never") } @Test @@ -42,5 +42,11 @@ class DaysOfWeekTest { .isEqualTo("Sat, Sun") } - @Test fun `getNextAlarm`() {} + @Test + fun `delete after dismiss is a valid value`() { + Locale.setDefault(Locale.ENGLISH) + assertThat(DaysOfWeek.deleteAfterDismiss.isDeleteAfterDismiss).isTrue() + assertThat(DaysOfWeek.deleteAfterDismiss.toString(context, false)).isEqualTo("") + assertThat(DaysOfWeek.deleteAfterDismiss.toString(context, true)).isEqualTo("Never") + } }