Skip to content

Commit

Permalink
feat(alarm): Delete on Dismiss
Browse files Browse the repository at this point in the history
This implements #361

Reverted changes in the database layout, use DaysOfWeek(-1) instead
Added some tests
Moved the row below the repeat row
  • Loading branch information
yuriykulikov committed Sep 26, 2021
1 parent 2436957 commit 7d8d13c
Show file tree
Hide file tree
Showing 18 changed files with 165 additions and 80 deletions.
4 changes: 2 additions & 2 deletions .editorconfig
Expand Up @@ -2,5 +2,5 @@
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
max_line_length = 140
indent_size = 2
max_line_length = 100
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Expand Up @@ -115,6 +115,8 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

testOptions { unitTests.isReturnDefaultValues = true }
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
Expand Down
65 changes: 65 additions & 0 deletions app/src/androidTest/java/com/better/alarm/test/ListTest.kt
Expand Up @@ -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<AlarmValue>(R.id.list_fragment_list)
.filter(enabled())
.items()
.hasSize(1)

ListAsserts.assertThatList<AlarmValue>(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<AlarmValue>(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<AlarmValue>(R.id.list_fragment_list).items().hasSize(2)
}
}
3 changes: 2 additions & 1 deletion app/src/main/java/com/better/alarm/model/AlarmCore.kt
Expand Up @@ -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" }
Expand Down
6 changes: 1 addition & 5 deletions app/src/main/java/com/better/alarm/model/AlarmValue.kt
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
18 changes: 14 additions & 4 deletions app/src/main/java/com/better/alarm/model/DaysOfWeek.kt
Expand Up @@ -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
Expand All @@ -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 */
Expand All @@ -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 '_') +
Expand All @@ -66,6 +74,8 @@ data class DaysOfWeek(val coded: Int) {
}

companion object {
val deleteAfterDismiss = DaysOfWeek(-1)

private val DAY_MAP =
intArrayOf(
Calendar.MONDAY,
Expand Down
Expand Up @@ -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
Expand Down
10 changes: 1 addition & 9 deletions app/src/main/java/com/better/alarm/persistance/Columns.kt
Expand Up @@ -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"

Expand All @@ -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
Expand All @@ -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
}
}
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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)
}
}
}
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Expand Down
Expand Up @@ -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()
})
Expand Down Expand Up @@ -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" }
Expand Down
Expand Up @@ -39,7 +39,7 @@ fun DaysOfWeek.showDialog(context: Context): Single<DaysOfWeek> {
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 =
Expand Down
72 changes: 35 additions & 37 deletions app/src/main/res/layout/details_fragment_lower.xml
Expand Up @@ -49,6 +49,41 @@

</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/dividerHorizontal" />

<LinearLayout
android:id="@+id/details_delete_on_dismiss_row"
android:layout_width="match_parent"
android:layout_height="68dip"
android:orientation="horizontal">

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center|left"
android:text="@string/alarm_delete_on_dismiss"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary" />

<LinearLayout
android:layout_width="68dip"
android:layout_height="match_parent"
android:gravity="center">

<CheckBox
android:id="@+id/details_delete_on_dismiss_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:focusable="false" />
</LinearLayout>
</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="1dip"
Expand Down Expand Up @@ -115,43 +150,6 @@
</LinearLayout>
</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/dividerHorizontal" />

<LinearLayout
android:id="@+id/details_delete_on_dismiss_row"
android:layout_width="match_parent"
android:layout_height="68dip"
android:orientation="horizontal">

<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center|left"
android:text="@string/alarm_delete_on_dismiss"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="?android:attr/textAppearanceMedium" />

<LinearLayout
android:layout_width="68dip"
android:layout_height="match_parent"
android:gravity="center">

<CheckBox
android:id="@+id/details_delete_on_dismiss_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:clickable="false"
android:focusable="false" />

</LinearLayout>
</LinearLayout>


<View
android:layout_width="match_parent"
android:layout_height="1dip"
Expand Down

0 comments on commit 7d8d13c

Please sign in to comment.