Implement action mode, select to delete
This commit is contained in:
parent
e1357ecd8f
commit
db3e0c9d07
15 changed files with 154 additions and 112 deletions
|
@ -67,7 +67,8 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:554dda71a4'
|
||||
// implementation 'com.github.SimpleMobileTools:Simple-Commons:554dda71a4'
|
||||
implementation project(":commons")
|
||||
implementation 'com.facebook.stetho:stetho:1.5.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'com.shawnlin:number-picker:2.4.6'
|
||||
|
|
|
@ -26,7 +26,7 @@ import org.greenrobot.eventbus.ThreadMode
|
|||
|
||||
class App : Application(), LifecycleObserver {
|
||||
|
||||
private var countDownTimers = mutableMapOf<Long, CountDownTimer>()
|
||||
private var countDownTimers = mutableMapOf<Int, CountDownTimer>()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
@ -108,7 +108,7 @@ class App : Application(), LifecycleObserver {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateTimerState(timerId: Long, state: TimerState) {
|
||||
private fun updateTimerState(timerId: Int, state: TimerState) {
|
||||
timerHelper.getTimer(timerId) { timer ->
|
||||
val newTimer = timer.copy(state = state)
|
||||
timerHelper.insertOrUpdateTimer(newTimer) {
|
||||
|
|
|
@ -104,7 +104,7 @@ class MainActivity : SimpleActivity() {
|
|||
val tabToOpen = intent.getIntExtra(OPEN_TAB, TAB_CLOCK)
|
||||
view_pager.setCurrentItem(tabToOpen, false)
|
||||
if (tabToOpen == TAB_TIMER) {
|
||||
val timerId = intent.getLongExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
val timerId = intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
(view_pager.adapter as ViewPagerAdapter).updateTimerPosition(timerId)
|
||||
}
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ class MainActivity : SimpleActivity() {
|
|||
val tabToOpen = intent.getIntExtra(OPEN_TAB, config.lastUsedViewPagerPage)
|
||||
intent.removeExtra(OPEN_TAB)
|
||||
if (tabToOpen == TAB_TIMER) {
|
||||
val timerId = intent.getLongExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
val timerId = intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
viewPagerAdapter.updateTimerPosition(timerId)
|
||||
}
|
||||
view_pager.currentItem = tabToOpen
|
||||
|
|
|
@ -16,7 +16,7 @@ class SplashActivity : BaseSplashActivity() {
|
|||
intent.extras?.containsKey(OPEN_TAB) == true -> {
|
||||
Intent(this, MainActivity::class.java).apply {
|
||||
putExtra(OPEN_TAB, intent.getIntExtra(OPEN_TAB, TAB_CLOCK))
|
||||
putExtra(TIMER_ID, intent.getLongExtra(TIMER_ID, INVALID_TIMER_ID))
|
||||
putExtra(TIMER_ID, intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID))
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
package com.simplemobiletools.clock.adapters
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.simplemobiletools.clock.R
|
||||
import com.simplemobiletools.clock.activities.SimpleActivity
|
||||
import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog
|
||||
import com.simplemobiletools.clock.extensions.*
|
||||
import com.simplemobiletools.clock.extensions.getFormattedDuration
|
||||
import com.simplemobiletools.clock.extensions.hideTimerNotification
|
||||
import com.simplemobiletools.clock.extensions.secondsToMillis
|
||||
import com.simplemobiletools.clock.extensions.timerHelper
|
||||
import com.simplemobiletools.clock.models.Timer
|
||||
import com.simplemobiletools.clock.models.TimerEvent
|
||||
import com.simplemobiletools.clock.models.TimerState
|
||||
import com.simplemobiletools.commons.adapters.MyRecyclerViewListAdapter
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
import com.simplemobiletools.commons.views.MyRecyclerView
|
||||
import kotlinx.android.synthetic.main.item_timer.view.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class TimerAdapter(
|
||||
private val activity: SimpleActivity,
|
||||
private val onRefresh: () -> Unit,
|
||||
private val onClick: (Timer) -> Unit,
|
||||
) : ListAdapter<Timer, TimerAdapter.TimerViewHolder>(diffUtil) {
|
||||
private val simpleActivity: SimpleActivity,
|
||||
recyclerView: MyRecyclerView,
|
||||
onRefresh: () -> Unit,
|
||||
private val onItemClick: (Timer) -> Unit,
|
||||
) : MyRecyclerViewListAdapter<Timer>(simpleActivity, recyclerView, diffUtil, null, onItemClick, onRefresh) {
|
||||
|
||||
companion object {
|
||||
private val diffUtil = object : DiffUtil.ItemCallback<Timer>() {
|
||||
|
@ -36,101 +40,130 @@ class TimerAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
private val config = activity.config
|
||||
private var textColor = config.textColor
|
||||
private var adjustedPrimaryColor = activity.getAdjustedPrimaryColor()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder {
|
||||
return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false))
|
||||
init {
|
||||
setupDragListener(true)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TimerViewHolder, position: Int) {
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
override fun getActionMenuId() = R.menu.cab_alarms
|
||||
|
||||
fun updateTextColor(textColor: Int) {
|
||||
this.textColor = textColor
|
||||
onRefresh.invoke()
|
||||
}
|
||||
override fun prepareActionMode(menu: Menu) {}
|
||||
|
||||
inner class TimerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
fun bind(timer: Timer) {
|
||||
itemView.apply {
|
||||
post {
|
||||
timer_play_pause.background = activity.resources.getColoredDrawableWithColor(R.drawable.circle_background_filled, adjustedPrimaryColor)
|
||||
timer_play_pause.applyColorFilter(if (adjustedPrimaryColor == Color.WHITE) Color.BLACK else Color.WHITE)
|
||||
timer_reset.applyColorFilter(textColor)
|
||||
timer_delete.applyColorFilter(textColor)
|
||||
}
|
||||
timer_label.setTextColor(textColor)
|
||||
timer_label.setHintTextColor(textColor.adjustAlpha(0.7f))
|
||||
//only update when different to prevent flickering and unnecessary updates
|
||||
if (timer_label.text.toString() != timer.label) {
|
||||
timer_label.setText(timer.label)
|
||||
}
|
||||
|
||||
|
||||
timer_time.setTextColor(textColor)
|
||||
timer_time.text = when (timer.state) {
|
||||
is TimerState.Finished -> 0.getFormattedDuration()
|
||||
is TimerState.Idle -> timer.seconds.getFormattedDuration()
|
||||
is TimerState.Paused -> timer.state.tick.getFormattedDuration()
|
||||
is TimerState.Running -> timer.state.tick.getFormattedDuration()
|
||||
}
|
||||
timer_time.setOnClickListener {
|
||||
changeDuration(timer)
|
||||
}
|
||||
|
||||
timer_delete.applyColorFilter(textColor)
|
||||
timer_delete.setOnClickListener {
|
||||
activity.timerHelper.deleteTimer(timer.id!!) {
|
||||
onRefresh.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
timer_reset.applyColorFilter(textColor)
|
||||
timer_reset.setOnClickListener {
|
||||
stopTimer(timer)
|
||||
}
|
||||
|
||||
|
||||
timer_play_pause.setOnClickListener {
|
||||
when (val state = timer.state) {
|
||||
is TimerState.Idle -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis))
|
||||
is TimerState.Paused -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, state.tick))
|
||||
is TimerState.Running -> EventBus.getDefault().post(TimerEvent.Pause(timer.id!!, state.tick))
|
||||
is TimerState.Finished -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis))
|
||||
}
|
||||
}
|
||||
|
||||
updateViewStates(timer.state)
|
||||
|
||||
setOnClickListener {
|
||||
onClick.invoke(timer)
|
||||
}
|
||||
}
|
||||
override fun actionItemPressed(id: Int) {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
private fun updateViewStates(state: TimerState) {
|
||||
when (id) {
|
||||
R.id.cab_delete -> deleteItems()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSelectableItemCount() = itemCount
|
||||
|
||||
override fun getIsItemSelectable(position: Int) = true
|
||||
|
||||
override fun getItemSelectionKey(position: Int) = getItem(position).id
|
||||
|
||||
override fun getItemKeyPosition(key: Int): Int {
|
||||
var position = -1
|
||||
for (i in 0 until itemCount) {
|
||||
if (key == getItem(i).id) {
|
||||
position = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return position
|
||||
}
|
||||
|
||||
|
||||
override fun onActionModeCreated() {}
|
||||
|
||||
override fun onActionModeDestroyed() {}
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = createViewHolder(R.layout.item_timer, parent)
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bindView(getItem(position), true, true) { itemView, _ ->
|
||||
setupView(itemView, getItem(position))
|
||||
}
|
||||
bindViewHolder(holder)
|
||||
}
|
||||
|
||||
private fun deleteItems() {
|
||||
val positions = getSelectedItemPositions()
|
||||
val timersToRemove = positions.map { position ->
|
||||
getItem(position)
|
||||
}
|
||||
removeSelectedItems(positions)
|
||||
activity.timerHelper.deleteTimers(timersToRemove) {
|
||||
onRefresh.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun setupView(view: View, timer: Timer) {
|
||||
view.apply {
|
||||
val isSelected = selectedKeys.contains(timer.id)
|
||||
timer_frame.isSelected = isSelected
|
||||
|
||||
timer_label.setTextColor(textColor)
|
||||
timer_label.setHintTextColor(textColor.adjustAlpha(0.7f))
|
||||
timer_label.text = timer.label
|
||||
|
||||
timer_time.setTextColor(textColor)
|
||||
timer_time.text = when (timer.state) {
|
||||
is TimerState.Finished -> 0.getFormattedDuration()
|
||||
is TimerState.Idle -> timer.seconds.getFormattedDuration()
|
||||
is TimerState.Paused -> timer.state.tick.getFormattedDuration()
|
||||
is TimerState.Running -> timer.state.tick.getFormattedDuration()
|
||||
}
|
||||
timer_time.setOnClickListener {
|
||||
changeDuration(timer)
|
||||
}
|
||||
|
||||
timer_delete.applyColorFilter(textColor)
|
||||
timer_delete.setOnClickListener {
|
||||
simpleActivity.timerHelper.deleteTimer(timer.id!!) {
|
||||
onRefresh.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
timer_reset.applyColorFilter(textColor)
|
||||
timer_reset.setOnClickListener {
|
||||
stopTimer(timer)
|
||||
}
|
||||
|
||||
timer_play_pause.background = simpleActivity.resources.getColoredDrawableWithColor(R.drawable.circle_background_filled, adjustedPrimaryColor)
|
||||
timer_play_pause.applyColorFilter(if (adjustedPrimaryColor == Color.WHITE) Color.BLACK else Color.WHITE)
|
||||
timer_play_pause.setOnClickListener {
|
||||
when (val state = timer.state) {
|
||||
is TimerState.Idle -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis))
|
||||
is TimerState.Paused -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, state.tick))
|
||||
is TimerState.Running -> EventBus.getDefault().post(TimerEvent.Pause(timer.id!!, state.tick))
|
||||
is TimerState.Finished -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis))
|
||||
}
|
||||
}
|
||||
|
||||
val state = timer.state
|
||||
val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished
|
||||
itemView.timer_reset.beInvisibleIf(!resetPossible)
|
||||
itemView.timer_delete.beInvisibleIf(!(!resetPossible && itemCount > 1))
|
||||
timer_reset.beInvisibleIf(!resetPossible)
|
||||
timer_delete.beInvisibleIf(!(!resetPossible && itemCount > 1))
|
||||
val drawableId = if (state is TimerState.Running) R.drawable.ic_pause_vector else R.drawable.ic_play_vector
|
||||
val iconColor = if (adjustedPrimaryColor == Color.WHITE) Color.BLACK else Color.WHITE
|
||||
itemView.timer_play_pause.setImageDrawable(activity.resources.getColoredDrawableWithColor(drawableId, iconColor))
|
||||
timer_play_pause.setImageDrawable(simpleActivity.resources.getColoredDrawableWithColor(drawableId, iconColor))
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeDuration(timer: Timer) {
|
||||
MyTimePickerDialogDialog(activity, timer.seconds) { seconds ->
|
||||
MyTimePickerDialogDialog(simpleActivity, timer.seconds) { seconds ->
|
||||
val timerSeconds = if (seconds <= 0) 10 else seconds
|
||||
updateTimer(timer.copy(seconds = timerSeconds))
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTimer(timer: Timer, refresh: Boolean = true) {
|
||||
activity.timerHelper.insertOrUpdateTimer(timer) {
|
||||
simpleActivity.timerHelper.insertOrUpdateTimer(timer) {
|
||||
if (refresh) {
|
||||
onRefresh.invoke()
|
||||
}
|
||||
|
@ -139,7 +172,7 @@ class TimerAdapter(
|
|||
|
||||
private fun stopTimer(timer: Timer) {
|
||||
EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis))
|
||||
activity.hideTimerNotification()
|
||||
simpleActivity.hideTimerNotification()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
|
|||
(fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound)
|
||||
}
|
||||
|
||||
fun updateTimerPosition(timerId: Long) {
|
||||
fun updateTimerPosition(timerId: Int) {
|
||||
(fragments[TAB_TIMER] as? TimerFragment)?.updatePosition(timerId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ fun Context.getOpenAlarmTabIntent(): PendingIntent {
|
|||
return PendingIntent.getActivity(this, OPEN_ALARMS_TAB_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
fun Context.getOpenTimerTabIntent(timerId: Long): PendingIntent {
|
||||
fun Context.getOpenTimerTabIntent(timerId: Int): PendingIntent {
|
||||
val intent = getLaunchIntent() ?: Intent(this, SplashActivity::class.java)
|
||||
intent.putExtra(OPEN_TAB, TAB_TIMER)
|
||||
intent.putExtra(TIMER_ID, timerId)
|
||||
|
|
|
@ -44,7 +44,7 @@ class TimerFragment : Fragment() {
|
|||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
view = (inflater.inflate(R.layout.fragment_timer, container, false) as ViewGroup).apply {
|
||||
timerAdapter = TimerAdapter(requireActivity() as SimpleActivity, ::refreshTimers, ::openEditTimer)
|
||||
timerAdapter = TimerAdapter(requireActivity() as SimpleActivity, timers_list, ::refreshTimers, ::openEditTimer)
|
||||
|
||||
storeStateVariables()
|
||||
|
||||
|
@ -105,7 +105,7 @@ class TimerFragment : Fragment() {
|
|||
currentEditAlarmDialog?.updateAlarmSound(alarmSound)
|
||||
}
|
||||
|
||||
fun updatePosition(timerId: Long) {
|
||||
fun updatePosition(timerId: Int) {
|
||||
activity?.timerHelper?.getTimers { timers ->
|
||||
val position = timers.indexOfFirst { it.id == timerId }
|
||||
if (position != INVALID_POSITION) {
|
||||
|
|
|
@ -47,7 +47,7 @@ const val TAB_ALARM = 1
|
|||
const val TAB_STOPWATCH = 2
|
||||
const val TAB_TIMER = 3
|
||||
const val TIMER_ID = "timer_id"
|
||||
const val INVALID_TIMER_ID = -1L
|
||||
const val INVALID_TIMER_ID = -1
|
||||
|
||||
// stopwatch sorting
|
||||
const val SORT_BY_LAP = 1
|
||||
|
|
|
@ -14,7 +14,7 @@ class TimerHelper(val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getTimer(timerId: Long, callback: (timer: Timer) -> Unit) {
|
||||
fun getTimer(timerId: Int, callback: (timer: Timer) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
callback.invoke(timerDao.getTimer(timerId))
|
||||
}
|
||||
|
@ -27,10 +27,17 @@ class TimerHelper(val context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun deleteTimer(id: Long, callback: () -> Unit = {}) {
|
||||
fun deleteTimer(id: Int, callback: () -> Unit = {}) {
|
||||
ensureBackgroundThread {
|
||||
timerDao.deleteTimer(id)
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteTimers(timers: List<Timer>, callback: () -> Unit = {}) {
|
||||
ensureBackgroundThread {
|
||||
timerDao.deleteTimers(timers)
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package com.simplemobiletools.clock.interfaces
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.*
|
||||
import com.simplemobiletools.clock.models.Timer
|
||||
|
||||
@Dao
|
||||
|
@ -13,11 +10,14 @@ interface TimerDao {
|
|||
fun getTimers(): List<Timer>
|
||||
|
||||
@Query("SELECT * FROM timers WHERE id=:id")
|
||||
fun getTimer(id: Long): Timer
|
||||
fun getTimer(id: Int): Timer
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrUpdateTimer(timer: Timer): Long
|
||||
|
||||
@Query("DELETE FROM timers WHERE id=:id")
|
||||
fun deleteTimer(id: Long)
|
||||
fun deleteTimer(id: Int)
|
||||
|
||||
@Delete
|
||||
fun deleteTimers(list: List<Timer>)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import androidx.room.PrimaryKey
|
|||
|
||||
@Entity(tableName = "timers")
|
||||
data class Timer(
|
||||
@PrimaryKey(autoGenerate = true) var id: Long?,
|
||||
@PrimaryKey(autoGenerate = true) var id: Int?,
|
||||
var seconds: Int,
|
||||
val state: TimerState,
|
||||
var vibrate: Boolean,
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package com.simplemobiletools.clock.models
|
||||
|
||||
sealed class TimerEvent(open val timerId: Long) {
|
||||
data class Reset(override val timerId: Long, val duration: Long) : TimerEvent(timerId)
|
||||
data class Start(override val timerId: Long, val duration: Long) : TimerEvent(timerId)
|
||||
data class Pause(override val timerId: Long, val duration: Long) : TimerEvent(timerId)
|
||||
data class Finish(override val timerId: Long, val duration: Long) : TimerEvent(timerId)
|
||||
data class Refresh(override val timerId: Long) : TimerEvent(timerId)
|
||||
sealed class TimerEvent(open val timerId: Int) {
|
||||
data class Reset(override val timerId: Int, val duration: Long) : TimerEvent(timerId)
|
||||
data class Start(override val timerId: Int, val duration: Long) : TimerEvent(timerId)
|
||||
data class Pause(override val timerId: Int, val duration: Long) : TimerEvent(timerId)
|
||||
data class Finish(override val timerId: Int, val duration: Long) : TimerEvent(timerId)
|
||||
data class Refresh(override val timerId: Int) : TimerEvent(timerId)
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@ class TimerService : Service() {
|
|||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private fun notification(title: String, contentText: String, firstRunningTimerId: Long): Notification {
|
||||
private fun notification(title: String, contentText: String, firstRunningTimerId: Int): Notification {
|
||||
val channelId = "simple_alarm_timer"
|
||||
val label = getString(R.string.timer)
|
||||
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/timer_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
|
|
Loading…
Reference in a new issue