From 921ca92885e973935a1c9e6a8b1b63e386b9cc34 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Tue, 31 Aug 2021 21:18:45 +0100 Subject: [PATCH 01/32] Setup adding/removing multiple timers --- app/build.gradle | 3 + .../clock/adapters/TimerAdapter.kt | 144 +++++++++++++++ .../clock/adapters/ViewPagerAdapter.kt | 3 +- .../clock/databases/AppDatabase.kt | 38 ++++ .../clock/extensions/Context.kt | 4 + .../clock/fragments/TimerFragment.kt | 161 ++++++++--------- .../clock/helpers/Constants.kt | 6 +- .../clock/helpers/Converters.kt | 22 +++ .../clock/helpers/TimerHelper.kt | 51 ++++++ .../clock/interfaces/TimerDao.kt | 20 +++ .../simplemobiletools/clock/models/Timer.kt | 16 ++ app/src/main/res/drawable/ic_add_vector.xml | 11 ++ app/src/main/res/layout/fragment_timer.xml | 170 ++++++------------ app/src/main/res/layout/item_timer.xml | 111 ++++++++++++ 14 files changed, 554 insertions(+), 206 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt create mode 100644 app/src/main/res/drawable/ic_add_vector.xml create mode 100644 app/src/main/res/layout/item_timer.xml diff --git a/app/build.gradle b/app/build.gradle index d79e05d0..a49054bf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-kapt' def keystorePropertiesFile = rootProject.file("keystore.properties") def keystoreProperties = new Properties() @@ -76,4 +77,6 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'androidx.room:room-runtime:2.3.0' + kapt 'androidx.room:room-compiler:2.3.0' } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt new file mode 100644 index 00000000..d1ffce48 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -0,0 +1,144 @@ +package com.simplemobiletools.clock.adapters + +import android.media.AudioManager +import android.media.RingtoneManager +import android.view.LayoutInflater +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.helpers.PICK_AUDIO_FILE_INTENT_ID +import com.simplemobiletools.clock.models.Timer +import com.simplemobiletools.clock.models.TimerState +import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog +import com.simplemobiletools.commons.extensions.getDefaultAlarmSound +import com.simplemobiletools.commons.extensions.getFormattedDuration +import com.simplemobiletools.commons.extensions.onTextChangeListener +import com.simplemobiletools.commons.models.AlarmSound +import kotlinx.android.synthetic.main.item_timer.view.* +import org.greenrobot.eventbus.EventBus + +class TimerAdapter( + private val activity: SimpleActivity, + private val onRefresh: () -> Unit, + + ) : ListAdapter(diffUtil) { + + private val config = activity.config + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder { + return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false)) + } + + override fun onBindViewHolder(holder: TimerViewHolder, position: Int) { + holder.bind(getItem(position)) + } + + fun getItemAt(position: Int): Timer { + return getItem(position) + } + + inner class TimerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + fun bind(timer: Timer) { + itemView.apply { + timer_time.text = timer.seconds.getFormattedDuration() + timer_label.setText(timer.label) + + timer_time.text = timer.seconds.getFormattedDuration() + timer_label.setText(timer.label) + + val textColor = activity.config.textColor + + timer_initial_time.text = timer.seconds.getFormattedDuration() + timer_initial_time.colorLeftDrawable(textColor) + + timer_vibrate.isChecked = timer.vibrate + timer_vibrate.colorLeftDrawable(textColor) + + timer_sound.text = timer.soundTitle + timer_sound.colorLeftDrawable(textColor) + + timer_time.setOnClickListener { + stopTimer(timer) + } + + timer_time.setOnClickListener { + changeDuration(timer) + } + + timer_initial_time.setOnClickListener { + changeDuration(timer) + } + + timer_vibrate_holder.setOnClickListener { + timer_vibrate.toggle() + updateTimer(timer.copy(vibrate = timer_vibrate.isChecked), false) + } + + timer_sound.setOnClickListener { + SelectAlarmSoundDialog(activity, config.timerSoundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, + RingtoneManager.TYPE_ALARM, true, + onAlarmPicked = { sound -> + if (sound != null) { + updateAlarmSound(timer, sound) + } + }, + onAlarmSoundDeleted = { sound -> + if (timer.soundUri == sound.uri) { + val defaultAlarm = context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM) + updateAlarmSound(timer, defaultAlarm) + } + + context.checkAlarmsWithDeletedSoundUri(sound.uri) + }) + } + + timer_label.onTextChangeListener { text -> + updateTimer(timer.copy(label = text), false) + } + } + } + } + + private fun stopTimer(timer: Timer) { + EventBus.getDefault().post(TimerState.Idle) + activity.hideTimerNotification() + } + + private fun changeDuration(timer: Timer) { + MyTimePickerDialogDialog(activity, timer.seconds) { seconds -> + val timerSeconds = if (seconds <= 0) 10 else seconds + updateTimer(timer.copy(seconds = timerSeconds)) + } + } + + fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { + updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) + } + + private fun updateTimer(timer: Timer, refresh: Boolean = true) { + activity.timerHelper.insertOrUpdateTimer(timer) + if (refresh) { + onRefresh.invoke() + } + } + + + companion object { + val diffUtil = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: Timer, newItem: Timer): Boolean { + return oldItem == newItem + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt index c79be53e..6969b81d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt @@ -11,7 +11,6 @@ import com.simplemobiletools.clock.fragments.TimerFragment import com.simplemobiletools.clock.helpers.TABS_COUNT import com.simplemobiletools.clock.helpers.TAB_ALARM import com.simplemobiletools.clock.helpers.TAB_CLOCK -import com.simplemobiletools.clock.helpers.TAB_TIMER import com.simplemobiletools.commons.models.AlarmSound class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { @@ -51,6 +50,6 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { } fun updateTimerTabAlarmSound(alarmSound: AlarmSound) { - (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) +// (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt new file mode 100644 index 00000000..41f91466 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt @@ -0,0 +1,38 @@ +package com.simplemobiletools.clock.databases + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.simplemobiletools.clock.helpers.Converters +import com.simplemobiletools.clock.interfaces.TimerDao +import com.simplemobiletools.clock.models.Timer + +@Database(entities = [Timer::class], version = 1) +@TypeConverters(Converters::class) +abstract class AppDatabase : RoomDatabase() { + + abstract fun TimerDao(): TimerDao + + companion object { + private var db: AppDatabase? = null + + fun getInstance(context: Context): AppDatabase { + if (db == null) { + synchronized(AppDatabase::class) { + if (db == null) { + db = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app.db") + .build() + db!!.openHelper.setWriteAheadLoggingEnabled(true) + } + } + } + return db!! + } + + fun destroyInstance() { + db = null + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index 16dd29e9..5b178cec 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -20,7 +20,9 @@ import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.ReminderActivity import com.simplemobiletools.clock.activities.SnoozeReminderActivity import com.simplemobiletools.clock.activities.SplashActivity +import com.simplemobiletools.clock.databases.AppDatabase import com.simplemobiletools.clock.helpers.* +import com.simplemobiletools.clock.interfaces.TimerDao import com.simplemobiletools.clock.models.Alarm import com.simplemobiletools.clock.models.MyTimeZone import com.simplemobiletools.clock.receivers.AlarmReceiver @@ -39,6 +41,8 @@ import kotlin.math.pow val Context.config: Config get() = Config.newInstance(applicationContext) val Context.dbHelper: DBHelper get() = DBHelper.newInstance(applicationContext) +val Context.timerDb: TimerDao get() = AppDatabase.getInstance(applicationContext).TimerDao() +val Context.timerHelper: TimerHelper get() = TimerHelper(this) fun Context.getFormattedDate(calendar: Calendar): String { val dayOfWeek = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7 // make sure index 0 means monday diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index cac7c2c7..49241e48 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -1,31 +1,31 @@ package com.simplemobiletools.clock.fragments import android.graphics.Color -import android.media.AudioManager -import android.media.RingtoneManager import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.viewpager2.widget.ViewPager2 import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity +import com.simplemobiletools.clock.adapters.TimerAdapter import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog -import com.simplemobiletools.clock.extensions.* -import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID +import com.simplemobiletools.clock.extensions.config +import com.simplemobiletools.clock.extensions.hideTimerNotification +import com.simplemobiletools.clock.extensions.timerHelper +import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState -import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.models.AlarmSound import kotlinx.android.synthetic.main.fragment_timer.view.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import kotlin.math.roundToInt class TimerFragment : Fragment() { lateinit var view: ViewGroup + private lateinit var timerAdapter: TimerAdapter override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -39,91 +39,82 @@ 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 { - val config = requiredActivity.config - val textColor = config.textColor + timerAdapter = TimerAdapter(requireActivity() as SimpleActivity) { + refreshTimers() + } + timer_view_pager.adapter = timerAdapter + timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + updateViews(position) + } + }) - timer_time.text = config.timerSeconds.getFormattedDuration() - timer_label.setText(config.timerLabel) - - activity?.updateTextColors(timer_fragment) - timer_play_pause.background = resources.getColoredDrawableWithColor(R.drawable.circle_background_filled, requireContext().getAdjustedPrimaryColor()) - timer_play_pause.applyColorFilter(if (requireContext().getAdjustedPrimaryColor() == Color.WHITE) Color.BLACK else Color.WHITE) - timer_reset.applyColorFilter(textColor) - - timer_initial_time.text = config.timerSeconds.getFormattedDuration() - timer_initial_time.colorLeftDrawable(textColor) - - timer_vibrate.isChecked = config.timerVibrate - timer_vibrate.colorLeftDrawable(textColor) - - timer_sound.text = config.timerSoundTitle - timer_sound.colorLeftDrawable(textColor) - - timer_time.setOnClickListener { - stopTimer() + timer_add.setOnClickListener { + activity?.hideKeyboard(it) + activity?.timerHelper?.insertNewTimer { + refreshTimers(true) + } } - timer_play_pause.setOnClickListener { - val state = config.timerState + activity?.updateTextColors(timer_fragment) - when (state) { - is TimerState.Idle -> EventBus.getDefault().post(TimerState.Start(config.timerSeconds.secondsToMillis)) - is TimerState.Paused -> EventBus.getDefault().post(TimerState.Start(state.tick)) - is TimerState.Running -> EventBus.getDefault().post(TimerState.Pause(state.tick)) - is TimerState.Finished -> EventBus.getDefault().post(TimerState.Start(config.timerSeconds.secondsToMillis)) + val textColor = requireContext().config.textColor + timer_play_pause.background = + resources.getColoredDrawableWithColor(R.drawable.circle_background_filled, requireActivity().getAdjustedPrimaryColor()) + timer_play_pause.applyColorFilter(if (activity?.getAdjustedPrimaryColor() == Color.WHITE) Color.BLACK else Color.WHITE) + timer_reset.applyColorFilter(textColor) + + + timer_play_pause.setOnClickListener { + val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) + when (val state = timer.state) { +// is TimerState.Idle -> EventBus.getDefault().post(TimerState.Start(timer.seconds.secondsToMillis)) +// is TimerState.Paused -> EventBus.getDefault().post(TimerState.Start(state.tick)) +// is TimerState.Running -> EventBus.getDefault().post(TimerState.Pause(state.tick)) +// is TimerState.Finished -> EventBus.getDefault().post(TimerState.Start(timer.seconds.secondsToMillis)) else -> { } } } timer_reset.setOnClickListener { - stopTimer() + val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) + stopTimer(timer) } - timer_time.setOnClickListener { - changeDuration() + timer_delete.setOnClickListener { + val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) + activity?.timerHelper?.deleteTimer(timer.id!!) { + refreshTimers() + } } - timer_initial_time.setOnClickListener { - changeDuration() - } - - timer_vibrate_holder.setOnClickListener { - timer_vibrate.toggle() - config.timerVibrate = timer_vibrate.isChecked - config.timerChannelId = null - } - - timer_sound.setOnClickListener { - SelectAlarmSoundDialog(activity as SimpleActivity, config.timerSoundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, - RingtoneManager.TYPE_ALARM, true, - onAlarmPicked = { sound -> - if (sound != null) { - updateAlarmSound(sound) - } - }, - onAlarmSoundDeleted = { sound -> - if (config.timerSoundUri == sound.uri) { - val defaultAlarm = context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM) - updateAlarmSound(defaultAlarm) - } - - context.checkAlarmsWithDeletedSoundUri(sound.uri) - }) - } - - timer_label.onTextChangeListener { text -> - config.timerLabel = text - } + refreshTimers() } - return view } - private fun stopTimer() { + private fun updateViews(position: Int) { + val timer = timerAdapter.getItemAt(position) + //check if timer is running to update view + } + + private fun refreshTimers(scrollToLast: Boolean = false) { + activity?.timerHelper?.getTimers { timers -> + timerAdapter.submitList(timers) + activity?.runOnUiThread { + view.timer_delete.beVisibleIf(timers.size > 1) + if (scrollToLast) { + view.timer_view_pager.currentItem = timers.lastIndex + } + } + } + } + + private fun stopTimer(timer: Timer) { EventBus.getDefault().post(TimerState.Idle) activity?.hideTimerNotification() - view.timer_time.text = activity?.config?.timerSeconds?.getFormattedDuration() +// view.timer_time.text = activity?.config?.timerSeconds?.getFormattedDuration() } private fun changeDuration() { @@ -131,23 +122,23 @@ class TimerFragment : Fragment() { val timerSeconds = if (seconds <= 0) 10 else seconds activity?.config?.timerSeconds = timerSeconds val duration = timerSeconds.getFormattedDuration() - view.timer_initial_time.text = duration +// view.timer_initial_time.text = duration - if (view.timer_reset.isGone()) { - stopTimer() - } +// if (view.timer_reset.isGone()) { +// stopTimer() +// } } } @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(state: TimerState.Idle) { - view.timer_time.text = requiredActivity.config.timerSeconds.getFormattedDuration() +// view.timer_time.text = requiredActivity.config.timerSeconds.getFormattedDuration() updateViewStates(state) } @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(state: TimerState.Running) { - view.timer_time.text = state.tick.div(1000F).roundToInt().getFormattedDuration() +// view.timer_time.text = state.tick.div(1000F).roundToInt().getFormattedDuration() updateViewStates(state) } @@ -158,7 +149,7 @@ class TimerFragment : Fragment() { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(state: TimerState.Finished) { - view.timer_time.text = 0.getFormattedDuration() +// view.timer_time.text = 0.getFormattedDuration() updateViewStates(state) } @@ -180,10 +171,10 @@ class TimerFragment : Fragment() { view.timer_play_pause.setImageDrawable(resources.getColoredDrawableWithColor(drawableId, iconColor)) } - - fun updateAlarmSound(alarmSound: AlarmSound) { - activity?.config?.timerSoundTitle = alarmSound.title - activity?.config?.timerSoundUri = alarmSound.uri - view.timer_sound.text = alarmSound.title - } +// +// fun updateAlarmSound(alarmSound: AlarmSound) { +// activity?.config?.timerSoundTitle = alarmSound.title +// activity?.config?.timerSoundUri = alarmSound.uri +// view.timer_sound.text = alarmSound.title +// } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index 7709ab9e..19ba8266 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -1,7 +1,9 @@ package com.simplemobiletools.clock.helpers import com.simplemobiletools.clock.models.MyTimeZone -import java.util.* +import java.util.Calendar +import java.util.Date +import java.util.TimeZone import kotlin.math.pow // shared preferences @@ -56,6 +58,8 @@ const val SORT_BY_ALARM_TIME = 1 const val TODAY_BIT = -1 const val TOMORROW_BIT = -2 +const val DEFAULT_TIME = 300 + fun getDefaultTimeZoneTitle(id: Int) = getAllTimeZones().firstOrNull { it.id == id }?.title ?: "" fun getMSTillNextMinute(): Long { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt new file mode 100644 index 00000000..499733ec --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt @@ -0,0 +1,22 @@ +package com.simplemobiletools.clock.helpers + +import androidx.room.TypeConverter +import com.google.gson.Gson +import com.simplemobiletools.clock.models.StateWrapper +import com.simplemobiletools.clock.models.TimerState + +class Converters { + private val gson = Gson() + + @TypeConverter + fun jsonToTimerState(value: String): TimerState { + return try { + gson.fromJson(value, StateWrapper::class.java).state + } catch (e: Exception) { + TimerState.Idle + } + } + + @TypeConverter + fun timerStateToJson(state: TimerState) = gson.toJson(StateWrapper(state)) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt new file mode 100644 index 00000000..29b84d47 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -0,0 +1,51 @@ +package com.simplemobiletools.clock.helpers + +import android.content.Context +import android.media.RingtoneManager +import com.simplemobiletools.clock.extensions.timerDb +import com.simplemobiletools.clock.models.Timer +import com.simplemobiletools.clock.models.TimerState +import com.simplemobiletools.commons.extensions.getDefaultAlarmSound +import com.simplemobiletools.commons.extensions.getDefaultAlarmTitle +import com.simplemobiletools.commons.helpers.ensureBackgroundThread + +class TimerHelper(val context: Context) { + private val timerDao = context.timerDb + + fun getTimers(callback: (notes: List) -> Unit) { + ensureBackgroundThread { + callback.invoke(timerDao.getTimers()) + } + } + + fun insertOrUpdateTimer(timer: Timer, callback: () -> Unit = {}) { + ensureBackgroundThread { + timerDao.insertOrUpdateTimer(timer) + callback.invoke() + } + } + + fun deleteTimer(id: Long, callback: () -> Unit = {}) { + ensureBackgroundThread { + timerDao.deleteTimer(id) + callback.invoke() + } + } + + fun insertNewTimer(callback: () -> Unit = {}) { + ensureBackgroundThread { + timerDao.insertOrUpdateTimer( + Timer(id = null, + seconds = DEFAULT_TIME, + TimerState.Idle, + false, + context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM).uri, + context.getDefaultAlarmTitle(RingtoneManager.TYPE_ALARM), + "", + DEFAULT_MAX_TIMER_REMINDER_SECS.toString()) + ) + + callback.invoke() + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt new file mode 100644 index 00000000..d5595aec --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt @@ -0,0 +1,20 @@ +package com.simplemobiletools.clock.interfaces + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.simplemobiletools.clock.models.Timer + +@Dao +interface TimerDao { + + @Query("SELECT * FROM timers") + fun getTimers(): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertOrUpdateTimer(timer: Timer): Long + + @Query("DELETE FROM timers WHERE id=:id") + fun deleteTimer(id: Long) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt new file mode 100644 index 00000000..b93b2ce4 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt @@ -0,0 +1,16 @@ +package com.simplemobiletools.clock.models + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "timers") +data class Timer( + @PrimaryKey(autoGenerate = true) val id: Long?, + val seconds: Int, + val state: TimerState, + val vibrate: Boolean, + val soundUri: String, + val soundTitle: String, + val label: String, + val maxReminderSecs: String, +) diff --git a/app/src/main/res/drawable/ic_add_vector.xml b/app/src/main/res/drawable/ic_add_vector.xml new file mode 100644 index 00000000..ccd06b3c --- /dev/null +++ b/app/src/main/res/drawable/ic_add_vector.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml index b16ee540..87297742 100644 --- a/app/src/main/res/layout/fragment_timer.xml +++ b/app/src/main/res/layout/fragment_timer.xml @@ -6,127 +6,16 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toTopOf="parent" /> + + + + + + + + + diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml new file mode 100644 index 00000000..1f0cf8ac --- /dev/null +++ b/app/src/main/res/layout/item_timer.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8474e6a800c311f4350d625a22d9a61c59bddb43 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 1 Sep 2021 19:58:38 +0100 Subject: [PATCH 02/32] handle multiple timer states - feat: separate timer actions from states by adding a new class TimerEvent to hold actions Reset, Start, Pause, Finish, Refesh that could be performed on a timer. - feat: handle multiple countdown timers in the App file by creating a map of the timer id to the countdown timer. - fix: use gson instance from TypeAdapter in Room's Converters class - ref: remove scroll view parent from each timer item, a fix for the keyboard obscuring a label will be implemented in a future commit --- .../kotlin/com/simplemobiletools/clock/App.kt | 85 +++++--- .../clock/adapters/TimerAdapter.kt | 79 +++++--- .../clock/adapters/ViewPagerAdapter.kt | 3 +- .../clock/extensions/Context.kt | 12 +- .../clock/extensions/gson/TypeAdapter.kt | 3 - .../clock/fragments/TimerFragment.kt | 91 ++++----- .../clock/helpers/Constants.kt | 1 + .../clock/helpers/Converters.kt | 3 +- .../clock/helpers/TimerHelper.kt | 16 +- .../clock/interfaces/TimerDao.kt | 3 + .../clock/models/TimerEvent.kt | 9 + .../clock/models/TimerState.kt | 3 - .../clock/services/TimerService.kt | 2 +- app/src/main/res/layout/fragment_timer.xml | 2 +- app/src/main/res/layout/item_timer.xml | 186 +++++++++--------- 15 files changed, 266 insertions(+), 232 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index abdd5b92..9cc7c842 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -5,16 +5,18 @@ import android.app.NotificationManager import android.content.Context import android.os.Build import android.os.CountDownTimer +import android.util.Log import androidx.annotation.RequiresApi import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import com.facebook.stetho.Stetho -import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.getOpenTimerTabIntent import com.simplemobiletools.clock.extensions.getTimerNotification +import com.simplemobiletools.clock.extensions.timerHelper import com.simplemobiletools.clock.helpers.TIMER_NOTIF_ID +import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.clock.services.TimerStopService import com.simplemobiletools.clock.services.startTimerService @@ -25,8 +27,7 @@ import org.greenrobot.eventbus.ThreadMode class App : Application(), LifecycleObserver { - private var timer: CountDownTimer? = null - private var lastTick = 0L + private var timers = mutableMapOf() override fun onCreate() { super.onCreate() @@ -48,63 +49,83 @@ class App : Application(), LifecycleObserver { @RequiresApi(Build.VERSION_CODES.O) @OnLifecycleEvent(Lifecycle.Event.ON_STOP) private fun onAppBackgrounded() { - if (config.timerState is TimerState.Running) { - startTimerService(this) + timerHelper.getTimers { timers -> + if (timers.any { it.state is TimerState.Running }) { + startTimerService(this) + } } } @OnLifecycleEvent(Lifecycle.Event.ON_START) private fun onAppForegrounded() { EventBus.getDefault().post(TimerStopService) + timerHelper.getTimers { timers -> + val runningTimers = timers.filter { it.state is TimerState.Running } + runningTimers.forEach { timer -> + EventBus.getDefault().post(TimerEvent.Start(timer.id!!, (timer.state as TimerState.Running).tick)) + } + } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Idle) { - config.timerState = state - timer?.cancel() + fun onMessageEvent(event: TimerEvent.Reset) { + Log.w(TAG, "onMessageEvent: $event") + updateTimerState(event.timerId, TimerState.Idle) + timers[event.timerId]?.cancel() } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Start) { - timer = object : CountDownTimer(state.duration, 1000) { + fun onMessageEvent(event: TimerEvent.Start) { + Log.w(TAG, "onMessageEvent: $event") + val countDownTimer = object : CountDownTimer(event.duration, 1000) { override fun onTick(tick: Long) { - lastTick = tick - - val newState = TimerState.Running(state.duration, tick) - EventBus.getDefault().post(newState) - config.timerState = newState + Log.d(TAG, "onMessageEvent--> $tick") + updateTimerState(event.timerId, TimerState.Running(event.duration, tick)) } override fun onFinish() { - EventBus.getDefault().post(TimerState.Finish(state.duration)) + EventBus.getDefault().post(TimerEvent.Finish(event.timerId, event.duration)) EventBus.getDefault().post(TimerStopService) } }.start() + timers[event.timerId] = countDownTimer } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(event: TimerState.Finish) { - val pendingIntent = getOpenTimerTabIntent() - val notification = getTimerNotification(pendingIntent, false) - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(TIMER_NOTIF_ID, notification) - - EventBus.getDefault().post(TimerState.Finished) + fun onMessageEvent(event: TimerEvent.Finish) { + Log.w(TAG, "onMessageEvent: $event") + timerHelper.getTimer(event.timerId) { timer -> + val pendingIntent = getOpenTimerTabIntent(event.timerId) + val notification = getTimerNotification(timer, pendingIntent, false) + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.notify(TIMER_NOTIF_ID, notification) + updateTimerState(event.timerId, TimerState.Finished) + } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Finished) { - config.timerState = state + fun onMessageEvent(event: TimerEvent.Pause) { + Log.w(TAG, "onMessageEvent: $event") + timerHelper.getTimer(event.timerId) { timer -> + updateTimerState(event.timerId, TimerState.Paused(event.duration, (timer.state as TimerState.Running).tick)) + timers[event.timerId]?.cancel() + } } - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(event: TimerState.Pause) { - EventBus.getDefault().post(TimerState.Paused(event.duration, lastTick)) + private fun updateTimerState(timerId: Long, state: TimerState) { + timerHelper.getTimer(timerId) { timer -> + val newTimer = timer.copy(state = state) + Log.w(TAG, "updateTimerState: $newTimer") + timerHelper.insertOrUpdateTimer(newTimer) { + EventBus.getDefault().post(TimerEvent.Refresh(timerId)) + timerHelper.getTimer(timerId) { + Log.e(TAG, "updated timer $it") + } + } + } } - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Paused) { - config.timerState = state - timer?.cancel() + companion object { + private const val TAG = "App" } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index d1ffce48..899bf347 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -2,6 +2,7 @@ package com.simplemobiletools.clock.adapters import android.media.AudioManager import android.media.RingtoneManager +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,7 +12,10 @@ 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.checkAlarmsWithDeletedSoundUri +import com.simplemobiletools.clock.extensions.colorLeftDrawable +import com.simplemobiletools.clock.extensions.config +import com.simplemobiletools.clock.extensions.timerHelper import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState @@ -20,16 +24,13 @@ import com.simplemobiletools.commons.extensions.getDefaultAlarmSound import com.simplemobiletools.commons.extensions.getFormattedDuration import com.simplemobiletools.commons.extensions.onTextChangeListener import com.simplemobiletools.commons.models.AlarmSound +import kotlin.math.roundToInt import kotlinx.android.synthetic.main.item_timer.view.* -import org.greenrobot.eventbus.EventBus class TimerAdapter( private val activity: SimpleActivity, private val onRefresh: () -> Unit, - - ) : ListAdapter(diffUtil) { - - private val config = activity.config +) : ListAdapter(diffUtil) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder { return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false)) @@ -45,28 +46,32 @@ class TimerAdapter( inner class TimerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + init { + itemView.timer_label.onTextChangeListener { text -> + Log.w(TAG, "timer_label") + updateTimer(getItemAt(adapterPosition).copy(label = text), false) + } + + itemView.post { + val textColor = activity.config.textColor + itemView.timer_initial_time.colorLeftDrawable(textColor) + itemView.timer_vibrate.colorLeftDrawable(textColor) + itemView.timer_sound.colorLeftDrawable(textColor) + } + } + fun bind(timer: Timer) { itemView.apply { - timer_time.text = timer.seconds.getFormattedDuration() - timer_label.setText(timer.label) - - timer_time.text = timer.seconds.getFormattedDuration() - timer_label.setText(timer.label) - - val textColor = activity.config.textColor + //only update when different to prevent flickering and unnecessary updates + if (timer_label.text.toString() != timer.label) { + timer_label.setText(timer.label) + } timer_initial_time.text = timer.seconds.getFormattedDuration() - timer_initial_time.colorLeftDrawable(textColor) timer_vibrate.isChecked = timer.vibrate - timer_vibrate.colorLeftDrawable(textColor) timer_sound.text = timer.soundTitle - timer_sound.colorLeftDrawable(textColor) - - timer_time.setOnClickListener { - stopTimer(timer) - } timer_time.setOnClickListener { changeDuration(timer) @@ -77,12 +82,13 @@ class TimerAdapter( } timer_vibrate_holder.setOnClickListener { + Log.w(TAG, "toggle") timer_vibrate.toggle() updateTimer(timer.copy(vibrate = timer_vibrate.isChecked), false) } timer_sound.setOnClickListener { - SelectAlarmSoundDialog(activity, config.timerSoundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, + SelectAlarmSoundDialog(activity, timer.soundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, RingtoneManager.TYPE_ALARM, true, onAlarmPicked = { sound -> if (sound != null) { @@ -99,39 +105,52 @@ class TimerAdapter( }) } - timer_label.onTextChangeListener { text -> - updateTimer(timer.copy(label = text), false) + + when (timer.state) { + is TimerState.Finished -> { + timer_time.text = 0.getFormattedDuration() + } + + is TimerState.Idle -> { + timer_time.text = timer.seconds.getFormattedDuration() + } + + is TimerState.Paused -> { + timer_time.text = timer.state.tick.div(1000F).roundToInt().getFormattedDuration() + } + + is TimerState.Running -> { + timer_time.text = timer.state.tick.div(1000F).roundToInt().getFormattedDuration() + } } } } } - private fun stopTimer(timer: Timer) { - EventBus.getDefault().post(TimerState.Idle) - activity.hideTimerNotification() - } - private fun changeDuration(timer: Timer) { MyTimePickerDialogDialog(activity, timer.seconds) { seconds -> val timerSeconds = if (seconds <= 0) 10 else seconds + Log.w(TAG, "changeDuration") updateTimer(timer.copy(seconds = timerSeconds)) } } fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { + Log.w(TAG, "updateAlarmSound: $timer") updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) } private fun updateTimer(timer: Timer, refresh: Boolean = true) { + Log.w(TAG, "updateTimer: $timer") activity.timerHelper.insertOrUpdateTimer(timer) if (refresh) { onRefresh.invoke() } } - companion object { - val diffUtil = object : DiffUtil.ItemCallback() { + private const val TAG = "TimerAdapter" + private val diffUtil = object : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean { return oldItem.id == newItem.id } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt index 6969b81d..c79be53e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt @@ -11,6 +11,7 @@ import com.simplemobiletools.clock.fragments.TimerFragment import com.simplemobiletools.clock.helpers.TABS_COUNT import com.simplemobiletools.clock.helpers.TAB_ALARM import com.simplemobiletools.clock.helpers.TAB_CLOCK +import com.simplemobiletools.clock.helpers.TAB_TIMER import com.simplemobiletools.commons.models.AlarmSound class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { @@ -50,6 +51,6 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { } fun updateTimerTabAlarmSound(alarmSound: AlarmSound) { -// (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) + (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index 5b178cec..d17da18d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -25,6 +25,7 @@ import com.simplemobiletools.clock.helpers.* import com.simplemobiletools.clock.interfaces.TimerDao import com.simplemobiletools.clock.models.Alarm import com.simplemobiletools.clock.models.MyTimeZone +import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.receivers.AlarmReceiver import com.simplemobiletools.clock.receivers.DateTimeWidgetUpdateReceiver import com.simplemobiletools.clock.receivers.HideAlarmReceiver @@ -139,9 +140,10 @@ fun Context.getOpenAlarmTabIntent(): PendingIntent { return PendingIntent.getActivity(this, OPEN_ALARMS_TAB_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) } -fun Context.getOpenTimerTabIntent(): PendingIntent { +fun Context.getOpenTimerTabIntent(timerId: Long): PendingIntent { val intent = getLaunchIntent() ?: Intent(this, SplashActivity::class.java) intent.putExtra(OPEN_TAB, TAB_TIMER) + intent.putExtra(TIMER_ID, timerId) return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) } @@ -260,8 +262,8 @@ fun Context.showAlarmNotification(alarm: Alarm) { } @SuppressLint("NewApi") -fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { - var soundUri = config.timerSoundUri +fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { + var soundUri = timer.soundUri if (soundUri == SILENT) { soundUri = "" } else { @@ -292,7 +294,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: lightColor = getAdjustedPrimaryColor() setSound(Uri.parse(soundUri), audioAttributes) - if (!config.timerVibrate) { + if (!timer.vibrate) { vibrationPattern = longArrayOf(0L) } @@ -321,7 +323,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - if (config.timerVibrate) { + if (timer.vibrate) { val vibrateArray = LongArray(2) { 500 } builder.setVibrate(vibrateArray) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt index 597e5861..40e948af 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/gson/TypeAdapter.kt @@ -7,11 +7,8 @@ import com.simplemobiletools.clock.models.TimerState val timerStates = valueOf() .registerSubtype(TimerState.Idle::class.java) - .registerSubtype(TimerState.Start::class.java) .registerSubtype(TimerState.Running::class.java) - .registerSubtype(TimerState.Pause::class.java) .registerSubtype(TimerState.Paused::class.java) - .registerSubtype(TimerState.Finish::class.java) .registerSubtype(TimerState.Finished::class.java) inline fun valueOf(): RuntimeTypeAdapterFactory = RuntimeTypeAdapterFactory.of(T::class.java) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 49241e48..bf5d54cb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -2,6 +2,7 @@ package com.simplemobiletools.clock.fragments import android.graphics.Color import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -10,13 +11,16 @@ import androidx.viewpager2.widget.ViewPager2 import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity import com.simplemobiletools.clock.adapters.TimerAdapter -import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog import com.simplemobiletools.clock.extensions.config 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.extensions.* +import com.simplemobiletools.commons.models.AlarmSound +import kotlinx.android.synthetic.main.fragment_timer.timer_view_pager import kotlinx.android.synthetic.main.fragment_timer.view.* import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -24,7 +28,7 @@ import org.greenrobot.eventbus.ThreadMode class TimerFragment : Fragment() { - lateinit var view: ViewGroup + private lateinit var view: ViewGroup private lateinit var timerAdapter: TimerAdapter override fun onCreate(savedInstanceState: Bundle?) { @@ -42,7 +46,10 @@ class TimerFragment : Fragment() { timerAdapter = TimerAdapter(requireActivity() as SimpleActivity) { refreshTimers() } + timer_view_pager.adapter = timerAdapter + //set empty page transformer to disable item animations + timer_view_pager.setPageTransformer { _, _ -> } timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { updateViews(position) @@ -64,16 +71,13 @@ class TimerFragment : Fragment() { timer_play_pause.applyColorFilter(if (activity?.getAdjustedPrimaryColor() == Color.WHITE) Color.BLACK else Color.WHITE) timer_reset.applyColorFilter(textColor) - timer_play_pause.setOnClickListener { val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) when (val state = timer.state) { -// is TimerState.Idle -> EventBus.getDefault().post(TimerState.Start(timer.seconds.secondsToMillis)) -// is TimerState.Paused -> EventBus.getDefault().post(TimerState.Start(state.tick)) -// is TimerState.Running -> EventBus.getDefault().post(TimerState.Pause(state.tick)) -// is TimerState.Finished -> EventBus.getDefault().post(TimerState.Start(timer.seconds.secondsToMillis)) - else -> { - } + 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)) } } @@ -95,67 +99,39 @@ class TimerFragment : Fragment() { } private fun updateViews(position: Int) { - val timer = timerAdapter.getItemAt(position) - //check if timer is running to update view + activity?.runOnUiThread { + val timer = timerAdapter.getItemAt(position) + updateViewStates(timer.state) + } } private fun refreshTimers(scrollToLast: Boolean = false) { activity?.timerHelper?.getTimers { timers -> - timerAdapter.submitList(timers) - activity?.runOnUiThread { - view.timer_delete.beVisibleIf(timers.size > 1) + Log.d(TAG, "refreshTimers: $timers") + timerAdapter.submitList(timers) { if (scrollToLast) { view.timer_view_pager.currentItem = timers.lastIndex } + updateViews(timer_view_pager.currentItem) } } } private fun stopTimer(timer: Timer) { - EventBus.getDefault().post(TimerState.Idle) + EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) activity?.hideTimerNotification() -// view.timer_time.text = activity?.config?.timerSeconds?.getFormattedDuration() - } - - private fun changeDuration() { - MyTimePickerDialogDialog(activity as SimpleActivity, requireContext().config.timerSeconds) { seconds -> - val timerSeconds = if (seconds <= 0) 10 else seconds - activity?.config?.timerSeconds = timerSeconds - val duration = timerSeconds.getFormattedDuration() -// view.timer_initial_time.text = duration - -// if (view.timer_reset.isGone()) { -// stopTimer() -// } - } } @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Idle) { -// view.timer_time.text = requiredActivity.config.timerSeconds.getFormattedDuration() - updateViewStates(state) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Running) { -// view.timer_time.text = state.tick.div(1000F).roundToInt().getFormattedDuration() - updateViewStates(state) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Paused) { - updateViewStates(state) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMessageEvent(state: TimerState.Finished) { -// view.timer_time.text = 0.getFormattedDuration() - updateViewStates(state) + fun onMessageEvent(event: TimerEvent.Refresh) { + Log.d(TAG, "onMessageEvent: $event") + refreshTimers() } private fun updateViewStates(state: TimerState) { val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished view.timer_reset.beVisibleIf(resetPossible) + view.timer_delete.beVisibleIf(!resetPossible && timerAdapter.itemCount > 1) val drawableId = if (state is TimerState.Running) { R.drawable.ic_pause_vector @@ -171,10 +147,15 @@ class TimerFragment : Fragment() { view.timer_play_pause.setImageDrawable(resources.getColoredDrawableWithColor(drawableId, iconColor)) } -// -// fun updateAlarmSound(alarmSound: AlarmSound) { -// activity?.config?.timerSoundTitle = alarmSound.title -// activity?.config?.timerSoundUri = alarmSound.uri -// view.timer_sound.text = alarmSound.title -// } + + fun updateAlarmSound(alarmSound: AlarmSound) { + val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) + activity?.timerHelper?.insertOrUpdateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) { + refreshTimers() + } + } + + companion object { + private const val TAG = "TimerFragment" + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index 19ba8266..8a60c2e6 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -45,6 +45,7 @@ const val TAB_CLOCK = 0 const val TAB_ALARM = 1 const val TAB_STOPWATCH = 2 const val TAB_TIMER = 3 +const val TIMER_ID = "timer_position" // stopwatch sorting const val SORT_BY_LAP = 1 diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt index 499733ec..9b6a16f0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Converters.kt @@ -1,12 +1,11 @@ package com.simplemobiletools.clock.helpers import androidx.room.TypeConverter -import com.google.gson.Gson +import com.simplemobiletools.clock.extensions.gson.gson import com.simplemobiletools.clock.models.StateWrapper import com.simplemobiletools.clock.models.TimerState class Converters { - private val gson = Gson() @TypeConverter fun jsonToTimerState(value: String): TimerState { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index 29b84d47..d6c8ea0b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -2,6 +2,7 @@ package com.simplemobiletools.clock.helpers import android.content.Context import android.media.RingtoneManager +import android.util.Log import com.simplemobiletools.clock.extensions.timerDb import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState @@ -12,15 +13,22 @@ import com.simplemobiletools.commons.helpers.ensureBackgroundThread class TimerHelper(val context: Context) { private val timerDao = context.timerDb - fun getTimers(callback: (notes: List) -> Unit) { + fun getTimers(callback: (timers: List) -> Unit) { ensureBackgroundThread { callback.invoke(timerDao.getTimers()) } } + fun getTimer(timerId: Long, callback: (timer: Timer) -> Unit) { + ensureBackgroundThread { + callback.invoke(timerDao.getTimer(timerId)) + } + } + fun insertOrUpdateTimer(timer: Timer, callback: () -> Unit = {}) { ensureBackgroundThread { - timerDao.insertOrUpdateTimer(timer) + val id = timerDao.insertOrUpdateTimer(timer) + Log.d(TAG, "insertOrUpdateTimer: $id") callback.invoke() } } @@ -48,4 +56,8 @@ class TimerHelper(val context: Context) { callback.invoke() } } + + companion object { + private const val TAG = "TimerHelper" + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt index d5595aec..b29d3fc8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt @@ -12,6 +12,9 @@ interface TimerDao { @Query("SELECT * FROM timers") fun getTimers(): List + @Query("SELECT * FROM timers WHERE id=:id") + fun getTimer(id: Long): Timer + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrUpdateTimer(timer: Timer): Long diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt new file mode 100644 index 00000000..c5ae5ec5 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt @@ -0,0 +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) +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt index e45239e3..ad8f3a6e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerState.kt @@ -2,10 +2,7 @@ package com.simplemobiletools.clock.models sealed class TimerState { object Idle : TimerState() - data class Start(val duration: Long) : TimerState() data class Running(val duration: Long, val tick: Long) : TimerState() - data class Pause(val duration: Long) : TimerState() data class Paused(val duration: Long, val tick: Long) : TimerState() - data class Finish(val duration: Long) : TimerState() object Finished : TimerState() } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index f0f92c8a..6ea59b28 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -76,7 +76,7 @@ class TimerService : Service() { .setContentTitle(label) .setContentText(formattedDuration) .setSmallIcon(R.drawable.ic_timer) - .setContentIntent(this.getOpenTimerTabIntent()) + .setContentIntent(this.getOpenTimerTabIntent(0)) .setPriority(Notification.PRIORITY_DEFAULT) .setSound(null) .setOngoing(true) diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml index 87297742..7e321090 100644 --- a/app/src/main/res/layout/fragment_timer.xml +++ b/app/src/main/res/layout/fragment_timer.xml @@ -24,7 +24,7 @@ android:background="?attr/selectableItemBackgroundBorderless" android:padding="@dimen/normal_margin" android:src="@drawable/ic_reset_vector" - android:visibility="gone" + android:visibility="visible" app:layout_constraintBottom_toBottomOf="@+id/timer_play_pause" app:layout_constraintEnd_toStartOf="@+id/timer_play_pause" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index 1f0cf8ac..d4e0c7aa 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -1,111 +1,103 @@ - + android:layout_height="match_parent"> - + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/normal_margin" + android:background="?attr/selectableItemBackground" + android:gravity="center_horizontal" + android:padding="@dimen/small_margin" + android:textSize="@dimen/stopwatch_text_size" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="00:00" /> - + + + + - - + android:text="@string/vibrate" + android:textSize="@dimen/bigger_text_size" /> - + + + + + + + + - - - - - - - - - - - - - - - - - + android:layout_marginStart="10dp" + android:hint="@string/label" + android:maxLines="1" + android:singleLine="true" + android:textCursorDrawable="@null" + android:textSize="@dimen/normal_text_size" /> + + From 9b421b367667a874750690d5ed6469351bdabe4f Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 1 Sep 2021 21:26:56 +0100 Subject: [PATCH 03/32] Handle timer service notifications --- .../clock/adapters/TimerAdapter.kt | 16 +++-- .../clock/extensions/Long.kt | 6 ++ .../clock/fragments/TimerFragment.kt | 17 +++-- .../clock/helpers/TimerHelper.kt | 6 +- .../clock/interfaces/TimerDao.kt | 2 +- .../simplemobiletools/clock/models/Timer.kt | 2 +- .../clock/services/TimerService.kt | 62 ++++++++++++++----- app/src/main/res/values-az/strings.xml | 6 ++ app/src/main/res/values-cs/strings.xml | 6 ++ app/src/main/res/values-cy/strings.xml | 6 ++ app/src/main/res/values-da/strings.xml | 6 ++ app/src/main/res/values-de/strings.xml | 6 ++ app/src/main/res/values-el/strings.xml | 6 ++ app/src/main/res/values-es/strings.xml | 6 ++ app/src/main/res/values-eu/strings.xml | 6 ++ app/src/main/res/values-fi/strings.xml | 6 ++ app/src/main/res/values-fr/strings.xml | 6 ++ app/src/main/res/values-hr/strings.xml | 6 ++ app/src/main/res/values-id/strings.xml | 6 ++ app/src/main/res/values-in/strings.xml | 6 ++ app/src/main/res/values-it/strings.xml | 6 ++ app/src/main/res/values-ja/strings.xml | 6 ++ app/src/main/res/values-lt/strings.xml | 6 ++ app/src/main/res/values-ml/strings.xml | 6 ++ app/src/main/res/values-nb/strings.xml | 6 ++ app/src/main/res/values-nl/strings.xml | 6 ++ app/src/main/res/values-pl/strings.xml | 6 ++ app/src/main/res/values-pt/strings.xml | 6 ++ app/src/main/res/values-ru/strings.xml | 6 ++ app/src/main/res/values-sk/strings.xml | 6 ++ app/src/main/res/values-sv/strings.xml | 6 ++ app/src/main/res/values-tr/strings.xml | 6 ++ app/src/main/res/values-uk/strings.xml | 6 ++ app/src/main/res/values-zh-rCN/strings.xml | 6 ++ app/src/main/res/values-zh-rTW/strings.xml | 6 ++ app/src/main/res/values/strings.xml | 6 ++ 36 files changed, 252 insertions(+), 33 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 899bf347..9e407c5f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -12,10 +12,7 @@ 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.checkAlarmsWithDeletedSoundUri -import com.simplemobiletools.clock.extensions.colorLeftDrawable -import com.simplemobiletools.clock.extensions.config -import com.simplemobiletools.clock.extensions.timerHelper +import com.simplemobiletools.clock.extensions.* import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState @@ -116,11 +113,11 @@ class TimerAdapter( } is TimerState.Paused -> { - timer_time.text = timer.state.tick.div(1000F).roundToInt().getFormattedDuration() + timer_time.text = timer.state.tick.getFormattedDuration() } is TimerState.Running -> { - timer_time.text = timer.state.tick.div(1000F).roundToInt().getFormattedDuration() + timer_time.text = timer.state.tick.getFormattedDuration() } } } @@ -142,9 +139,10 @@ class TimerAdapter( private fun updateTimer(timer: Timer, refresh: Boolean = true) { Log.w(TAG, "updateTimer: $timer") - activity.timerHelper.insertOrUpdateTimer(timer) - if (refresh) { - onRefresh.invoke() + activity.timerHelper.insertOrUpdateTimer(timer){ + if (refresh) { + onRefresh.invoke() + } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt index 7500e536..d731ed59 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Long.kt @@ -1,8 +1,10 @@ package com.simplemobiletools.clock.extensions import android.text.format.DateFormat +import com.simplemobiletools.commons.extensions.getFormattedDuration import java.util.* import java.util.concurrent.TimeUnit +import kotlin.math.roundToInt fun Long.formatStopwatchTime(useLongerMSFormat: Boolean): String { val MSFormat = if (useLongerMSFormat) "%03d" else "%01d" @@ -37,5 +39,9 @@ fun Long.timestampFormat(format: String = "dd. MM. yyyy"): String { return DateFormat.format(format, calendar).toString() } +fun Long.getFormattedDuration(forceShowHours: Boolean = false): String { + return this.div(1000F).roundToInt().getFormattedDuration(forceShowHours) +} + val Long.secondsToMillis get() = TimeUnit.SECONDS.toMillis(this) val Long.millisToSeconds get() = TimeUnit.MILLISECONDS.toSeconds(this) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index bf5d54cb..7936ed8b 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -100,17 +100,24 @@ class TimerFragment : Fragment() { private fun updateViews(position: Int) { activity?.runOnUiThread { - val timer = timerAdapter.getItemAt(position) - updateViewStates(timer.state) + if (timerAdapter.itemCount > 0) { + val timer = timerAdapter.getItemAt(position) + updateViewStates(timer.state) + view.timer_play_pause.beVisible() + } else { + view.timer_delete.beGone() + view.timer_play_pause.beGone() + view.timer_reset.beGone() + } } } - private fun refreshTimers(scrollToLast: Boolean = false) { + private fun refreshTimers(scrollToLatest: Boolean = false) { activity?.timerHelper?.getTimers { timers -> Log.d(TAG, "refreshTimers: $timers") timerAdapter.submitList(timers) { - if (scrollToLast) { - view.timer_view_pager.currentItem = timers.lastIndex + if (scrollToLatest) { + view.timer_view_pager.currentItem = 0 } updateViews(timer_view_pager.currentItem) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index d6c8ea0b..e2ce3496 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -43,14 +43,16 @@ class TimerHelper(val context: Context) { fun insertNewTimer(callback: () -> Unit = {}) { ensureBackgroundThread { timerDao.insertOrUpdateTimer( - Timer(id = null, + Timer( + id = null, seconds = DEFAULT_TIME, TimerState.Idle, false, context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM).uri, context.getDefaultAlarmTitle(RingtoneManager.TYPE_ALARM), "", - DEFAULT_MAX_TIMER_REMINDER_SECS.toString()) + System.currentTimeMillis(), + ) ) callback.invoke() diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt index b29d3fc8..133c8320 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt @@ -9,7 +9,7 @@ import com.simplemobiletools.clock.models.Timer @Dao interface TimerDao { - @Query("SELECT * FROM timers") + @Query("SELECT * FROM timers ORDER BY createdAt DESC") fun getTimers(): List @Query("SELECT * FROM timers WHERE id=:id") diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt index b93b2ce4..a7f8b2e9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt @@ -12,5 +12,5 @@ data class Timer( val soundUri: String, val soundTitle: String, val label: String, - val maxReminderSecs: String, + val createdAt: Long, ) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index 6ea59b28..99c341b7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -12,11 +12,15 @@ import android.os.IBinder import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import com.simplemobiletools.clock.R -import com.simplemobiletools.clock.extensions.config +import com.simplemobiletools.clock.extensions.getFormattedDuration import com.simplemobiletools.clock.extensions.getOpenTimerTabIntent +import com.simplemobiletools.clock.extensions.timerHelper import com.simplemobiletools.clock.helpers.TIMER_RUNNING_NOTIF_ID +import com.simplemobiletools.clock.models.TimerEvent +import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.commons.extensions.getFormattedDuration import com.simplemobiletools.commons.helpers.isOreoPlus +import kotlin.math.roundToInt import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -34,18 +38,43 @@ class TimerService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) - - val formattedDuration = config.timerSeconds.getFormattedDuration() - startForeground(TIMER_RUNNING_NOTIF_ID, notification(formattedDuration)) - + updateNotification() + startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timer_notification_msg), -1)) return START_NOT_STICKY } + private fun updateNotification() { + timerHelper.getTimers { timers -> + val runningTimers = timers.filter { it.state is TimerState.Running } + if (runningTimers.isNotEmpty()) { + val firstTimer = runningTimers.first() + val formattedDuration = (firstTimer.state as TimerState.Running).tick.getFormattedDuration() + val contextText = when { + runningTimers.size > 1 -> { + getString(R.string.timer_multiple_notification_msg, runningTimers.size) + } + firstTimer.label.isNotEmpty() -> { + getString(R.string.timer_single_notification_label_msg, firstTimer.label) + } + else -> { + getString(R.string.timer_single_notification_msg, runningTimers.size) + } + } + startForeground(TIMER_RUNNING_NOTIF_ID, notification(formattedDuration, contextText, firstTimer.id!!)) + } + } + } + @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerStopService) { stopService() } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageEvent(event: TimerEvent.Refresh) { + updateNotification() + } + private fun stopService() { if (isOreoPlus()) { stopForeground(true) @@ -60,7 +89,7 @@ class TimerService : Service() { } @TargetApi(Build.VERSION_CODES.O) - private fun notification(formattedDuration: String): Notification { + private fun notification(title: String, contentText: String, firstRunningTimerId: Long): Notification { val channelId = "simple_alarm_timer" val label = getString(R.string.timer) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -73,15 +102,18 @@ class TimerService : Service() { } val builder = NotificationCompat.Builder(this) - .setContentTitle(label) - .setContentText(formattedDuration) - .setSmallIcon(R.drawable.ic_timer) - .setContentIntent(this.getOpenTimerTabIntent(0)) - .setPriority(Notification.PRIORITY_DEFAULT) - .setSound(null) - .setOngoing(true) - .setAutoCancel(true) - .setChannelId(channelId) + .setContentTitle(title) + .setContentText(contentText) + .setSmallIcon(R.drawable.ic_timer) + .setPriority(Notification.PRIORITY_DEFAULT) + .setSound(null) + .setOngoing(true) + .setAutoCancel(true) + .setChannelId(channelId) + + if (firstRunningTimerId != -1L) { + builder.setContentIntent(this.getOpenTimerTabIntent(firstRunningTimerId)) + } builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) return builder.build() diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index a9e6cfea..5f379b60 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Saat bölməsi Siqnal bölməsi diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 0501d23c..17156571 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Záložka hodin Záložka budíku diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 8e177151..c84a6548 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Tab cloc Tab larwm diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index e4bca230..f658b461 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Ur Alarm diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 483a116c..3b64d43b 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Uhr Wecker diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 549a81c6..700ed3d6 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -18,6 +18,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Ετικέτα Ρολογιού Ετικέτα Αφύπνισης diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f7c9c035..4de9c65a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Pestaña de reloj Pestaña de alarma diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 117c3153..952f6bad 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Erloju fitxa Alarma fitxa diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 24f85ffd..6a8ce234 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -16,6 +16,12 @@ Pyyhkäise oikealle sammuttaaksesi tai vasemmalle torkuttaaksesi. Luontijärjestys Herätysaika +  + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running   Kello-välilehti diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e14b52ea..6635335f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Horloge Réveil diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 751d5244..bd991217 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Kartica sata Kartica alarma diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 42db057b..dd2447f5 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Tab jam Tab alarm diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 42db057b..dd2447f5 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Tab jam Tab alarm diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 574944c0..68893b2b 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Scheda orologio Scheda sveglia diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index ee9c1e70..6560fd4e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + 時計 アラーム diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index a2d53474..8b4f600f 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Laikrodžio skirtukas Žadintuvo skirtukas diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 638166c8..32517ac3 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + ക്ലോക്ക് ടാബ് അലാറം ടാബ് diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 122a31ea..906aa8a6 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Klokke Alarm diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index d166eaf2..47ae120b 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -17,6 +17,12 @@ Aanmaakvolgorde Alarmtijd + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Tab Klok Tab Alarm diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index cb9cb961..532e786e 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Zegar Alarm diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 03876796..db9fc791 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Relógio Alarme diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 721f0e08..fb3bbf1b 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Часы Будильник diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 777a19a3..ef249f79 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -17,6 +17,12 @@ Poradia vytvorenia Času budíka + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Okno s časom Okno s budíkom diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 02f55b43..9539f017 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Fliken Klocka Fliken Alarm diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 6cc351b4..759f7923 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Saat sekmesi Alarm sekmesi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 5300662f..3caeefdf 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Годинник Будильник diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 05a7d5b9..4781772e 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + 时钟页面 闹钟页面 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 66177ddb..bfda24f9 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + 時鐘頁面 鬧鐘頁面 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3d6d4bc5..869cbd33 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,6 +17,12 @@ Creation order Alarm time + + Timers are running + %d timers are running + Timer for %s is running + %d timer is running + Clock tab Alarm tab From 9df105ae8d659028ac16f65e5ba89b6df77b48cd Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Thu, 2 Sep 2021 20:27:07 +0100 Subject: [PATCH 04/32] Handle timer notifications --- .../kotlin/com/simplemobiletools/clock/App.kt | 12 ++++--- .../clock/activities/MainActivity.kt | 32 ++++++++++++++++--- .../clock/activities/SplashActivity.kt | 5 ++- .../clock/adapters/TimerAdapter.kt | 2 +- .../clock/adapters/ViewPagerAdapter.kt | 8 +++++ .../clock/extensions/Context.kt | 12 +++++-- .../clock/fragments/TimerFragment.kt | 31 ++++++++++++++++-- .../clock/helpers/Constants.kt | 3 +- .../clock/helpers/TimerHelper.kt | 3 +- .../simplemobiletools/clock/models/Timer.kt | 1 + .../clock/services/TimerService.kt | 11 +++++-- 11 files changed, 97 insertions(+), 23 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index 9cc7c842..f627a09e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -27,7 +27,7 @@ import org.greenrobot.eventbus.ThreadMode class App : Application(), LifecycleObserver { - private var timers = mutableMapOf() + private var countDownTimers = mutableMapOf() override fun onCreate() { super.onCreate() @@ -62,7 +62,9 @@ class App : Application(), LifecycleObserver { timerHelper.getTimers { timers -> val runningTimers = timers.filter { it.state is TimerState.Running } runningTimers.forEach { timer -> - EventBus.getDefault().post(TimerEvent.Start(timer.id!!, (timer.state as TimerState.Running).tick)) + if (countDownTimers[timer.id] == null) { + EventBus.getDefault().post(TimerEvent.Start(timer.id!!, (timer.state as TimerState.Running).tick)) + } } } } @@ -71,7 +73,7 @@ class App : Application(), LifecycleObserver { fun onMessageEvent(event: TimerEvent.Reset) { Log.w(TAG, "onMessageEvent: $event") updateTimerState(event.timerId, TimerState.Idle) - timers[event.timerId]?.cancel() + countDownTimers[event.timerId]?.cancel() } @Subscribe(threadMode = ThreadMode.MAIN) @@ -88,7 +90,7 @@ class App : Application(), LifecycleObserver { EventBus.getDefault().post(TimerStopService) } }.start() - timers[event.timerId] = countDownTimer + countDownTimers[event.timerId] = countDownTimer } @Subscribe(threadMode = ThreadMode.MAIN) @@ -108,7 +110,7 @@ class App : Application(), LifecycleObserver { Log.w(TAG, "onMessageEvent: $event") timerHelper.getTimer(event.timerId) { timer -> updateTimerState(event.timerId, TimerState.Paused(event.duration, (timer.state as TimerState.Running).tick)) - timers[event.timerId]?.cancel() + countDownTimers[event.timerId]?.cancel() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt index 8931fdb9..81fe0ecd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt @@ -3,6 +3,7 @@ package com.simplemobiletools.clock.activities import android.content.Intent import android.graphics.drawable.ColorDrawable import android.os.Bundle +import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.WindowManager @@ -19,9 +20,11 @@ import com.simplemobiletools.commons.helpers.LICENSE_RTL import com.simplemobiletools.commons.helpers.LICENSE_STETHO import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.models.FAQItem -import kotlinx.android.synthetic.main.activity_main.* +import kotlinx.android.synthetic.main.activity_main.main_tabs_holder +import kotlinx.android.synthetic.main.activity_main.view_pager class MainActivity : SimpleActivity() { + private val TAG = "MainActivity" private var storedTextColor = 0 private var storedBackgroundColor = 0 private var storedPrimaryColor = 0 @@ -29,8 +32,8 @@ class MainActivity : SimpleActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + intent.extras?.printAllItems("onCreate") appLaunched(BuildConfig.APPLICATION_ID) - storeStateVariables() initFragments() @@ -100,9 +103,17 @@ class MainActivity : SimpleActivity() { } override fun onNewIntent(intent: Intent) { + intent.extras?.printAllItems("onNewIntent") if (intent.extras?.containsKey(OPEN_TAB) == true) { - view_pager.setCurrentItem(intent.getIntExtra(OPEN_TAB, TAB_CLOCK), false) + 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) + Log.e(TAG, "onNewIntent: TIMER ID: $timerId") + (view_pager.adapter as ViewPagerAdapter).updateTimerPosition(timerId) + } } + super.onNewIntent(intent) } private fun storeStateVariables() { @@ -136,7 +147,8 @@ class MainActivity : SimpleActivity() { private fun getViewPagerAdapter() = view_pager.adapter as? ViewPagerAdapter private fun initFragments() { - view_pager.adapter = ViewPagerAdapter(supportFragmentManager) + val viewPagerAdapter = ViewPagerAdapter(supportFragmentManager) + view_pager.adapter = viewPagerAdapter view_pager.onPageChangeListener { main_tabs_holder.getTabAt(it)?.select() invalidateOptionsMenu() @@ -144,6 +156,12 @@ class MainActivity : SimpleActivity() { val tabToOpen = intent.getIntExtra(OPEN_TAB, config.lastUsedViewPagerPage) intent.removeExtra(OPEN_TAB) + if (tabToOpen == TAB_TIMER) { + Log.e(TAG, "initFragments: TIMER") + val timerId = intent.getLongExtra(TIMER_ID, INVALID_TIMER_ID) + Log.e(TAG, "initFragments: TIMER ID: $timerId") + viewPagerAdapter.updateTimerPosition(timerId) + } view_pager.currentItem = tabToOpen view_pager.offscreenPageLimit = TABS_COUNT - 1 main_tabs_holder.onTabSelectionChanged( @@ -195,3 +213,9 @@ class MainActivity : SimpleActivity() { startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) } } + +fun Bundle.printAllItems(where:String) { + for (key in keySet()) { + Log.e(where, "Item: key: $key - value: ${get(key)}") + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt b/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt index 860cd54e..164ace18 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt @@ -1,9 +1,7 @@ package com.simplemobiletools.clock.activities import android.content.Intent -import com.simplemobiletools.clock.helpers.OPEN_TAB -import com.simplemobiletools.clock.helpers.TAB_ALARM -import com.simplemobiletools.clock.helpers.TAB_CLOCK +import com.simplemobiletools.clock.helpers.* import com.simplemobiletools.commons.activities.BaseSplashActivity class SplashActivity : BaseSplashActivity() { @@ -18,6 +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)) startActivity(this) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 9e407c5f..10d1ff18 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -134,7 +134,7 @@ class TimerAdapter( fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { Log.w(TAG, "updateAlarmSound: $timer") - updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) + updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri, channelId = null)) } private fun updateTimer(timer: Timer, refresh: Boolean = true) { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt index c79be53e..e2177b51 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt @@ -1,5 +1,6 @@ package com.simplemobiletools.clock.adapters +import android.util.Log import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -53,4 +54,11 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { fun updateTimerTabAlarmSound(alarmSound: AlarmSound) { (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) } + + private val TAG = "ViewPagerAdapter" + + fun updateTimerPosition(timerId: Long) { + Log.e(TAG, "updateTimerPosition: $timerId") + (fragments[TAB_TIMER] as? TimerFragment)?.updatePosition(timerId) + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index d17da18d..d2e7fa4d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -13,6 +13,7 @@ import android.net.Uri import android.os.PowerManager import android.text.SpannableString import android.text.style.RelativeSizeSpan +import android.util.Log import android.widget.Toast import androidx.core.app.AlarmManagerCompat import androidx.core.app.NotificationCompat @@ -20,6 +21,7 @@ import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.ReminderActivity import com.simplemobiletools.clock.activities.SnoozeReminderActivity import com.simplemobiletools.clock.activities.SplashActivity +import com.simplemobiletools.clock.activities.printAllItems import com.simplemobiletools.clock.databases.AppDatabase import com.simplemobiletools.clock.helpers.* import com.simplemobiletools.clock.interfaces.TimerDao @@ -144,6 +146,7 @@ fun Context.getOpenTimerTabIntent(timerId: Long): PendingIntent { val intent = getLaunchIntent() ?: Intent(this, SplashActivity::class.java) intent.putExtra(OPEN_TAB, TAB_TIMER) intent.putExtra(TIMER_ID, timerId) + intent.extras?.printAllItems("getOpenTimerTabIntent") return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) } @@ -260,7 +263,7 @@ fun Context.showAlarmNotification(alarm: Alarm) { scheduleNextAlarm(alarm, false) } } - +private const val TAG = "ALARMMMM" @SuppressLint("NewApi") fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { var soundUri = timer.soundUri @@ -270,9 +273,12 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add grantReadUriPermission(soundUri) } + Log.w(TAG, "getTimerNotification: timerSOUNDURI=${timer.soundUri}") + Log.w(TAG, "getTimerNotification: soundUri=${soundUri}") + val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val channelId = config.timerChannelId ?: "simple_timer_channel_${soundUri}_${System.currentTimeMillis()}" - config.timerChannelId = channelId + val channelId = timer.channelId ?: "simple_timer_channel_${soundUri}_${System.currentTimeMillis()}" + timerHelper.insertOrUpdateTimer(timer.copy(channelId = channelId)) if (isOreoPlus()) { try { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 7936ed8b..4b45fc24 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -27,9 +27,10 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode class TimerFragment : Fragment() { - + private val INVALID_POSITION = -1 private lateinit var view: ViewGroup private lateinit var timerAdapter: TimerAdapter + private var timerPositionToScrollTo = INVALID_POSITION override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -116,8 +117,13 @@ class TimerFragment : Fragment() { activity?.timerHelper?.getTimers { timers -> Log.d(TAG, "refreshTimers: $timers") timerAdapter.submitList(timers) { - if (scrollToLatest) { - view.timer_view_pager.currentItem = 0 + Log.e(TAG, "submitted list: timerPositionToScrollTo=$timerPositionToScrollTo") + if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { + Log.e(TAG, "scrolling to position=$timerPositionToScrollTo") + view.timer_view_pager.setCurrentItem(timerPositionToScrollTo, false) + timerPositionToScrollTo = INVALID_POSITION + } else if (scrollToLatest) { + view.timer_view_pager.setCurrentItem(0, false) } updateViews(timer_view_pager.currentItem) } @@ -162,6 +168,25 @@ class TimerFragment : Fragment() { } } + fun updatePosition(timerId: Long) { + Log.e(TAG, "updatePosition TIMER: $timerId") + activity?.timerHelper?.getTimers { timers -> + val position = timers.indexOfFirst { it.id == timerId } + Log.e(TAG, "updatePosition POSITION: $position") + if (position != INVALID_POSITION) { + activity?.runOnUiThread { + if (timerAdapter.itemCount > position) { + Log.e(TAG, "updatePosition now: $position") + view.timer_view_pager.setCurrentItem(position, false) + } else { + Log.e(TAG, "updatePosition later: $position") + timerPositionToScrollTo = position + } + } + } + } + } + companion object { private const val TAG = "TimerFragment" } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index 8a60c2e6..0bf22bc3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -45,7 +45,8 @@ const val TAB_CLOCK = 0 const val TAB_ALARM = 1 const val TAB_STOPWATCH = 2 const val TAB_TIMER = 3 -const val TIMER_ID = "timer_position" +const val TIMER_ID = "timer_id" +const val INVALID_TIMER_ID = -1L // stopwatch sorting const val SORT_BY_LAP = 1 diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index e2ce3496..798250c8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -45,7 +45,8 @@ class TimerHelper(val context: Context) { timerDao.insertOrUpdateTimer( Timer( id = null, - seconds = DEFAULT_TIME, +// seconds = DEFAULT_TIME, + seconds = 5, TimerState.Idle, false, context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM).uri, diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt index a7f8b2e9..bd098589 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt @@ -13,4 +13,5 @@ data class Timer( val soundTitle: String, val label: String, val createdAt: Long, + val channelId: String? = null, ) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index 99c341b7..7a1053b7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -9,12 +9,14 @@ import android.content.Context import android.content.Intent import android.os.Build import android.os.IBinder +import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import com.simplemobiletools.clock.R import com.simplemobiletools.clock.extensions.getFormattedDuration import com.simplemobiletools.clock.extensions.getOpenTimerTabIntent import com.simplemobiletools.clock.extensions.timerHelper +import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID import com.simplemobiletools.clock.helpers.TIMER_RUNNING_NOTIF_ID import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState @@ -39,7 +41,7 @@ class TimerService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) updateNotification() - startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timer_notification_msg), -1)) + startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timer_notification_msg), INVALID_TIMER_ID)) return START_NOT_STICKY } @@ -111,13 +113,18 @@ class TimerService : Service() { .setAutoCancel(true) .setChannelId(channelId) - if (firstRunningTimerId != -1L) { + if (firstRunningTimerId != INVALID_TIMER_ID) { + Log.e(TAG, "notification: Setting content intent for $firstRunningTimerId" ) builder.setContentIntent(this.getOpenTimerTabIntent(firstRunningTimerId)) } builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) return builder.build() } + + companion object { + private const val TAG = "TimerService" + } } @RequiresApi(Build.VERSION_CODES.O) From 037eac6864894fc086e16825e0c74ee94ca08778 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Thu, 2 Sep 2021 23:47:40 +0100 Subject: [PATCH 05/32] timer UI improvements + bug fixes - add page indicator - add default timer based on saved prefs when the database is created - post scolling to the first timer when a new timer is added --- .../clock/databases/AppDatabase.kt | 28 + .../clock/fragments/TimerFragment.kt | 35 +- .../clock/helpers/TimerHelper.kt | 22 +- .../views/pageindicator/PagerIndicator.kt | 577 ++++++++++++++++++ .../views/pageindicator/ViewPager2Attacher.kt | 80 +++ app/src/main/res/layout/fragment_timer.xml | 16 +- app/src/main/res/values/attrs.xml | 21 + app/src/main/res/values/styles.xml | 10 + 8 files changed, 763 insertions(+), 26 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt create mode 100644 app/src/main/res/values/attrs.xml diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt b/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt index 41f91466..9752b84f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/databases/AppDatabase.kt @@ -5,9 +5,12 @@ import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters +import androidx.sqlite.db.SupportSQLiteDatabase +import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.helpers.Converters import com.simplemobiletools.clock.interfaces.TimerDao import com.simplemobiletools.clock.models.Timer +import java.util.concurrent.Executors @Database(entities = [Timer::class], version = 1) @TypeConverters(Converters::class) @@ -23,6 +26,12 @@ abstract class AppDatabase : RoomDatabase() { synchronized(AppDatabase::class) { if (db == null) { db = Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app.db") + .addCallback(object : Callback() { + override fun onCreate(db: SupportSQLiteDatabase) { + super.onCreate(db) + insertDefaultTimer(context) + } + }) .build() db!!.openHelper.setWriteAheadLoggingEnabled(true) } @@ -31,6 +40,25 @@ abstract class AppDatabase : RoomDatabase() { return db!! } + private fun insertDefaultTimer(context: Context) { + Executors.newSingleThreadScheduledExecutor().execute { + val config = context.config + db!!.TimerDao().insertOrUpdateTimer( + Timer( + id = null, + seconds = config.timerSeconds, + state = config.timerState, + vibrate = config.timerVibrate, + soundUri = config.timerSoundUri, + soundTitle = config.timerSoundTitle, + label = config.timerLabel ?: "", + createdAt = System.currentTimeMillis(), + channelId = config.timerChannelId, + ) + ) + } + } + fun destroyInstance() { db = null } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 4b45fc24..70719c39 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -53,10 +53,20 @@ class TimerFragment : Fragment() { timer_view_pager.setPageTransformer { _, _ -> } timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { + Log.i(TAG, "onPageSelected: $position") updateViews(position) + indicator_view.setCurrentPosition(0) } }) + activity?.let { + val textColor = it.config.textColor + indicator_view.setSelectedDotColor(textColor) + indicator_view.setDotColor(textColor.adjustAlpha(0.5f)) + indicator_view.attachToPager(timer_view_pager) + } + + timer_add.setOnClickListener { activity?.hideKeyboard(it) activity?.timerHelper?.insertNewTimer { @@ -101,14 +111,12 @@ class TimerFragment : Fragment() { private fun updateViews(position: Int) { activity?.runOnUiThread { - if (timerAdapter.itemCount > 0) { + if (timerAdapter.itemCount > position) { val timer = timerAdapter.getItemAt(position) updateViewStates(timer.state) view.timer_play_pause.beVisible() } else { - view.timer_delete.beGone() - view.timer_play_pause.beGone() - view.timer_reset.beGone() + Log.e(TAG, "updateViews: position $position is greater than adapter itemCount ${timerAdapter.itemCount}") } } } @@ -117,15 +125,18 @@ class TimerFragment : Fragment() { activity?.timerHelper?.getTimers { timers -> Log.d(TAG, "refreshTimers: $timers") timerAdapter.submitList(timers) { - Log.e(TAG, "submitted list: timerPositionToScrollTo=$timerPositionToScrollTo") - if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { - Log.e(TAG, "scrolling to position=$timerPositionToScrollTo") - view.timer_view_pager.setCurrentItem(timerPositionToScrollTo, false) - timerPositionToScrollTo = INVALID_POSITION - } else if (scrollToLatest) { - view.timer_view_pager.setCurrentItem(0, false) + view.timer_view_pager.post { + Log.e(TAG, "submitted list: timerPositionToScrollTo=$timerPositionToScrollTo") + if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { + Log.e(TAG, "scrolling to position=$timerPositionToScrollTo") + view.timer_view_pager.setCurrentItem(timerPositionToScrollTo, false) + timerPositionToScrollTo = INVALID_POSITION + } else if (scrollToLatest) { + Log.e(TAG, "scrolling to latest") + view.timer_view_pager.setCurrentItem(0, false) + } + updateViews(timer_view_pager.currentItem) } - updateViews(timer_view_pager.currentItem) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index 798250c8..adc0c2b3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -1,13 +1,10 @@ package com.simplemobiletools.clock.helpers import android.content.Context -import android.media.RingtoneManager import android.util.Log +import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.timerDb import com.simplemobiletools.clock.models.Timer -import com.simplemobiletools.clock.models.TimerState -import com.simplemobiletools.commons.extensions.getDefaultAlarmSound -import com.simplemobiletools.commons.extensions.getDefaultAlarmTitle import com.simplemobiletools.commons.helpers.ensureBackgroundThread class TimerHelper(val context: Context) { @@ -42,17 +39,18 @@ class TimerHelper(val context: Context) { fun insertNewTimer(callback: () -> Unit = {}) { ensureBackgroundThread { + val config = context.config timerDao.insertOrUpdateTimer( Timer( id = null, -// seconds = DEFAULT_TIME, - seconds = 5, - TimerState.Idle, - false, - context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM).uri, - context.getDefaultAlarmTitle(RingtoneManager.TYPE_ALARM), - "", - System.currentTimeMillis(), + seconds = config.timerSeconds, + state = config.timerState, + vibrate = config.timerVibrate, + soundUri = config.timerSoundUri, + soundTitle = config.timerSoundTitle, + label = config.timerLabel ?: "", + createdAt = System.currentTimeMillis(), + channelId = config.timerChannelId, ) ) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt b/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt new file mode 100644 index 00000000..ea9e49f6 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt @@ -0,0 +1,577 @@ +package com.simplemobiletools.clock.views.pageindicator + +import android.animation.ArgbEvaluator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.util.AttributeSet +import android.util.SparseArray +import android.view.View +import android.widget.LinearLayout +import androidx.annotation.ColorInt +import androidx.annotation.IntDef +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.viewpager2.widget.ViewPager2 +import com.simplemobiletools.clock.R + +/** + * Page indicator that scrolls to selected item based on + * [PagerIndicator](https://github.com/TinkoffCreditSystems/PagerIndicator) + * */ +class PagerIndicator @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = R.attr.scrollingPagerIndicatorStyle +) : View(context, attrs, defStyleAttr) { + @IntDef(RecyclerView.HORIZONTAL, RecyclerView.VERTICAL) + annotation class Orientation + + private var infiniteDotCount = 0 + private val dotMinimumSize: Int + private val dotNormalSize: Int + private val dotSelectedSize: Int + private val spaceBetweenDotCenters: Int + private var visibleDotCount = 0 + private var visibleDotThreshold: Int + private var orientation: Int + private var visibleFramePosition = 0f + private var visibleFrameWidth = 0f + private var firstDotOffset = 0f + private var dotScale: SparseArray? = null + private var itemCount = 0 + private val paint: Paint + private val colorEvaluator = ArgbEvaluator() + + @ColorInt + private var dotColor: Int + + @ColorInt + private var selectedDotColor: Int + private var looped: Boolean + private var attachRunnable: Runnable? = null + private var currentAttacher: PagerAttacher<*>? = null + private var dotCountInitialized = false + + /** + * Sets dot count + * + * @param count new dot count + */ + var dotCount: Int + get() = if (looped && itemCount > visibleDotCount) { + infiniteDotCount + } else { + itemCount + } + set(count) { + initDots(count) + } + + init { + val attributes = context.obtainStyledAttributes( + attrs, + R.styleable.PagerIndicator, + defStyleAttr, + R.style.PagerIndicator) + dotColor = attributes.getColor(R.styleable.PagerIndicator_spi_dotColor, 0) + selectedDotColor = + attributes.getColor(R.styleable.PagerIndicator_spi_dotSelectedColor, dotColor) + dotNormalSize = + attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotSize, 0) + dotSelectedSize = + attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotSelectedSize, + 0) + val dotMinimumSize = + attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotMinimumSize, + -1) + this.dotMinimumSize = if (dotMinimumSize <= dotNormalSize) dotMinimumSize else -1 + spaceBetweenDotCenters = + attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotSpacing, + 0) + dotNormalSize + looped = attributes.getBoolean(R.styleable.PagerIndicator_spi_looped, false) + val visibleDotCount = + attributes.getInt(R.styleable.PagerIndicator_spi_visibleDotCount, 5) + setVisibleDotCount(visibleDotCount) + visibleDotThreshold = + attributes.getInt(R.styleable.PagerIndicator_spi_visibleDotThreshold, 2) + orientation = attributes.getInt(R.styleable.PagerIndicator_spi_orientation, + RecyclerView.HORIZONTAL) + attributes.recycle() + paint = Paint() + paint.isAntiAlias = true + if (isInEditMode) { + dotCount = visibleDotCount + onPageScrolled(visibleDotCount / 2, 0f) + } + } + + /** + * You should make indicator looped in your PagerAttacher implementation if your custom pager is looped too + * If pager has less items than visible_dot_count, indicator will work as usual; + * otherwise it will always be in infinite state. + * + * @param looped true if pager is looped + */ + fun setLooped(looped: Boolean) { + this.looped = looped + reattach() + invalidate() + } + + /** + * @return not selected dot color + */ + @ColorInt + fun getDotColor(): Int { + return dotColor + } + + /** + * Sets dot color + * + * @param color dot color + */ + fun setDotColor(@ColorInt color: Int) { + dotColor = color + invalidate() + } + + /** + * @return the selected dot color + */ + @ColorInt + fun getSelectedDotColor(): Int { + return selectedDotColor + } + + /** + * Sets selected dot color + * + * @param color selected dot color + */ + fun setSelectedDotColor(@ColorInt color: Int) { + selectedDotColor = color + invalidate() + } + + /** + * Maximum number of dots which will be visible at the same time. + * If pager has more pages than visible_dot_count, indicator will scroll to show extra dots. + * Must be odd number. + * + * @return visible dot count + */ + fun getVisibleDotCount(): Int { + return visibleDotCount + } + + /** + * Sets visible dot count. Maximum number of dots which will be visible at the same time. + * If pager has more pages than visible_dot_count, indicator will scroll to show extra dots. + * Must be odd number. + * + * @param visibleDotCount visible dot count + * @throws IllegalStateException when pager is already attached + */ + fun setVisibleDotCount(visibleDotCount: Int) { + require(visibleDotCount % 2 != 0) { "visibleDotCount must be odd" } + this.visibleDotCount = visibleDotCount + infiniteDotCount = visibleDotCount + 2 + if (attachRunnable != null) { + reattach() + } else { + requestLayout() + } + } + + /** + * The minimum number of dots which should be visible. + * If pager has less pages than visibleDotThreshold, no dots will be shown. + * + * @return visible dot threshold. + */ + fun getVisibleDotThreshold(): Int { + return visibleDotThreshold + } + + /** + * Sets the minimum number of dots which should be visible. + * If pager has less pages than visibleDotThreshold, no dots will be shown. + * + * @param visibleDotThreshold visible dot threshold. + */ + fun setVisibleDotThreshold(visibleDotThreshold: Int) { + this.visibleDotThreshold = visibleDotThreshold + if (attachRunnable != null) { + reattach() + } else { + requestLayout() + } + } + + /** + * The visible orientation of the dots + * + * @return dot orientation (RecyclerView.HORIZONTAL, RecyclerView.VERTICAL) + */ + @Orientation + fun getOrientation(): Int { + return orientation + } + + /** + * Set the dot orientation + * + * @param orientation dot orientation (RecyclerView.HORIZONTAL, RecyclerView.VERTICAL) + */ + fun setOrientation(@Orientation orientation: Int) { + this.orientation = orientation + if (attachRunnable != null) { + reattach() + } else { + requestLayout() + } + } + + /** + * Attaches indicator to ViewPager2 + * + * @param pager pager to attach + */ + fun attachToPager(pager: ViewPager2) { + attachToPager(pager, ViewPager2Attacher()) + } + + /** + * Attaches to any custom pager + * + * @param pager pager to attach + * @param attacher helper which should setup this indicator to work with custom pager + */ + fun attachToPager(pager: T, attacher: PagerAttacher) { + detachFromPager() + attacher.attachToPager(this, pager) + currentAttacher = attacher + attachRunnable = Runnable { + itemCount = -1 + attachToPager(pager, attacher) + } + } + + /** + * Detaches indicator from pager. + */ + fun detachFromPager() { + if (currentAttacher != null) { + currentAttacher!!.detachFromPager() + currentAttacher = null + attachRunnable = null + } + dotCountInitialized = false + } + + /** + * Detaches indicator from pager and attaches it again. + * It may be useful for refreshing after adapter count change. + */ + fun reattach() { + if (attachRunnable != null) { + attachRunnable!!.run() + invalidate() + } + } + + /** + * This method must be called from ViewPager.OnPageChangeListener.onPageScrolled or from some + * similar callback if you use custom PagerAttacher. + * + * @param page index of the first page currently being displayed + * Page position+1 will be visible if offset is nonzero + * @param offset Value from [0, 1) indicating the offset from the page at position + */ + fun onPageScrolled(page: Int, offset: Float) { + require(!(offset < 0 || offset > 1)) { "Offset must be [0, 1]" } + if (page < 0 || page != 0 && page >= itemCount) { + throw IndexOutOfBoundsException("page must be [0, adapter.getItemCount())") + } + if (!looped || itemCount <= visibleDotCount && itemCount > 1) { + dotScale!!.clear() + if (orientation == LinearLayout.HORIZONTAL) { + scaleDotByOffset(page, offset) + if (page < itemCount - 1) { + scaleDotByOffset(page + 1, 1 - offset) + } else if (itemCount > 1) { + scaleDotByOffset(0, 1 - offset) + } + } else { // Vertical orientation + scaleDotByOffset(page, offset) + } + invalidate() + } + if (orientation == LinearLayout.HORIZONTAL) { + adjustFramePosition(offset, page) + } else { + adjustFramePosition(offset, page - 1) + } + invalidate() + } + + /** + * Sets currently selected position (according to your pager's adapter) + * + * @param position new current position + */ + fun setCurrentPosition(position: Int) { + if (position != 0 && (position < 0 || position >= itemCount)) { + throw IndexOutOfBoundsException("Position must be [0, adapter.getItemCount()]") + } + if (itemCount == 0) { + return + } + adjustFramePosition(0f, position) + updateScaleInIdleState(position) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + // Width + val measuredWidth: Int + // Height + val measuredHeight: Int + if (orientation == LinearLayoutManager.HORIZONTAL) { + // We ignore widthMeasureSpec because width is based on visibleDotCount + measuredWidth = if (isInEditMode) { + // Maximum width with all dots visible + (visibleDotCount - 1) * spaceBetweenDotCenters + dotSelectedSize + } else { + if (itemCount >= visibleDotCount) visibleFrameWidth.toInt() else (itemCount - 1) * spaceBetweenDotCenters + dotSelectedSize + } + val heightMode = MeasureSpec.getMode(heightMeasureSpec) + val heightSize = MeasureSpec.getSize(heightMeasureSpec) + + // Height + val desiredHeight = dotSelectedSize + measuredHeight = when (heightMode) { + MeasureSpec.EXACTLY -> heightSize + MeasureSpec.AT_MOST -> desiredHeight.coerceAtMost(heightSize) + MeasureSpec.UNSPECIFIED -> desiredHeight + else -> desiredHeight + } + } else { + measuredHeight = if (isInEditMode) { + (visibleDotCount - 1) * spaceBetweenDotCenters + dotSelectedSize + } else { + if (itemCount >= visibleDotCount) visibleFrameWidth.toInt() else (itemCount) * spaceBetweenDotCenters + dotSelectedSize + } + val widthMode = MeasureSpec.getMode(widthMeasureSpec) + val widthSize = MeasureSpec.getSize(widthMeasureSpec) + + // Width + val desiredWidth = dotSelectedSize + measuredWidth = when (widthMode) { + MeasureSpec.EXACTLY -> widthSize + MeasureSpec.AT_MOST -> desiredWidth.coerceAtMost(widthSize) + MeasureSpec.UNSPECIFIED -> desiredWidth + else -> desiredWidth + } + } + setMeasuredDimension(measuredWidth, measuredHeight) + } + + override fun onDraw(canvas: Canvas) { + val dotCount = dotCount + if (dotCount < visibleDotThreshold) { + return + } + + // Some empirical coefficients + val scaleDistance = (spaceBetweenDotCenters + (dotSelectedSize - dotNormalSize) / 2) * 0.7f + val smallScaleDistance = (dotSelectedSize / 2).toFloat() + val centerScaleDistance = 6f / 7f * spaceBetweenDotCenters + val firstVisibleDotPos = + (visibleFramePosition - firstDotOffset).toInt() / spaceBetweenDotCenters + var lastVisibleDotPos = (firstVisibleDotPos + + (visibleFramePosition + visibleFrameWidth - getDotOffsetAt(firstVisibleDotPos)).toInt() + / spaceBetweenDotCenters) + + // If real dots count is less than we can draw inside visible frame, we move lastVisibleDotPos + // to the last item + if (firstVisibleDotPos == 0 && lastVisibleDotPos + 1 > dotCount) { + lastVisibleDotPos = dotCount - 1 + } + for (i in firstVisibleDotPos..lastVisibleDotPos) { + val dot = getDotOffsetAt(i) + if (dot >= visibleFramePosition && dot < visibleFramePosition + visibleFrameWidth) { + var diameter: Float + var scale: Float + + // Calculate scale according to current page position + scale = if (looped && itemCount > visibleDotCount) { + val frameCenter = visibleFramePosition + visibleFrameWidth / 2 + if (dot >= frameCenter - centerScaleDistance + && dot <= frameCenter + ) { + (dot - frameCenter + centerScaleDistance) / centerScaleDistance + } else if (dot > frameCenter + && dot < frameCenter + centerScaleDistance + ) { + 1 - (dot - frameCenter) / centerScaleDistance + } else { + 0f + } + } else { + getDotScaleAt(i) + } + diameter = dotNormalSize + (dotSelectedSize - dotNormalSize) * scale + + // Additional scale for dots at corners + if (itemCount > visibleDotCount) { + val currentScaleDistance = if (!looped && (i == 0 || i == dotCount - 1)) { + smallScaleDistance + } else { + scaleDistance + } + var size = width + if (orientation == LinearLayoutManager.VERTICAL) { + size = height + } + if (dot - visibleFramePosition < currentScaleDistance) { + val calculatedDiameter = + diameter * (dot - visibleFramePosition) / currentScaleDistance + if (calculatedDiameter <= dotMinimumSize) { + diameter = dotMinimumSize.toFloat() + } else if (calculatedDiameter < diameter) { + diameter = calculatedDiameter + } + } else if (dot - visibleFramePosition > size - currentScaleDistance) { + val calculatedDiameter = + diameter * (-dot + visibleFramePosition + size) / currentScaleDistance + if (calculatedDiameter <= dotMinimumSize) { + diameter = dotMinimumSize.toFloat() + } else if (calculatedDiameter < diameter) { + diameter = calculatedDiameter + } + } + } + paint.color = calculateDotColor(scale) + if (orientation == LinearLayoutManager.HORIZONTAL) { + canvas.drawCircle(dot - visibleFramePosition, ( + measuredHeight / 2).toFloat(), + diameter / 2, + paint) + } else { + canvas.drawCircle((measuredWidth / 2).toFloat(), + dot - visibleFramePosition, + diameter / 2, + paint) + } + } + } + } + + @ColorInt + private fun calculateDotColor(dotScale: Float): Int { + return colorEvaluator.evaluate(dotScale, dotColor, selectedDotColor) as Int + } + + private fun updateScaleInIdleState(currentPos: Int) { + if (!looped || itemCount < visibleDotCount) { + dotScale!!.clear() + dotScale!!.put(currentPos, 1f) + invalidate() + } + } + + private fun initDots(itemCount: Int) { + if (this.itemCount == itemCount && dotCountInitialized) { + return + } + this.itemCount = itemCount + dotCountInitialized = true + dotScale = SparseArray() + if (itemCount < visibleDotThreshold) { + requestLayout() + invalidate() + return + } + firstDotOffset = + if (looped && this.itemCount > visibleDotCount) 0f else dotSelectedSize / 2.toFloat() + visibleFrameWidth = + ((visibleDotCount - 1) * spaceBetweenDotCenters + dotSelectedSize).toFloat() + requestLayout() + invalidate() + } + + private fun adjustFramePosition(offset: Float, pos: Int) { + if (itemCount <= visibleDotCount) { + // Without scroll + visibleFramePosition = 0f + } else if (!looped && itemCount > visibleDotCount) { + // Not looped with scroll + val center = getDotOffsetAt(pos) + spaceBetweenDotCenters * offset + visibleFramePosition = center - visibleFrameWidth / 2 + + // Block frame offset near start and end + val firstCenteredDotIndex = visibleDotCount / 2 + val lastCenteredDot = getDotOffsetAt(dotCount - 1 - firstCenteredDotIndex) + if (visibleFramePosition + visibleFrameWidth / 2 < getDotOffsetAt(firstCenteredDotIndex)) { + visibleFramePosition = getDotOffsetAt(firstCenteredDotIndex) - visibleFrameWidth / 2 + } else if (visibleFramePosition + visibleFrameWidth / 2 > lastCenteredDot) { + visibleFramePosition = lastCenteredDot - visibleFrameWidth / 2 + } + } else { + // Looped with scroll + val center = getDotOffsetAt(infiniteDotCount / 2) + spaceBetweenDotCenters * offset + visibleFramePosition = center - visibleFrameWidth / 2 + } + } + + private fun scaleDotByOffset(position: Int, offset: Float) { + if (dotScale == null || dotCount == 0) { + return + } + setDotScaleAt(position, 1 - Math.abs(offset)) + } + + private fun getDotOffsetAt(index: Int): Float { + return firstDotOffset + index * spaceBetweenDotCenters + } + + private fun getDotScaleAt(index: Int): Float { + val scale = dotScale!![index] + return scale ?: 0f + } + + private fun setDotScaleAt(index: Int, scale: Float) { + if (scale == 0f) { + dotScale!!.remove(index) + } else { + dotScale!!.put(index, scale) + } + } + + /** + * Interface for attaching to custom pagers. + * + * @param custom pager's class + */ + interface PagerAttacher { + /** + * Here you should add all needed callbacks to track pager's item count, position and offset + * You must call: + * [PagerIndicator.setDotCount] - initially and after page selection, + * [PagerIndicator.setCurrentPosition] - initially and after page selection, + * [PagerIndicator.onPageScrolled] - in your pager callback to track scroll offset, + * [PagerIndicator.reattach] - each time your adapter items change. + * + * @param indicator indicator + * @param pager pager to attach + */ + fun attachToPager(indicator: PagerIndicator, pager: T) + + /** + * Here you should unregister all callbacks previously added to pager and adapter + */ + fun detachFromPager() + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt b/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt new file mode 100644 index 00000000..4d5bd6d4 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt @@ -0,0 +1,80 @@ +package com.simplemobiletools.clock.views.pageindicator + +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver +import androidx.viewpager2.widget.ViewPager2 +import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback + +/** + * ViewPager2 Attacher for [PagerIndicator] +) */ +class ViewPager2Attacher : PagerIndicator.PagerAttacher { + private var dataSetObserver: AdapterDataObserver? = null + private var attachedAdapter: RecyclerView.Adapter<*>? = null + private var onPageChangeListener: OnPageChangeCallback? = null + private var pager: ViewPager2? = null + + override fun attachToPager(indicator: PagerIndicator, pager: ViewPager2) { + attachedAdapter = pager.adapter + checkNotNull(attachedAdapter) { "Set adapter before call attachToPager() method" } + this.pager = pager + updateIndicatorDotsAndPosition(indicator) + dataSetObserver = object : AdapterDataObserver() { + override fun onChanged() { + indicator.reattach() + } + } + attachedAdapter!!.registerAdapterDataObserver(dataSetObserver!!) + onPageChangeListener = object : OnPageChangeCallback() { + var idleState = true + override fun onPageScrolled( + position: Int, + positionOffset: Float, + positionOffsetPixel: Int + ) { + updateIndicatorOnPagerScrolled(indicator, position, positionOffset) + } + + override fun onPageSelected(position: Int) { + if (idleState) { + updateIndicatorDotsAndPosition(indicator) + } + } + + override fun onPageScrollStateChanged(state: Int) { + idleState = state == ViewPager2.SCROLL_STATE_IDLE + } + } + pager.registerOnPageChangeCallback(onPageChangeListener!!) + } + + override fun detachFromPager() { + attachedAdapter!!.unregisterAdapterDataObserver(dataSetObserver!!) + pager!!.unregisterOnPageChangeCallback(onPageChangeListener!!) + } + + private fun updateIndicatorDotsAndPosition(indicator: PagerIndicator) { + indicator.dotCount = attachedAdapter!!.itemCount + indicator.setCurrentPosition(pager!!.currentItem) + } + + private fun updateIndicatorOnPagerScrolled( + indicator: PagerIndicator, + position: Int, + positionOffset: Float + ) { + // ViewPager may emit negative positionOffset for very fast scrolling + val offset: Float = when { + positionOffset < 0 -> { + 0f + } + positionOffset > 1 -> { + 1f + } + else -> { + positionOffset + } + } + indicator.onPageScrolled(position, offset) + } +} diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml index 7e321090..10865841 100644 --- a/app/src/main/res/layout/fragment_timer.xml +++ b/app/src/main/res/layout/fragment_timer.xml @@ -8,15 +8,27 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0be50415..d6daf9cf 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -2,4 +2,14 @@ From ca4e5d9a0a16f361d87f8bb888e0eb596afdefbe Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 3 Sep 2021 00:11:44 +0100 Subject: [PATCH 06/32] make windowSoftInputMode adJustPan - this ensures the textfield is always above the keyboard --- app/src/main/AndroidManifest.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6765eaad..f4c8c08f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -37,7 +37,8 @@ + android:launchMode="singleTask" + android:windowSoftInputMode="adjustPan"/> Date: Fri, 3 Sep 2021 00:19:42 +0100 Subject: [PATCH 07/32] cleanup, remove logs --- .../kotlin/com/simplemobiletools/clock/App.kt | 14 -------- .../clock/activities/MainActivity.kt | 13 -------- .../clock/adapters/TimerAdapter.kt | 32 +++++++------------ .../clock/adapters/ViewPagerAdapter.kt | 4 --- .../clock/extensions/Context.kt | 8 +---- .../clock/fragments/TimerFragment.kt | 17 ---------- .../clock/helpers/TimerHelper.kt | 8 +---- .../clock/services/TimerService.kt | 8 ----- 8 files changed, 14 insertions(+), 90 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index f627a09e..7bc445fb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -5,7 +5,6 @@ import android.app.NotificationManager import android.content.Context import android.os.Build import android.os.CountDownTimer -import android.util.Log import androidx.annotation.RequiresApi import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver @@ -71,17 +70,14 @@ class App : Application(), LifecycleObserver { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Reset) { - Log.w(TAG, "onMessageEvent: $event") updateTimerState(event.timerId, TimerState.Idle) countDownTimers[event.timerId]?.cancel() } @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Start) { - Log.w(TAG, "onMessageEvent: $event") val countDownTimer = object : CountDownTimer(event.duration, 1000) { override fun onTick(tick: Long) { - Log.d(TAG, "onMessageEvent--> $tick") updateTimerState(event.timerId, TimerState.Running(event.duration, tick)) } @@ -95,7 +91,6 @@ class App : Application(), LifecycleObserver { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Finish) { - Log.w(TAG, "onMessageEvent: $event") timerHelper.getTimer(event.timerId) { timer -> val pendingIntent = getOpenTimerTabIntent(event.timerId) val notification = getTimerNotification(timer, pendingIntent, false) @@ -107,7 +102,6 @@ class App : Application(), LifecycleObserver { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Pause) { - Log.w(TAG, "onMessageEvent: $event") timerHelper.getTimer(event.timerId) { timer -> updateTimerState(event.timerId, TimerState.Paused(event.duration, (timer.state as TimerState.Running).tick)) countDownTimers[event.timerId]?.cancel() @@ -117,17 +111,9 @@ class App : Application(), LifecycleObserver { private fun updateTimerState(timerId: Long, state: TimerState) { timerHelper.getTimer(timerId) { timer -> val newTimer = timer.copy(state = state) - Log.w(TAG, "updateTimerState: $newTimer") timerHelper.insertOrUpdateTimer(newTimer) { EventBus.getDefault().post(TimerEvent.Refresh(timerId)) - timerHelper.getTimer(timerId) { - Log.e(TAG, "updated timer $it") - } } } } - - companion object { - private const val TAG = "App" - } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt index 81fe0ecd..015e49af 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt @@ -3,7 +3,6 @@ package com.simplemobiletools.clock.activities import android.content.Intent import android.graphics.drawable.ColorDrawable import android.os.Bundle -import android.util.Log import android.view.Menu import android.view.MenuItem import android.view.WindowManager @@ -24,7 +23,6 @@ import kotlinx.android.synthetic.main.activity_main.main_tabs_holder import kotlinx.android.synthetic.main.activity_main.view_pager class MainActivity : SimpleActivity() { - private val TAG = "MainActivity" private var storedTextColor = 0 private var storedBackgroundColor = 0 private var storedPrimaryColor = 0 @@ -32,7 +30,6 @@ class MainActivity : SimpleActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - intent.extras?.printAllItems("onCreate") appLaunched(BuildConfig.APPLICATION_ID) storeStateVariables() initFragments() @@ -103,13 +100,11 @@ class MainActivity : SimpleActivity() { } override fun onNewIntent(intent: Intent) { - intent.extras?.printAllItems("onNewIntent") if (intent.extras?.containsKey(OPEN_TAB) == true) { 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) - Log.e(TAG, "onNewIntent: TIMER ID: $timerId") (view_pager.adapter as ViewPagerAdapter).updateTimerPosition(timerId) } } @@ -157,9 +152,7 @@ class MainActivity : SimpleActivity() { val tabToOpen = intent.getIntExtra(OPEN_TAB, config.lastUsedViewPagerPage) intent.removeExtra(OPEN_TAB) if (tabToOpen == TAB_TIMER) { - Log.e(TAG, "initFragments: TIMER") val timerId = intent.getLongExtra(TIMER_ID, INVALID_TIMER_ID) - Log.e(TAG, "initFragments: TIMER ID: $timerId") viewPagerAdapter.updateTimerPosition(timerId) } view_pager.currentItem = tabToOpen @@ -213,9 +206,3 @@ class MainActivity : SimpleActivity() { startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) } } - -fun Bundle.printAllItems(where:String) { - for (key in keySet()) { - Log.e(where, "Item: key: $key - value: ${get(key)}") - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 10d1ff18..4a920f11 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -2,7 +2,6 @@ package com.simplemobiletools.clock.adapters import android.media.AudioManager import android.media.RingtoneManager -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -21,7 +20,6 @@ import com.simplemobiletools.commons.extensions.getDefaultAlarmSound import com.simplemobiletools.commons.extensions.getFormattedDuration import com.simplemobiletools.commons.extensions.onTextChangeListener import com.simplemobiletools.commons.models.AlarmSound -import kotlin.math.roundToInt import kotlinx.android.synthetic.main.item_timer.view.* class TimerAdapter( @@ -29,6 +27,18 @@ class TimerAdapter( private val onRefresh: () -> Unit, ) : ListAdapter(diffUtil) { + companion object { + private val diffUtil = object : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: Timer, newItem: Timer): Boolean { + return oldItem == newItem + } + } + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder { return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false)) } @@ -45,7 +55,6 @@ class TimerAdapter( init { itemView.timer_label.onTextChangeListener { text -> - Log.w(TAG, "timer_label") updateTimer(getItemAt(adapterPosition).copy(label = text), false) } @@ -79,7 +88,6 @@ class TimerAdapter( } timer_vibrate_holder.setOnClickListener { - Log.w(TAG, "toggle") timer_vibrate.toggle() updateTimer(timer.copy(vibrate = timer_vibrate.isChecked), false) } @@ -127,35 +135,19 @@ class TimerAdapter( private fun changeDuration(timer: Timer) { MyTimePickerDialogDialog(activity, timer.seconds) { seconds -> val timerSeconds = if (seconds <= 0) 10 else seconds - Log.w(TAG, "changeDuration") updateTimer(timer.copy(seconds = timerSeconds)) } } fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { - Log.w(TAG, "updateAlarmSound: $timer") updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri, channelId = null)) } private fun updateTimer(timer: Timer, refresh: Boolean = true) { - Log.w(TAG, "updateTimer: $timer") activity.timerHelper.insertOrUpdateTimer(timer){ if (refresh) { onRefresh.invoke() } } } - - companion object { - private const val TAG = "TimerAdapter" - private val diffUtil = object : DiffUtil.ItemCallback() { - override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: Timer, newItem: Timer): Boolean { - return oldItem == newItem - } - } - } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt index e2177b51..bc2d9a97 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt @@ -1,6 +1,5 @@ package com.simplemobiletools.clock.adapters -import android.util.Log import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -55,10 +54,7 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { (fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound) } - private val TAG = "ViewPagerAdapter" - fun updateTimerPosition(timerId: Long) { - Log.e(TAG, "updateTimerPosition: $timerId") (fragments[TAB_TIMER] as? TimerFragment)?.updatePosition(timerId) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index d2e7fa4d..a30e1598 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -13,7 +13,6 @@ import android.net.Uri import android.os.PowerManager import android.text.SpannableString import android.text.style.RelativeSizeSpan -import android.util.Log import android.widget.Toast import androidx.core.app.AlarmManagerCompat import androidx.core.app.NotificationCompat @@ -21,7 +20,6 @@ import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.ReminderActivity import com.simplemobiletools.clock.activities.SnoozeReminderActivity import com.simplemobiletools.clock.activities.SplashActivity -import com.simplemobiletools.clock.activities.printAllItems import com.simplemobiletools.clock.databases.AppDatabase import com.simplemobiletools.clock.helpers.* import com.simplemobiletools.clock.interfaces.TimerDao @@ -146,7 +144,6 @@ fun Context.getOpenTimerTabIntent(timerId: Long): PendingIntent { val intent = getLaunchIntent() ?: Intent(this, SplashActivity::class.java) intent.putExtra(OPEN_TAB, TAB_TIMER) intent.putExtra(TIMER_ID, timerId) - intent.extras?.printAllItems("getOpenTimerTabIntent") return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) } @@ -263,7 +260,7 @@ fun Context.showAlarmNotification(alarm: Alarm) { scheduleNextAlarm(alarm, false) } } -private const val TAG = "ALARMMMM" + @SuppressLint("NewApi") fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { var soundUri = timer.soundUri @@ -273,9 +270,6 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add grantReadUriPermission(soundUri) } - Log.w(TAG, "getTimerNotification: timerSOUNDURI=${timer.soundUri}") - Log.w(TAG, "getTimerNotification: soundUri=${soundUri}") - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val channelId = timer.channelId ?: "simple_timer_channel_${soundUri}_${System.currentTimeMillis()}" timerHelper.insertOrUpdateTimer(timer.copy(channelId = channelId)) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 70719c39..f9322885 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -2,7 +2,6 @@ package com.simplemobiletools.clock.fragments import android.graphics.Color import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -53,7 +52,6 @@ class TimerFragment : Fragment() { timer_view_pager.setPageTransformer { _, _ -> } timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { - Log.i(TAG, "onPageSelected: $position") updateViews(position) indicator_view.setCurrentPosition(0) } @@ -115,24 +113,18 @@ class TimerFragment : Fragment() { val timer = timerAdapter.getItemAt(position) updateViewStates(timer.state) view.timer_play_pause.beVisible() - } else { - Log.e(TAG, "updateViews: position $position is greater than adapter itemCount ${timerAdapter.itemCount}") } } } private fun refreshTimers(scrollToLatest: Boolean = false) { activity?.timerHelper?.getTimers { timers -> - Log.d(TAG, "refreshTimers: $timers") timerAdapter.submitList(timers) { view.timer_view_pager.post { - Log.e(TAG, "submitted list: timerPositionToScrollTo=$timerPositionToScrollTo") if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { - Log.e(TAG, "scrolling to position=$timerPositionToScrollTo") view.timer_view_pager.setCurrentItem(timerPositionToScrollTo, false) timerPositionToScrollTo = INVALID_POSITION } else if (scrollToLatest) { - Log.e(TAG, "scrolling to latest") view.timer_view_pager.setCurrentItem(0, false) } updateViews(timer_view_pager.currentItem) @@ -148,7 +140,6 @@ class TimerFragment : Fragment() { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Refresh) { - Log.d(TAG, "onMessageEvent: $event") refreshTimers() } @@ -180,25 +171,17 @@ class TimerFragment : Fragment() { } fun updatePosition(timerId: Long) { - Log.e(TAG, "updatePosition TIMER: $timerId") activity?.timerHelper?.getTimers { timers -> val position = timers.indexOfFirst { it.id == timerId } - Log.e(TAG, "updatePosition POSITION: $position") if (position != INVALID_POSITION) { activity?.runOnUiThread { if (timerAdapter.itemCount > position) { - Log.e(TAG, "updatePosition now: $position") view.timer_view_pager.setCurrentItem(position, false) } else { - Log.e(TAG, "updatePosition later: $position") timerPositionToScrollTo = position } } } } } - - companion object { - private const val TAG = "TimerFragment" - } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index adc0c2b3..433d7e0d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -1,7 +1,6 @@ package com.simplemobiletools.clock.helpers import android.content.Context -import android.util.Log import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.timerDb import com.simplemobiletools.clock.models.Timer @@ -24,8 +23,7 @@ class TimerHelper(val context: Context) { fun insertOrUpdateTimer(timer: Timer, callback: () -> Unit = {}) { ensureBackgroundThread { - val id = timerDao.insertOrUpdateTimer(timer) - Log.d(TAG, "insertOrUpdateTimer: $id") + timerDao.insertOrUpdateTimer(timer) callback.invoke() } } @@ -57,8 +55,4 @@ class TimerHelper(val context: Context) { callback.invoke() } } - - companion object { - private const val TAG = "TimerHelper" - } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index 7a1053b7..46e94413 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -9,7 +9,6 @@ import android.content.Context import android.content.Intent import android.os.Build import android.os.IBinder -import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import com.simplemobiletools.clock.R @@ -20,9 +19,7 @@ import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID import com.simplemobiletools.clock.helpers.TIMER_RUNNING_NOTIF_ID import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState -import com.simplemobiletools.commons.extensions.getFormattedDuration import com.simplemobiletools.commons.helpers.isOreoPlus -import kotlin.math.roundToInt import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -114,17 +111,12 @@ class TimerService : Service() { .setChannelId(channelId) if (firstRunningTimerId != INVALID_TIMER_ID) { - Log.e(TAG, "notification: Setting content intent for $firstRunningTimerId" ) builder.setContentIntent(this.getOpenTimerTabIntent(firstRunningTimerId)) } builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) return builder.build() } - - companion object { - private const val TAG = "TimerService" - } } @RequiresApi(Build.VERSION_CODES.O) From 8d6b5770ed48f2411d9d7baac308fd8859350c62 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 3 Sep 2021 00:55:28 +0100 Subject: [PATCH 08/32] fix: reset/delete button getting hidden after multiple updates --- .../com/simplemobiletools/clock/fragments/TimerFragment.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index f9322885..7568a84f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -53,7 +53,6 @@ class TimerFragment : Fragment() { timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { override fun onPageSelected(position: Int) { updateViews(position) - indicator_view.setCurrentPosition(0) } }) @@ -145,8 +144,8 @@ class TimerFragment : Fragment() { private fun updateViewStates(state: TimerState) { val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished - view.timer_reset.beVisibleIf(resetPossible) - view.timer_delete.beVisibleIf(!resetPossible && timerAdapter.itemCount > 1) + view.timer_reset.beInvisibleIf(!resetPossible) + view.timer_delete.beInvisibleIf(!(!resetPossible && timerAdapter.itemCount > 1)) val drawableId = if (state is TimerState.Running) { R.drawable.ic_pause_vector From 2e78d675291890f53b9333b0c0bdb2d08b095be8 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 3 Sep 2021 19:42:25 +0100 Subject: [PATCH 09/32] ref: change timer UI to use RecyclerView - update commons library --- app/build.gradle | 2 +- .../clock/adapters/TimerAdapter.kt | 129 +++++++++++++----- .../clock/fragments/TimerFragment.kt | 121 ++++------------ .../clock/helpers/TimerHelper.kt | 1 - .../clock/interfaces/TimerDao.kt | 2 +- .../clock/services/TimerService.kt | 12 +- app/src/main/res/drawable/ic_add_vector.xml | 11 -- app/src/main/res/layout/fragment_timer.xml | 93 +++---------- app/src/main/res/layout/item_timer.xml | 46 ++++++- app/src/main/res/values-az/strings.xml | 1 + app/src/main/res/values-cs/strings.xml | 1 + app/src/main/res/values-cy/strings.xml | 1 + app/src/main/res/values-da/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-el/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-eu/strings.xml | 1 + app/src/main/res/values-fi/strings.xml | 1 + app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-hr/strings.xml | 1 + app/src/main/res/values-id/strings.xml | 1 + app/src/main/res/values-in/strings.xml | 1 + app/src/main/res/values-it/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values-lt/strings.xml | 1 + app/src/main/res/values-ml/strings.xml | 1 + app/src/main/res/values-nb/strings.xml | 1 + app/src/main/res/values-nl/strings.xml | 1 + app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-pt/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values-sk/strings.xml | 1 + app/src/main/res/values-sv/strings.xml | 1 + app/src/main/res/values-tr/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values-zh-rCN/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 38 files changed, 219 insertions(+), 227 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_add_vector.xml diff --git a/app/build.gradle b/app/build.gradle index a49054bf..6a149577 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,7 +67,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:ac45c5e893' + implementation 'com.github.SimpleMobileTools:Simple-Commons:554dda71a4' implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.shawnlin:number-picker:2.4.6' diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 4a920f11..cd86c346 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -1,5 +1,6 @@ package com.simplemobiletools.clock.adapters +import android.graphics.Color import android.media.AudioManager import android.media.RingtoneManager import android.view.LayoutInflater @@ -14,13 +15,13 @@ import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog import com.simplemobiletools.clock.extensions.* import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID import com.simplemobiletools.clock.models.Timer +import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog -import com.simplemobiletools.commons.extensions.getDefaultAlarmSound -import com.simplemobiletools.commons.extensions.getFormattedDuration -import com.simplemobiletools.commons.extensions.onTextChangeListener +import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.models.AlarmSound import kotlinx.android.synthetic.main.item_timer.view.* +import org.greenrobot.eventbus.EventBus class TimerAdapter( private val activity: SimpleActivity, @@ -39,6 +40,15 @@ class TimerAdapter( } } + private val config = activity.config + private var textColor = config.textColor + private var primaryColor = config.primaryColor + private var adjustedPrimaryColor = activity.getAdjustedPrimaryColor() + private var contrastColor = adjustedPrimaryColor.getContrastColor() + private var backgroundColor = config.backgroundColor + + private var selectedTimer: Timer? = null + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder { return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false)) } @@ -47,52 +57,78 @@ class TimerAdapter( holder.bind(getItem(position)) } - fun getItemAt(position: Int): Timer { - return getItem(position) + fun updateTextColor(textColor: Int) { + this.textColor = textColor + onRefresh.invoke() + } + + fun updatePrimaryColor(primaryColor: Int) { + this.primaryColor = primaryColor + adjustedPrimaryColor = activity.getAdjustedPrimaryColor() + contrastColor = adjustedPrimaryColor.getContrastColor() + onRefresh.invoke() } inner class TimerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { init { itemView.timer_label.onTextChangeListener { text -> - updateTimer(getItemAt(adapterPosition).copy(label = text), false) + updateTimer(getItem(adapterPosition).copy(label = text), false) } - itemView.post { - val textColor = activity.config.textColor - itemView.timer_initial_time.colorLeftDrawable(textColor) - itemView.timer_vibrate.colorLeftDrawable(textColor) - itemView.timer_sound.colorLeftDrawable(textColor) - } } fun bind(timer: Timer) { itemView.apply { + post { + timer_initial_time.colorLeftDrawable(textColor) + timer_vibrate.colorLeftDrawable(textColor) + timer_label_image.applyColorFilter(textColor) + timer_sound.colorLeftDrawable(textColor) + 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_initial_time.text = timer.seconds.getFormattedDuration() + timer_initial_time.setTextColor(textColor) timer_vibrate.isChecked = timer.vibrate + timer_vibrate.setTextColor(textColor) + timer_vibrate.setColors(textColor, adjustedPrimaryColor, backgroundColor) + timer_vibrate_holder.setOnClickListener { + timer_vibrate.toggle() + updateTimer(timer.copy(vibrate = timer_vibrate.isChecked, channelId = null), false) + } - timer_sound.text = timer.soundTitle - + 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_initial_time.setTextColor(textColor) timer_initial_time.setOnClickListener { changeDuration(timer) } - timer_vibrate_holder.setOnClickListener { - timer_vibrate.toggle() - updateTimer(timer.copy(vibrate = timer_vibrate.isChecked), false) - } + timer_sound.text = timer.soundTitle + timer_sound.setTextColor(textColor) timer_sound.setOnClickListener { + selectedTimer = timer SelectAlarmSoundDialog(activity, timer.soundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, RingtoneManager.TYPE_ALARM, true, onAlarmPicked = { sound -> @@ -110,26 +146,39 @@ class TimerAdapter( }) } - - when (timer.state) { - is TimerState.Finished -> { - timer_time.text = 0.getFormattedDuration() - } - - is TimerState.Idle -> { - timer_time.text = timer.seconds.getFormattedDuration() - } - - is TimerState.Paused -> { - timer_time.text = timer.state.tick.getFormattedDuration() - } - - is TimerState.Running -> { - timer_time.text = timer.state.tick.getFormattedDuration() + 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) } } + + private fun updateViewStates(state: TimerState) { + 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)) + 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)) + } } private fun changeDuration(timer: Timer) { @@ -139,15 +188,25 @@ class TimerAdapter( } } + fun updateAlarmSoundForSelectedTimer(alarmSound: AlarmSound) { + selectedTimer?.let { updateAlarmSound(it, alarmSound) } + } + fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri, channelId = null)) } private fun updateTimer(timer: Timer, refresh: Boolean = true) { - activity.timerHelper.insertOrUpdateTimer(timer){ + activity.timerHelper.insertOrUpdateTimer(timer) { if (refresh) { onRefresh.invoke() } } } + + private fun stopTimer(timer: Timer) { + EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) + activity.hideTimerNotification() + } + } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 7568a84f..ef8e7fd8 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -1,26 +1,22 @@ package com.simplemobiletools.clock.fragments -import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.viewpager2.widget.ViewPager2 import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity import com.simplemobiletools.clock.adapters.TimerAdapter import com.simplemobiletools.clock.extensions.config -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.extensions.* +import com.simplemobiletools.commons.extensions.hideKeyboard +import com.simplemobiletools.commons.extensions.updateTextColors import com.simplemobiletools.commons.models.AlarmSound -import kotlinx.android.synthetic.main.fragment_timer.timer_view_pager -import kotlinx.android.synthetic.main.fragment_timer.view.* +import kotlinx.android.synthetic.main.fragment_timer.timer_fragment +import kotlinx.android.synthetic.main.fragment_timer.view.timer_add +import kotlinx.android.synthetic.main.fragment_timer.view.timers_list import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -30,6 +26,7 @@ class TimerFragment : Fragment() { private lateinit var view: ViewGroup private lateinit var timerAdapter: TimerAdapter private var timerPositionToScrollTo = INVALID_POSITION + private var storedTextColor = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -47,22 +44,10 @@ class TimerFragment : Fragment() { refreshTimers() } - timer_view_pager.adapter = timerAdapter - //set empty page transformer to disable item animations - timer_view_pager.setPageTransformer { _, _ -> } - timer_view_pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - updateViews(position) - } - }) - - activity?.let { - val textColor = it.config.textColor - indicator_view.setSelectedDotColor(textColor) - indicator_view.setDotColor(textColor.adjustAlpha(0.5f)) - indicator_view.attachToPager(timer_view_pager) - } + storeStateVariables() + timers_list.adapter = timerAdapter + timers_list.itemAnimator = null timer_add.setOnClickListener { activity?.hideKeyboard(it) @@ -71,70 +56,43 @@ class TimerFragment : Fragment() { } } - activity?.updateTextColors(timer_fragment) - - val textColor = requireContext().config.textColor - timer_play_pause.background = - resources.getColoredDrawableWithColor(R.drawable.circle_background_filled, requireActivity().getAdjustedPrimaryColor()) - timer_play_pause.applyColorFilter(if (activity?.getAdjustedPrimaryColor() == Color.WHITE) Color.BLACK else Color.WHITE) - timer_reset.applyColorFilter(textColor) - - timer_play_pause.setOnClickListener { - val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) - 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)) - } - } - - timer_reset.setOnClickListener { - val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) - stopTimer(timer) - } - - timer_delete.setOnClickListener { - val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) - activity?.timerHelper?.deleteTimer(timer.id!!) { - refreshTimers() - } - } - refreshTimers() } return view } - private fun updateViews(position: Int) { - activity?.runOnUiThread { - if (timerAdapter.itemCount > position) { - val timer = timerAdapter.getItemAt(position) - updateViewStates(timer.state) - view.timer_play_pause.beVisible() - } + override fun onResume() { + super.onResume() + requireContext().updateTextColors(timer_fragment) + val configTextColor = requireContext().config.textColor + if (storedTextColor != configTextColor) { + (view.timers_list.adapter as TimerAdapter).updateTextColor(configTextColor) } } + override fun onPause() { + super.onPause() + storeStateVariables() + } + + private fun refreshTimers(scrollToLatest: Boolean = false) { activity?.timerHelper?.getTimers { timers -> timerAdapter.submitList(timers) { - view.timer_view_pager.post { + view.timers_list.post { if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { - view.timer_view_pager.setCurrentItem(timerPositionToScrollTo, false) + view.timers_list.scrollToPosition(timerPositionToScrollTo) timerPositionToScrollTo = INVALID_POSITION } else if (scrollToLatest) { - view.timer_view_pager.setCurrentItem(0, false) + view.timers_list.scrollToPosition(timers.lastIndex) } - updateViews(timer_view_pager.currentItem) } } } } - private fun stopTimer(timer: Timer) { - EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) - activity?.hideTimerNotification() + private fun storeStateVariables() { + storedTextColor = requireContext().config.textColor } @Subscribe(threadMode = ThreadMode.MAIN) @@ -142,31 +100,8 @@ class TimerFragment : Fragment() { refreshTimers() } - private fun updateViewStates(state: TimerState) { - val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished - view.timer_reset.beInvisibleIf(!resetPossible) - view.timer_delete.beInvisibleIf(!(!resetPossible && timerAdapter.itemCount > 1)) - - val drawableId = if (state is TimerState.Running) { - R.drawable.ic_pause_vector - } else { - R.drawable.ic_play_vector - } - - val iconColor = if (activity?.getAdjustedPrimaryColor() == Color.WHITE) { - Color.BLACK - } else { - Color.WHITE - } - - view.timer_play_pause.setImageDrawable(resources.getColoredDrawableWithColor(drawableId, iconColor)) - } - fun updateAlarmSound(alarmSound: AlarmSound) { - val timer = timerAdapter.getItemAt(timer_view_pager.currentItem) - activity?.timerHelper?.insertOrUpdateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri)) { - refreshTimers() - } + timerAdapter.updateAlarmSoundForSelectedTimer(alarmSound) } fun updatePosition(timerId: Long) { @@ -175,7 +110,7 @@ class TimerFragment : Fragment() { if (position != INVALID_POSITION) { activity?.runOnUiThread { if (timerAdapter.itemCount > position) { - view.timer_view_pager.setCurrentItem(position, false) + view.timers_list.scrollToPosition(position) } else { timerPositionToScrollTo = position } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index 433d7e0d..fb6771d4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -51,7 +51,6 @@ class TimerHelper(val context: Context) { channelId = config.timerChannelId, ) ) - callback.invoke() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt index 133c8320..fd64f8d2 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt @@ -9,7 +9,7 @@ import com.simplemobiletools.clock.models.Timer @Dao interface TimerDao { - @Query("SELECT * FROM timers ORDER BY createdAt DESC") + @Query("SELECT * FROM timers ORDER BY createdAt ASC") fun getTimers(): List @Query("SELECT * FROM timers WHERE id=:id") diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index 46e94413..40996000 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -49,15 +49,9 @@ class TimerService : Service() { val firstTimer = runningTimers.first() val formattedDuration = (firstTimer.state as TimerState.Running).tick.getFormattedDuration() val contextText = when { - runningTimers.size > 1 -> { - getString(R.string.timer_multiple_notification_msg, runningTimers.size) - } - firstTimer.label.isNotEmpty() -> { - getString(R.string.timer_single_notification_label_msg, firstTimer.label) - } - else -> { - getString(R.string.timer_single_notification_msg, runningTimers.size) - } + runningTimers.size > 1 -> getString(R.string.timer_multiple_notification_msg, runningTimers.size) + firstTimer.label.isNotEmpty() -> getString(R.string.timer_single_notification_label_msg, firstTimer.label) + else -> getString(R.string.timer_single_notification_msg, runningTimers.size) } startForeground(TIMER_RUNNING_NOTIF_ID, notification(formattedDuration, contextText, firstTimer.id!!)) } diff --git a/app/src/main/res/drawable/ic_add_vector.xml b/app/src/main/res/drawable/ic_add_vector.xml deleted file mode 100644 index ccd06b3c..00000000 --- a/app/src/main/res/drawable/ic_add_vector.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - diff --git a/app/src/main/res/layout/fragment_timer.xml b/app/src/main/res/layout/fragment_timer.xml index 10865841..cc93fea1 100644 --- a/app/src/main/res/layout/fragment_timer.xml +++ b/app/src/main/res/layout/fragment_timer.xml @@ -6,87 +6,32 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:layout_constraintTop_toTopOf="parent" + tools:itemCount="3" + tools:listitem="@layout/item_timer" /> - - - - - - - - - - - - + app:rippleColor="@color/pressed_item_foreground" /> diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index d4e0c7aa..1b6b4c39 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="wrap_content"> + + + + + + diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index 5f379b60..b7242bcd 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Saat bölməsi diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 17156571..59940686 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Záložka hodin diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index c84a6548..292bc0ce 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Tab cloc diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index f658b461..0bb34aa7 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Ur diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 3b64d43b..09c45de3 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Uhr diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 700ed3d6..f3484d66 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -23,6 +23,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Ετικέτα Ρολογιού diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 4de9c65a..37b4940a 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Pestaña de reloj diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 952f6bad..cb28eae8 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Erloju fitxa diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 6a8ce234..661fe387 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer   Kello-välilehti diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6635335f..76321c5e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Horloge diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index bd991217..c8501371 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Kartica sata diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index dd2447f5..79b28da0 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Tab jam diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index dd2447f5..79b28da0 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Tab jam diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 68893b2b..711d6397 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Scheda orologio diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 6560fd4e..2930c40b 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer 時計 diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 8b4f600f..796624c4 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Laikrodžio skirtukas diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 32517ac3..54ee4c5a 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer ക്ലോക്ക് ടാബ് diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index 906aa8a6..cf5fb931 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Klokke diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 47ae120b..884d5c77 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Tab Klok diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 532e786e..0e07fb25 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Zegar diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index db9fc791..2a5237fc 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Relógio diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index fb3bbf1b..88233cad 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Часы diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index ef249f79..6996e0a7 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Okno s časom diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 9539f017..5a33469a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Fliken Klocka diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 759f7923..b1bd6367 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Saat sekmesi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 3caeefdf..39dea5fa 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Годинник diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4781772e..62742678 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer 时钟页面 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index bfda24f9..9a47b674 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer 時鐘頁面 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 869cbd33..5eda1b79 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -22,6 +22,7 @@ %d timers are running Timer for %s is running %d timer is running + New Timer Clock tab From bd05a41b35321fb8faf247eb132e5d7e23b8caca Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 3 Sep 2021 19:44:14 +0100 Subject: [PATCH 10/32] Remove PagerIndicator and related files --- .../views/pageindicator/PagerIndicator.kt | 577 ------------------ .../views/pageindicator/ViewPager2Attacher.kt | 80 --- app/src/main/res/values/attrs.xml | 21 - app/src/main/res/values/styles.xml | 10 - 4 files changed, 688 deletions(-) delete mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt delete mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt delete mode 100644 app/src/main/res/values/attrs.xml diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt b/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt deleted file mode 100644 index ea9e49f6..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/PagerIndicator.kt +++ /dev/null @@ -1,577 +0,0 @@ -package com.simplemobiletools.clock.views.pageindicator - -import android.animation.ArgbEvaluator -import android.content.Context -import android.graphics.Canvas -import android.graphics.Paint -import android.util.AttributeSet -import android.util.SparseArray -import android.view.View -import android.widget.LinearLayout -import androidx.annotation.ColorInt -import androidx.annotation.IntDef -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import androidx.viewpager2.widget.ViewPager2 -import com.simplemobiletools.clock.R - -/** - * Page indicator that scrolls to selected item based on - * [PagerIndicator](https://github.com/TinkoffCreditSystems/PagerIndicator) - * */ -class PagerIndicator @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = R.attr.scrollingPagerIndicatorStyle -) : View(context, attrs, defStyleAttr) { - @IntDef(RecyclerView.HORIZONTAL, RecyclerView.VERTICAL) - annotation class Orientation - - private var infiniteDotCount = 0 - private val dotMinimumSize: Int - private val dotNormalSize: Int - private val dotSelectedSize: Int - private val spaceBetweenDotCenters: Int - private var visibleDotCount = 0 - private var visibleDotThreshold: Int - private var orientation: Int - private var visibleFramePosition = 0f - private var visibleFrameWidth = 0f - private var firstDotOffset = 0f - private var dotScale: SparseArray? = null - private var itemCount = 0 - private val paint: Paint - private val colorEvaluator = ArgbEvaluator() - - @ColorInt - private var dotColor: Int - - @ColorInt - private var selectedDotColor: Int - private var looped: Boolean - private var attachRunnable: Runnable? = null - private var currentAttacher: PagerAttacher<*>? = null - private var dotCountInitialized = false - - /** - * Sets dot count - * - * @param count new dot count - */ - var dotCount: Int - get() = if (looped && itemCount > visibleDotCount) { - infiniteDotCount - } else { - itemCount - } - set(count) { - initDots(count) - } - - init { - val attributes = context.obtainStyledAttributes( - attrs, - R.styleable.PagerIndicator, - defStyleAttr, - R.style.PagerIndicator) - dotColor = attributes.getColor(R.styleable.PagerIndicator_spi_dotColor, 0) - selectedDotColor = - attributes.getColor(R.styleable.PagerIndicator_spi_dotSelectedColor, dotColor) - dotNormalSize = - attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotSize, 0) - dotSelectedSize = - attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotSelectedSize, - 0) - val dotMinimumSize = - attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotMinimumSize, - -1) - this.dotMinimumSize = if (dotMinimumSize <= dotNormalSize) dotMinimumSize else -1 - spaceBetweenDotCenters = - attributes.getDimensionPixelSize(R.styleable.PagerIndicator_spi_dotSpacing, - 0) + dotNormalSize - looped = attributes.getBoolean(R.styleable.PagerIndicator_spi_looped, false) - val visibleDotCount = - attributes.getInt(R.styleable.PagerIndicator_spi_visibleDotCount, 5) - setVisibleDotCount(visibleDotCount) - visibleDotThreshold = - attributes.getInt(R.styleable.PagerIndicator_spi_visibleDotThreshold, 2) - orientation = attributes.getInt(R.styleable.PagerIndicator_spi_orientation, - RecyclerView.HORIZONTAL) - attributes.recycle() - paint = Paint() - paint.isAntiAlias = true - if (isInEditMode) { - dotCount = visibleDotCount - onPageScrolled(visibleDotCount / 2, 0f) - } - } - - /** - * You should make indicator looped in your PagerAttacher implementation if your custom pager is looped too - * If pager has less items than visible_dot_count, indicator will work as usual; - * otherwise it will always be in infinite state. - * - * @param looped true if pager is looped - */ - fun setLooped(looped: Boolean) { - this.looped = looped - reattach() - invalidate() - } - - /** - * @return not selected dot color - */ - @ColorInt - fun getDotColor(): Int { - return dotColor - } - - /** - * Sets dot color - * - * @param color dot color - */ - fun setDotColor(@ColorInt color: Int) { - dotColor = color - invalidate() - } - - /** - * @return the selected dot color - */ - @ColorInt - fun getSelectedDotColor(): Int { - return selectedDotColor - } - - /** - * Sets selected dot color - * - * @param color selected dot color - */ - fun setSelectedDotColor(@ColorInt color: Int) { - selectedDotColor = color - invalidate() - } - - /** - * Maximum number of dots which will be visible at the same time. - * If pager has more pages than visible_dot_count, indicator will scroll to show extra dots. - * Must be odd number. - * - * @return visible dot count - */ - fun getVisibleDotCount(): Int { - return visibleDotCount - } - - /** - * Sets visible dot count. Maximum number of dots which will be visible at the same time. - * If pager has more pages than visible_dot_count, indicator will scroll to show extra dots. - * Must be odd number. - * - * @param visibleDotCount visible dot count - * @throws IllegalStateException when pager is already attached - */ - fun setVisibleDotCount(visibleDotCount: Int) { - require(visibleDotCount % 2 != 0) { "visibleDotCount must be odd" } - this.visibleDotCount = visibleDotCount - infiniteDotCount = visibleDotCount + 2 - if (attachRunnable != null) { - reattach() - } else { - requestLayout() - } - } - - /** - * The minimum number of dots which should be visible. - * If pager has less pages than visibleDotThreshold, no dots will be shown. - * - * @return visible dot threshold. - */ - fun getVisibleDotThreshold(): Int { - return visibleDotThreshold - } - - /** - * Sets the minimum number of dots which should be visible. - * If pager has less pages than visibleDotThreshold, no dots will be shown. - * - * @param visibleDotThreshold visible dot threshold. - */ - fun setVisibleDotThreshold(visibleDotThreshold: Int) { - this.visibleDotThreshold = visibleDotThreshold - if (attachRunnable != null) { - reattach() - } else { - requestLayout() - } - } - - /** - * The visible orientation of the dots - * - * @return dot orientation (RecyclerView.HORIZONTAL, RecyclerView.VERTICAL) - */ - @Orientation - fun getOrientation(): Int { - return orientation - } - - /** - * Set the dot orientation - * - * @param orientation dot orientation (RecyclerView.HORIZONTAL, RecyclerView.VERTICAL) - */ - fun setOrientation(@Orientation orientation: Int) { - this.orientation = orientation - if (attachRunnable != null) { - reattach() - } else { - requestLayout() - } - } - - /** - * Attaches indicator to ViewPager2 - * - * @param pager pager to attach - */ - fun attachToPager(pager: ViewPager2) { - attachToPager(pager, ViewPager2Attacher()) - } - - /** - * Attaches to any custom pager - * - * @param pager pager to attach - * @param attacher helper which should setup this indicator to work with custom pager - */ - fun attachToPager(pager: T, attacher: PagerAttacher) { - detachFromPager() - attacher.attachToPager(this, pager) - currentAttacher = attacher - attachRunnable = Runnable { - itemCount = -1 - attachToPager(pager, attacher) - } - } - - /** - * Detaches indicator from pager. - */ - fun detachFromPager() { - if (currentAttacher != null) { - currentAttacher!!.detachFromPager() - currentAttacher = null - attachRunnable = null - } - dotCountInitialized = false - } - - /** - * Detaches indicator from pager and attaches it again. - * It may be useful for refreshing after adapter count change. - */ - fun reattach() { - if (attachRunnable != null) { - attachRunnable!!.run() - invalidate() - } - } - - /** - * This method must be called from ViewPager.OnPageChangeListener.onPageScrolled or from some - * similar callback if you use custom PagerAttacher. - * - * @param page index of the first page currently being displayed - * Page position+1 will be visible if offset is nonzero - * @param offset Value from [0, 1) indicating the offset from the page at position - */ - fun onPageScrolled(page: Int, offset: Float) { - require(!(offset < 0 || offset > 1)) { "Offset must be [0, 1]" } - if (page < 0 || page != 0 && page >= itemCount) { - throw IndexOutOfBoundsException("page must be [0, adapter.getItemCount())") - } - if (!looped || itemCount <= visibleDotCount && itemCount > 1) { - dotScale!!.clear() - if (orientation == LinearLayout.HORIZONTAL) { - scaleDotByOffset(page, offset) - if (page < itemCount - 1) { - scaleDotByOffset(page + 1, 1 - offset) - } else if (itemCount > 1) { - scaleDotByOffset(0, 1 - offset) - } - } else { // Vertical orientation - scaleDotByOffset(page, offset) - } - invalidate() - } - if (orientation == LinearLayout.HORIZONTAL) { - adjustFramePosition(offset, page) - } else { - adjustFramePosition(offset, page - 1) - } - invalidate() - } - - /** - * Sets currently selected position (according to your pager's adapter) - * - * @param position new current position - */ - fun setCurrentPosition(position: Int) { - if (position != 0 && (position < 0 || position >= itemCount)) { - throw IndexOutOfBoundsException("Position must be [0, adapter.getItemCount()]") - } - if (itemCount == 0) { - return - } - adjustFramePosition(0f, position) - updateScaleInIdleState(position) - } - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - // Width - val measuredWidth: Int - // Height - val measuredHeight: Int - if (orientation == LinearLayoutManager.HORIZONTAL) { - // We ignore widthMeasureSpec because width is based on visibleDotCount - measuredWidth = if (isInEditMode) { - // Maximum width with all dots visible - (visibleDotCount - 1) * spaceBetweenDotCenters + dotSelectedSize - } else { - if (itemCount >= visibleDotCount) visibleFrameWidth.toInt() else (itemCount - 1) * spaceBetweenDotCenters + dotSelectedSize - } - val heightMode = MeasureSpec.getMode(heightMeasureSpec) - val heightSize = MeasureSpec.getSize(heightMeasureSpec) - - // Height - val desiredHeight = dotSelectedSize - measuredHeight = when (heightMode) { - MeasureSpec.EXACTLY -> heightSize - MeasureSpec.AT_MOST -> desiredHeight.coerceAtMost(heightSize) - MeasureSpec.UNSPECIFIED -> desiredHeight - else -> desiredHeight - } - } else { - measuredHeight = if (isInEditMode) { - (visibleDotCount - 1) * spaceBetweenDotCenters + dotSelectedSize - } else { - if (itemCount >= visibleDotCount) visibleFrameWidth.toInt() else (itemCount) * spaceBetweenDotCenters + dotSelectedSize - } - val widthMode = MeasureSpec.getMode(widthMeasureSpec) - val widthSize = MeasureSpec.getSize(widthMeasureSpec) - - // Width - val desiredWidth = dotSelectedSize - measuredWidth = when (widthMode) { - MeasureSpec.EXACTLY -> widthSize - MeasureSpec.AT_MOST -> desiredWidth.coerceAtMost(widthSize) - MeasureSpec.UNSPECIFIED -> desiredWidth - else -> desiredWidth - } - } - setMeasuredDimension(measuredWidth, measuredHeight) - } - - override fun onDraw(canvas: Canvas) { - val dotCount = dotCount - if (dotCount < visibleDotThreshold) { - return - } - - // Some empirical coefficients - val scaleDistance = (spaceBetweenDotCenters + (dotSelectedSize - dotNormalSize) / 2) * 0.7f - val smallScaleDistance = (dotSelectedSize / 2).toFloat() - val centerScaleDistance = 6f / 7f * spaceBetweenDotCenters - val firstVisibleDotPos = - (visibleFramePosition - firstDotOffset).toInt() / spaceBetweenDotCenters - var lastVisibleDotPos = (firstVisibleDotPos - + (visibleFramePosition + visibleFrameWidth - getDotOffsetAt(firstVisibleDotPos)).toInt() - / spaceBetweenDotCenters) - - // If real dots count is less than we can draw inside visible frame, we move lastVisibleDotPos - // to the last item - if (firstVisibleDotPos == 0 && lastVisibleDotPos + 1 > dotCount) { - lastVisibleDotPos = dotCount - 1 - } - for (i in firstVisibleDotPos..lastVisibleDotPos) { - val dot = getDotOffsetAt(i) - if (dot >= visibleFramePosition && dot < visibleFramePosition + visibleFrameWidth) { - var diameter: Float - var scale: Float - - // Calculate scale according to current page position - scale = if (looped && itemCount > visibleDotCount) { - val frameCenter = visibleFramePosition + visibleFrameWidth / 2 - if (dot >= frameCenter - centerScaleDistance - && dot <= frameCenter - ) { - (dot - frameCenter + centerScaleDistance) / centerScaleDistance - } else if (dot > frameCenter - && dot < frameCenter + centerScaleDistance - ) { - 1 - (dot - frameCenter) / centerScaleDistance - } else { - 0f - } - } else { - getDotScaleAt(i) - } - diameter = dotNormalSize + (dotSelectedSize - dotNormalSize) * scale - - // Additional scale for dots at corners - if (itemCount > visibleDotCount) { - val currentScaleDistance = if (!looped && (i == 0 || i == dotCount - 1)) { - smallScaleDistance - } else { - scaleDistance - } - var size = width - if (orientation == LinearLayoutManager.VERTICAL) { - size = height - } - if (dot - visibleFramePosition < currentScaleDistance) { - val calculatedDiameter = - diameter * (dot - visibleFramePosition) / currentScaleDistance - if (calculatedDiameter <= dotMinimumSize) { - diameter = dotMinimumSize.toFloat() - } else if (calculatedDiameter < diameter) { - diameter = calculatedDiameter - } - } else if (dot - visibleFramePosition > size - currentScaleDistance) { - val calculatedDiameter = - diameter * (-dot + visibleFramePosition + size) / currentScaleDistance - if (calculatedDiameter <= dotMinimumSize) { - diameter = dotMinimumSize.toFloat() - } else if (calculatedDiameter < diameter) { - diameter = calculatedDiameter - } - } - } - paint.color = calculateDotColor(scale) - if (orientation == LinearLayoutManager.HORIZONTAL) { - canvas.drawCircle(dot - visibleFramePosition, ( - measuredHeight / 2).toFloat(), - diameter / 2, - paint) - } else { - canvas.drawCircle((measuredWidth / 2).toFloat(), - dot - visibleFramePosition, - diameter / 2, - paint) - } - } - } - } - - @ColorInt - private fun calculateDotColor(dotScale: Float): Int { - return colorEvaluator.evaluate(dotScale, dotColor, selectedDotColor) as Int - } - - private fun updateScaleInIdleState(currentPos: Int) { - if (!looped || itemCount < visibleDotCount) { - dotScale!!.clear() - dotScale!!.put(currentPos, 1f) - invalidate() - } - } - - private fun initDots(itemCount: Int) { - if (this.itemCount == itemCount && dotCountInitialized) { - return - } - this.itemCount = itemCount - dotCountInitialized = true - dotScale = SparseArray() - if (itemCount < visibleDotThreshold) { - requestLayout() - invalidate() - return - } - firstDotOffset = - if (looped && this.itemCount > visibleDotCount) 0f else dotSelectedSize / 2.toFloat() - visibleFrameWidth = - ((visibleDotCount - 1) * spaceBetweenDotCenters + dotSelectedSize).toFloat() - requestLayout() - invalidate() - } - - private fun adjustFramePosition(offset: Float, pos: Int) { - if (itemCount <= visibleDotCount) { - // Without scroll - visibleFramePosition = 0f - } else if (!looped && itemCount > visibleDotCount) { - // Not looped with scroll - val center = getDotOffsetAt(pos) + spaceBetweenDotCenters * offset - visibleFramePosition = center - visibleFrameWidth / 2 - - // Block frame offset near start and end - val firstCenteredDotIndex = visibleDotCount / 2 - val lastCenteredDot = getDotOffsetAt(dotCount - 1 - firstCenteredDotIndex) - if (visibleFramePosition + visibleFrameWidth / 2 < getDotOffsetAt(firstCenteredDotIndex)) { - visibleFramePosition = getDotOffsetAt(firstCenteredDotIndex) - visibleFrameWidth / 2 - } else if (visibleFramePosition + visibleFrameWidth / 2 > lastCenteredDot) { - visibleFramePosition = lastCenteredDot - visibleFrameWidth / 2 - } - } else { - // Looped with scroll - val center = getDotOffsetAt(infiniteDotCount / 2) + spaceBetweenDotCenters * offset - visibleFramePosition = center - visibleFrameWidth / 2 - } - } - - private fun scaleDotByOffset(position: Int, offset: Float) { - if (dotScale == null || dotCount == 0) { - return - } - setDotScaleAt(position, 1 - Math.abs(offset)) - } - - private fun getDotOffsetAt(index: Int): Float { - return firstDotOffset + index * spaceBetweenDotCenters - } - - private fun getDotScaleAt(index: Int): Float { - val scale = dotScale!![index] - return scale ?: 0f - } - - private fun setDotScaleAt(index: Int, scale: Float) { - if (scale == 0f) { - dotScale!!.remove(index) - } else { - dotScale!!.put(index, scale) - } - } - - /** - * Interface for attaching to custom pagers. - * - * @param custom pager's class - */ - interface PagerAttacher { - /** - * Here you should add all needed callbacks to track pager's item count, position and offset - * You must call: - * [PagerIndicator.setDotCount] - initially and after page selection, - * [PagerIndicator.setCurrentPosition] - initially and after page selection, - * [PagerIndicator.onPageScrolled] - in your pager callback to track scroll offset, - * [PagerIndicator.reattach] - each time your adapter items change. - * - * @param indicator indicator - * @param pager pager to attach - */ - fun attachToPager(indicator: PagerIndicator, pager: T) - - /** - * Here you should unregister all callbacks previously added to pager and adapter - */ - fun detachFromPager() - } -} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt b/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt deleted file mode 100644 index 4d5bd6d4..00000000 --- a/app/src/main/kotlin/com/simplemobiletools/clock/views/pageindicator/ViewPager2Attacher.kt +++ /dev/null @@ -1,80 +0,0 @@ -package com.simplemobiletools.clock.views.pageindicator - -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver -import androidx.viewpager2.widget.ViewPager2 -import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback - -/** - * ViewPager2 Attacher for [PagerIndicator] -) */ -class ViewPager2Attacher : PagerIndicator.PagerAttacher { - private var dataSetObserver: AdapterDataObserver? = null - private var attachedAdapter: RecyclerView.Adapter<*>? = null - private var onPageChangeListener: OnPageChangeCallback? = null - private var pager: ViewPager2? = null - - override fun attachToPager(indicator: PagerIndicator, pager: ViewPager2) { - attachedAdapter = pager.adapter - checkNotNull(attachedAdapter) { "Set adapter before call attachToPager() method" } - this.pager = pager - updateIndicatorDotsAndPosition(indicator) - dataSetObserver = object : AdapterDataObserver() { - override fun onChanged() { - indicator.reattach() - } - } - attachedAdapter!!.registerAdapterDataObserver(dataSetObserver!!) - onPageChangeListener = object : OnPageChangeCallback() { - var idleState = true - override fun onPageScrolled( - position: Int, - positionOffset: Float, - positionOffsetPixel: Int - ) { - updateIndicatorOnPagerScrolled(indicator, position, positionOffset) - } - - override fun onPageSelected(position: Int) { - if (idleState) { - updateIndicatorDotsAndPosition(indicator) - } - } - - override fun onPageScrollStateChanged(state: Int) { - idleState = state == ViewPager2.SCROLL_STATE_IDLE - } - } - pager.registerOnPageChangeCallback(onPageChangeListener!!) - } - - override fun detachFromPager() { - attachedAdapter!!.unregisterAdapterDataObserver(dataSetObserver!!) - pager!!.unregisterOnPageChangeCallback(onPageChangeListener!!) - } - - private fun updateIndicatorDotsAndPosition(indicator: PagerIndicator) { - indicator.dotCount = attachedAdapter!!.itemCount - indicator.setCurrentPosition(pager!!.currentItem) - } - - private fun updateIndicatorOnPagerScrolled( - indicator: PagerIndicator, - position: Int, - positionOffset: Float - ) { - // ViewPager may emit negative positionOffset for very fast scrolling - val offset: Float = when { - positionOffset < 0 -> { - 0f - } - positionOffset > 1 -> { - 1f - } - else -> { - positionOffset - } - } - indicator.onPageScrolled(position, offset) - } -} diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml deleted file mode 100644 index 9ae4336c..00000000 --- a/app/src/main/res/values/attrs.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d6daf9cf..0be50415 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -2,14 +2,4 @@ From e1357ecd8ff09903bd9677d7beba2b0b83a676d4 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Tue, 7 Sep 2021 15:24:21 +0100 Subject: [PATCH 11/32] Implement concise view for timer - implement EditTimerDialog - adding a new timer would show the dialog - clicking on a timer item would show the dialog --- .../clock/adapters/TimerAdapter.kt | 79 +------ .../clock/dialogs/EditTimerDialog.kt | 115 ++++++++++ .../clock/extensions/Context.kt | 10 +- .../clock/fragments/TimerFragment.kt | 24 ++- .../simplemobiletools/clock/helpers/Config.kt | 7 + .../clock/helpers/Constants.kt | 1 + .../clock/helpers/TimerHelper.kt | 21 -- .../simplemobiletools/clock/models/Timer.kt | 16 +- app/src/main/res/layout/dialog_edit_timer.xml | 96 +++++++++ app/src/main/res/layout/item_timer.xml | 200 +++++++----------- 10 files changed, 330 insertions(+), 239 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt create mode 100644 app/src/main/res/layout/dialog_edit_timer.xml diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index cd86c346..e5f864db 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -1,8 +1,6 @@ package com.simplemobiletools.clock.adapters import android.graphics.Color -import android.media.AudioManager -import android.media.RingtoneManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -13,19 +11,17 @@ 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.helpers.PICK_AUDIO_FILE_INTENT_ID import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState -import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog import com.simplemobiletools.commons.extensions.* -import com.simplemobiletools.commons.models.AlarmSound 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(diffUtil) { companion object { @@ -42,12 +38,7 @@ class TimerAdapter( private val config = activity.config private var textColor = config.textColor - private var primaryColor = config.primaryColor private var adjustedPrimaryColor = activity.getAdjustedPrimaryColor() - private var contrastColor = adjustedPrimaryColor.getContrastColor() - private var backgroundColor = config.backgroundColor - - private var selectedTimer: Timer? = null override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TimerViewHolder { return TimerViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_timer, parent, false)) @@ -62,29 +53,11 @@ class TimerAdapter( onRefresh.invoke() } - fun updatePrimaryColor(primaryColor: Int) { - this.primaryColor = primaryColor - adjustedPrimaryColor = activity.getAdjustedPrimaryColor() - contrastColor = adjustedPrimaryColor.getContrastColor() - onRefresh.invoke() - } - inner class TimerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - init { - itemView.timer_label.onTextChangeListener { text -> - updateTimer(getItem(adapterPosition).copy(label = text), false) - } - - } - fun bind(timer: Timer) { itemView.apply { post { - timer_initial_time.colorLeftDrawable(textColor) - timer_vibrate.colorLeftDrawable(textColor) - timer_label_image.applyColorFilter(textColor) - timer_sound.colorLeftDrawable(textColor) 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) @@ -97,16 +70,6 @@ class TimerAdapter( timer_label.setText(timer.label) } - timer_initial_time.text = timer.seconds.getFormattedDuration() - timer_initial_time.setTextColor(textColor) - - timer_vibrate.isChecked = timer.vibrate - timer_vibrate.setTextColor(textColor) - timer_vibrate.setColors(textColor, adjustedPrimaryColor, backgroundColor) - timer_vibrate_holder.setOnClickListener { - timer_vibrate.toggle() - updateTimer(timer.copy(vibrate = timer_vibrate.isChecked, channelId = null), false) - } timer_time.setTextColor(textColor) timer_time.text = when (timer.state) { @@ -119,33 +82,6 @@ class TimerAdapter( changeDuration(timer) } - timer_initial_time.setTextColor(textColor) - timer_initial_time.setOnClickListener { - changeDuration(timer) - } - - - timer_sound.text = timer.soundTitle - timer_sound.setTextColor(textColor) - timer_sound.setOnClickListener { - selectedTimer = timer - SelectAlarmSoundDialog(activity, timer.soundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, - RingtoneManager.TYPE_ALARM, true, - onAlarmPicked = { sound -> - if (sound != null) { - updateAlarmSound(timer, sound) - } - }, - onAlarmSoundDeleted = { sound -> - if (timer.soundUri == sound.uri) { - val defaultAlarm = context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM) - updateAlarmSound(timer, defaultAlarm) - } - - context.checkAlarmsWithDeletedSoundUri(sound.uri) - }) - } - timer_delete.applyColorFilter(textColor) timer_delete.setOnClickListener { activity.timerHelper.deleteTimer(timer.id!!) { @@ -167,7 +103,12 @@ class TimerAdapter( is TimerState.Finished -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis)) } } + updateViewStates(timer.state) + + setOnClickListener { + onClick.invoke(timer) + } } } @@ -188,14 +129,6 @@ class TimerAdapter( } } - fun updateAlarmSoundForSelectedTimer(alarmSound: AlarmSound) { - selectedTimer?.let { updateAlarmSound(it, alarmSound) } - } - - fun updateAlarmSound(timer: Timer, alarmSound: AlarmSound) { - updateTimer(timer.copy(soundTitle = alarmSound.title, soundUri = alarmSound.uri, channelId = null)) - } - private fun updateTimer(timer: Timer, refresh: Boolean = true) { activity.timerHelper.insertOrUpdateTimer(timer) { if (refresh) { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt b/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt new file mode 100644 index 00000000..0057e9ab --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt @@ -0,0 +1,115 @@ +package com.simplemobiletools.clock.dialogs + +import android.media.AudioManager +import android.media.RingtoneManager +import androidx.appcompat.app.AlertDialog +import com.simplemobiletools.clock.R +import com.simplemobiletools.clock.activities.SimpleActivity +import com.simplemobiletools.clock.extensions.* +import com.simplemobiletools.clock.helpers.PICK_AUDIO_FILE_INTENT_ID +import com.simplemobiletools.clock.models.Timer +import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog +import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.commons.models.AlarmSound +import kotlinx.android.synthetic.main.dialog_edit_timer.view.* + +class EditTimerDialog(val activity: SimpleActivity, val timer: Timer, val callback: () -> Unit) { + private val view = activity.layoutInflater.inflate(R.layout.dialog_edit_timer, null) + private val textColor = activity.config.textColor + + init { + restoreLastAlarm() + updateAlarmTime() + + view.apply { + + edit_timer_initial_time.text = timer.seconds.getFormattedDuration() + edit_timer_initial_time.setTextColor(textColor) + edit_timer_initial_time.setOnClickListener { + changeDuration(timer) + } + + edit_timer_vibrate.colorLeftDrawable(textColor) + edit_timer_vibrate.isChecked = timer.vibrate + edit_timer_vibrate.setTextColor(textColor) + edit_timer_vibrate_holder.setOnClickListener { + edit_timer_vibrate.toggle() + timer.vibrate = edit_timer_vibrate.isChecked + timer.channelId = null + } + + edit_timer_sound.colorLeftDrawable(textColor) + edit_timer_sound.text = timer.soundTitle + edit_timer_sound.setOnClickListener { + SelectAlarmSoundDialog(activity, timer.soundUri, AudioManager.STREAM_ALARM, PICK_AUDIO_FILE_INTENT_ID, + RingtoneManager.TYPE_ALARM, true, + onAlarmPicked = { sound -> + if (sound != null) { + updateAlarmSound(sound) + } + }, + onAlarmSoundDeleted = { sound -> + if (timer.soundUri == sound.uri) { + val defaultAlarm = context.getDefaultAlarmSound(RingtoneManager.TYPE_ALARM) + updateAlarmSound(defaultAlarm) + } + + context.checkAlarmsWithDeletedSoundUri(sound.uri) + }) + } + + + edit_timer_label_image.applyColorFilter(textColor) + edit_timer_label.setText(timer.label) + + } + + AlertDialog.Builder(activity) + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .create().apply { + activity.setupDialogStuff(view, this) { + getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { + timer.label = view.edit_timer_label.value + activity.timerHelper.insertOrUpdateTimer(timer){ + activity.config.timerLastConfig = timer + callback() + dismiss() + } + } + } + } + } + + private fun restoreLastAlarm() { + if (timer.id == null) { + activity.config.timerLastConfig?.let { lastConfig -> + timer.label = lastConfig.label + timer.seconds = lastConfig.seconds + timer.soundTitle = lastConfig.soundTitle + timer.soundUri = lastConfig.soundUri + timer.vibrate = lastConfig.vibrate + } + } + } + + private fun updateAlarmTime() { + view.edit_timer_initial_time.text = activity.getFormattedTime(timer.seconds * 60, false, true) + } + + private fun changeDuration(timer: Timer) { + MyTimePickerDialogDialog(activity, timer.seconds) { seconds -> + val timerSeconds = if (seconds <= 0) 10 else seconds + timer.seconds = timerSeconds + view.edit_timer_initial_time.text = timerSeconds.getFormattedDuration() + } + } + + fun updateAlarmSound(alarmSound: AlarmSound) { + timer.soundTitle = alarmSound.title + timer.soundUri = alarmSound.uri + timer.channelId = null + view.edit_timer_sound.text = alarmSound.title + } + +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index a30e1598..cfe71eee 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -85,6 +85,10 @@ fun Context.createNewAlarm(timeInMinutes: Int, weekDays: Int): Alarm { return Alarm(0, timeInMinutes, weekDays, false, false, defaultAlarmSound.title, defaultAlarmSound.uri, "") } +fun Context.createNewTimer(): Timer { + return Timer(null, config.timerSeconds, config.timerState, config.timerVibrate, config.timerSoundUri, config.timerSoundTitle, config.timerLabel ?: "", System.currentTimeMillis(), config.timerChannelId, ) +} + fun Context.scheduleNextAlarm(alarm: Alarm, showToast: Boolean) { val calendar = Calendar.getInstance() calendar.firstDayOfWeek = Calendar.MONDAY @@ -166,7 +170,8 @@ fun Context.hideNotification(id: Int) { fun Context.hideTimerNotification() = hideNotification(TIMER_NOTIF_ID) fun Context.updateWidgets() { - val widgetsCnt = AppWidgetManager.getInstance(applicationContext)?.getAppWidgetIds(ComponentName(applicationContext, MyWidgetDateTimeProvider::class.java)) ?: return + val widgetsCnt = + AppWidgetManager.getInstance(applicationContext)?.getAppWidgetIds(ComponentName(applicationContext, MyWidgetDateTimeProvider::class.java)) ?: return if (widgetsCnt.isNotEmpty()) { val ids = intArrayOf(R.xml.widget_date_time_info) Intent(applicationContext, MyWidgetDateTimeProvider::class.java).apply { @@ -179,7 +184,8 @@ fun Context.updateWidgets() { @SuppressLint("NewApi") fun Context.scheduleNextWidgetUpdate() { - val widgetsCnt = AppWidgetManager.getInstance(applicationContext)?.getAppWidgetIds(ComponentName(applicationContext, MyWidgetDateTimeProvider::class.java)) ?: return + val widgetsCnt = + AppWidgetManager.getInstance(applicationContext)?.getAppWidgetIds(ComponentName(applicationContext, MyWidgetDateTimeProvider::class.java)) ?: return if (widgetsCnt.isEmpty()) { return } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index ef8e7fd8..72c1aea5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -8,8 +8,11 @@ import androidx.fragment.app.Fragment import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity import com.simplemobiletools.clock.adapters.TimerAdapter +import com.simplemobiletools.clock.dialogs.EditTimerDialog import com.simplemobiletools.clock.extensions.config +import com.simplemobiletools.clock.extensions.createNewTimer import com.simplemobiletools.clock.extensions.timerHelper +import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.commons.extensions.hideKeyboard import com.simplemobiletools.commons.extensions.updateTextColors @@ -27,6 +30,7 @@ class TimerFragment : Fragment() { private lateinit var timerAdapter: TimerAdapter private var timerPositionToScrollTo = INVALID_POSITION private var storedTextColor = 0 + private var currentEditAlarmDialog: EditTimerDialog? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -40,9 +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() - } + timerAdapter = TimerAdapter(requireActivity() as SimpleActivity, ::refreshTimers, ::openEditTimer) storeStateVariables() @@ -50,9 +52,9 @@ class TimerFragment : Fragment() { timers_list.itemAnimator = null timer_add.setOnClickListener { - activity?.hideKeyboard(it) - activity?.timerHelper?.insertNewTimer { - refreshTimers(true) + activity?.run { + hideKeyboard() + openEditTimer(createNewTimer()) } } @@ -75,7 +77,6 @@ class TimerFragment : Fragment() { storeStateVariables() } - private fun refreshTimers(scrollToLatest: Boolean = false) { activity?.timerHelper?.getTimers { timers -> timerAdapter.submitList(timers) { @@ -101,7 +102,7 @@ class TimerFragment : Fragment() { } fun updateAlarmSound(alarmSound: AlarmSound) { - timerAdapter.updateAlarmSoundForSelectedTimer(alarmSound) + currentEditAlarmDialog?.updateAlarmSound(alarmSound) } fun updatePosition(timerId: Long) { @@ -118,4 +119,11 @@ class TimerFragment : Fragment() { } } } + + private fun openEditTimer(timer: Timer) { + currentEditAlarmDialog = EditTimerDialog(activity as SimpleActivity, timer) { + currentEditAlarmDialog = null + refreshTimers() + } + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt index 67c5ec41..3fb71c78 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Config.kt @@ -5,6 +5,7 @@ import android.media.RingtoneManager import com.simplemobiletools.clock.extensions.gson.gson import com.simplemobiletools.clock.models.Alarm import com.simplemobiletools.clock.models.StateWrapper +import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.commons.extensions.getDefaultAlarmSound import com.simplemobiletools.commons.extensions.getDefaultAlarmTitle @@ -79,6 +80,12 @@ class Config(context: Context) : BaseConfig(context) { } set(alarm) = prefs.edit().putString(ALARM_LAST_CONFIG, gson.toJson(alarm)).apply() + var timerLastConfig: Timer? + get() = prefs.getString(TIMER_LAST_CONFIG, null)?.let { lastAlarm -> + gson.fromJson(lastAlarm, Timer::class.java) + } + set(alarm) = prefs.edit().putString(TIMER_LAST_CONFIG, gson.toJson(alarm)).apply() + var timerChannelId: String? get() = prefs.getString(TIMER_CHANNEL_ID, null) set(id) = prefs.edit().putString(TIMER_CHANNEL_ID, id).apply() diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index 0bf22bc3..c99fe5a1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -20,6 +20,7 @@ const val TIMER_LABEL = "timer_label" const val TIMER_MAX_REMINDER_SECS = "timer_max_reminder_secs" const val ALARM_MAX_REMINDER_SECS = "alarm_max_reminder_secs" const val ALARM_LAST_CONFIG = "alarm_last_config" +const val TIMER_LAST_CONFIG = "timer_last_config" const val USE_TEXT_SHADOW = "use_text_shadow" const val INCREASE_VOLUME_GRADUALLY = "increase_volume_gradually" const val ALARMS_SORT_BY = "alarms_sort_by" diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index fb6771d4..ba255986 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -1,7 +1,6 @@ package com.simplemobiletools.clock.helpers import android.content.Context -import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.timerDb import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.commons.helpers.ensureBackgroundThread @@ -34,24 +33,4 @@ class TimerHelper(val context: Context) { callback.invoke() } } - - fun insertNewTimer(callback: () -> Unit = {}) { - ensureBackgroundThread { - val config = context.config - timerDao.insertOrUpdateTimer( - Timer( - id = null, - seconds = config.timerSeconds, - state = config.timerState, - vibrate = config.timerVibrate, - soundUri = config.timerSoundUri, - soundTitle = config.timerSoundTitle, - label = config.timerLabel ?: "", - createdAt = System.currentTimeMillis(), - channelId = config.timerChannelId, - ) - ) - callback.invoke() - } - } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt index bd098589..17062640 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt @@ -5,13 +5,13 @@ import androidx.room.PrimaryKey @Entity(tableName = "timers") data class Timer( - @PrimaryKey(autoGenerate = true) val id: Long?, - val seconds: Int, + @PrimaryKey(autoGenerate = true) var id: Long?, + var seconds: Int, val state: TimerState, - val vibrate: Boolean, - val soundUri: String, - val soundTitle: String, - val label: String, - val createdAt: Long, - val channelId: String? = null, + var vibrate: Boolean, + var soundUri: String, + var soundTitle: String, + var label: String, + var createdAt: Long, + var channelId: String? = null, ) diff --git a/app/src/main/res/layout/dialog_edit_timer.xml b/app/src/main/res/layout/dialog_edit_timer.xml new file mode 100644 index 00000000..f72ba1b4 --- /dev/null +++ b/app/src/main/res/layout/dialog_edit_timer.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index 1b6b4c39..96ea9bdf 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -1,145 +1,91 @@ - + android:layout_height="wrap_content" + android:background="?selectableItemBackground" + android:clickable="true" + android:focusable="true" + android:foreground="@drawable/selector"> - + android:layout_height="wrap_content"> - - - - - + android:layout_marginTop="@dimen/bigger_margin" + android:background="?attr/selectableItemBackground" + android:gravity="center_horizontal" + android:padding="@dimen/small_margin" + android:textSize="@dimen/stopwatch_text_size" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="00:00" /> - - - - - - - - - - + android:paddingStart="@dimen/small_margin" + android:paddingEnd="@dimen/small_margin" + android:textSize="@dimen/bigger_text_size" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/timer_time" + tools:text="Cook rice" /> - - + - + - + + + + From db3e0c9d0745faebeecb85f34f540df6fb4a5b05 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Tue, 7 Sep 2021 23:15:33 +0100 Subject: [PATCH 12/32] Implement action mode, select to delete --- app/build.gradle | 3 +- .../kotlin/com/simplemobiletools/clock/App.kt | 4 +- .../clock/activities/MainActivity.kt | 4 +- .../clock/activities/SplashActivity.kt | 2 +- .../clock/adapters/TimerAdapter.kt | 203 ++++++++++-------- .../clock/adapters/ViewPagerAdapter.kt | 2 +- .../clock/extensions/Context.kt | 2 +- .../clock/fragments/TimerFragment.kt | 4 +- .../clock/helpers/Constants.kt | 2 +- .../clock/helpers/TimerHelper.kt | 11 +- .../clock/interfaces/TimerDao.kt | 12 +- .../simplemobiletools/clock/models/Timer.kt | 2 +- .../clock/models/TimerEvent.kt | 12 +- .../clock/services/TimerService.kt | 2 +- app/src/main/res/layout/item_timer.xml | 1 + 15 files changed, 154 insertions(+), 112 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6a149577..e82a167e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index 7bc445fb..308a3c18 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -26,7 +26,7 @@ import org.greenrobot.eventbus.ThreadMode class App : Application(), LifecycleObserver { - private var countDownTimers = mutableMapOf() + private var countDownTimers = mutableMapOf() 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) { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt index 015e49af..1d80297f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/activities/MainActivity.kt @@ -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 diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt b/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt index 164ace18..d2d8c736 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/activities/SplashActivity.kt @@ -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) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index e5f864db..208be250 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -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(diffUtil) { + private val simpleActivity: SimpleActivity, + recyclerView: MyRecyclerView, + onRefresh: () -> Unit, + private val onItemClick: (Timer) -> Unit, +) : MyRecyclerViewListAdapter(simpleActivity, recyclerView, diffUtil, null, onItemClick, onRefresh) { companion object { private val diffUtil = object : DiffUtil.ItemCallback() { @@ -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() } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt index bc2d9a97..6f795b98 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/ViewPagerAdapter.kt @@ -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) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index cfe71eee..6bf10623 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -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) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 72c1aea5..d5249287 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -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) { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index c99fe5a1..5e304fef 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -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 diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt index ba255986..e51bede1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/TimerHelper.kt @@ -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, callback: () -> Unit = {}) { + ensureBackgroundThread { + timerDao.deleteTimers(timers) + callback.invoke() + } + } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt index fd64f8d2..834a6413 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/interfaces/TimerDao.kt @@ -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 @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) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt index 17062640..db663925 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/Timer.kt @@ -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, diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt index c5ae5ec5..938e3cd5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt @@ -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) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index 40996000..858d06f1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -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 diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index 96ea9bdf..f8e80a18 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -2,6 +2,7 @@ Date: Tue, 7 Sep 2021 23:34:30 +0100 Subject: [PATCH 13/32] Implement concise view for timer --- .../clock/adapters/TimerAdapter.kt | 34 +------------- app/src/main/res/layout/item_timer.xml | 45 +++++++------------ 2 files changed, 16 insertions(+), 63 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 208be250..0c6d42c1 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import com.simplemobiletools.clock.R import com.simplemobiletools.clock.activities.SimpleActivity -import com.simplemobiletools.clock.dialogs.MyTimePickerDialogDialog import com.simplemobiletools.clock.extensions.getFormattedDuration import com.simplemobiletools.clock.extensions.hideTimerNotification import com.simplemobiletools.clock.extensions.secondsToMillis @@ -25,7 +24,7 @@ class TimerAdapter( private val simpleActivity: SimpleActivity, recyclerView: MyRecyclerView, onRefresh: () -> Unit, - private val onItemClick: (Timer) -> Unit, + onItemClick: (Timer) -> Unit, ) : MyRecyclerViewListAdapter(simpleActivity, recyclerView, diffUtil, null, onItemClick, onRefresh) { companion object { @@ -75,12 +74,10 @@ class TimerAdapter( 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) { @@ -101,7 +98,6 @@ class TimerAdapter( } } - private fun setupView(view: View, timer: Timer) { view.apply { val isSelected = selectedKeys.contains(timer.id) @@ -118,23 +114,12 @@ class TimerAdapter( 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) { @@ -148,31 +133,14 @@ class TimerAdapter( val state = timer.state val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished 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 timer_play_pause.setImageDrawable(simpleActivity.resources.getColoredDrawableWithColor(drawableId, iconColor)) } } - private fun changeDuration(timer: Timer) { - 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) { - simpleActivity.timerHelper.insertOrUpdateTimer(timer) { - if (refresh) { - onRefresh.invoke() - } - } - } - private fun stopTimer(timer: Timer) { EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) simpleActivity.hideTimerNotification() } - } diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index f8e80a18..8a710a00 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -12,34 +12,30 @@ + android:layout_height="wrap_content" + android:paddingLeft="@dimen/activity_margin" + android:paddingTop="@dimen/medium_margin" + android:paddingBottom="@dimen/medium_margin"> @@ -49,28 +45,17 @@ android:id="@+id/timer_reset" android:layout_width="@dimen/stopwatch_button_small_size" android:layout_height="@dimen/stopwatch_button_small_size" + android:layout_marginStart="@dimen/bigger_margin" + android:layout_marginEnd="@dimen/bigger_margin" android:background="?attr/selectableItemBackgroundBorderless" android:padding="@dimen/normal_margin" android:src="@drawable/ic_reset_vector" android:visibility="visible" app:layout_constraintBottom_toBottomOf="@+id/timer_play_pause" app:layout_constraintEnd_toStartOf="@+id/timer_play_pause" - app:layout_constraintStart_toStartOf="parent" + app:layout_constraintStart_toEndOf="@id/timer_time" app:layout_constraintTop_toTopOf="@id/timer_play_pause" /> - + app:layout_constraintStart_toEndOf="@id/timer_reset" + app:layout_constraintTop_toTopOf="parent" /> From a214ef9327d709c066154fcfed5fe72388f2a0f7 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 8 Sep 2021 00:03:49 +0100 Subject: [PATCH 14/32] Handle deletion of timers when running --- .../kotlin/com/simplemobiletools/clock/App.kt | 12 ++++++++++-- .../clock/adapters/TimerAdapter.kt | 16 +++++++++------- .../clock/extensions/Context.kt | 11 ++++++----- .../simplemobiletools/clock/helpers/Constants.kt | 1 - .../simplemobiletools/clock/models/TimerEvent.kt | 1 + .../clock/receivers/HideTimerReceiver.kt | 5 ++++- 6 files changed, 30 insertions(+), 16 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index 308a3c18..af18cac4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -14,7 +14,7 @@ import com.facebook.stetho.Stetho import com.simplemobiletools.clock.extensions.getOpenTimerTabIntent import com.simplemobiletools.clock.extensions.getTimerNotification import com.simplemobiletools.clock.extensions.timerHelper -import com.simplemobiletools.clock.helpers.TIMER_NOTIF_ID +import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.clock.services.TimerStopService @@ -74,6 +74,14 @@ class App : Application(), LifecycleObserver { countDownTimers[event.timerId]?.cancel() } + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageEvent(event: TimerEvent.Delete) { + countDownTimers[event.timerId]?.cancel() + timerHelper.deleteTimer(event.timerId){ + EventBus.getDefault().post(TimerEvent.Refresh(INVALID_TIMER_ID)) + } + } + @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Start) { val countDownTimer = object : CountDownTimer(event.duration, 1000) { @@ -95,7 +103,7 @@ class App : Application(), LifecycleObserver { val pendingIntent = getOpenTimerTabIntent(event.timerId) val notification = getTimerNotification(timer, pendingIntent, false) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - notificationManager.notify(TIMER_NOTIF_ID, notification) + notificationManager.notify(event.timerId, notification) updateTimerState(event.timerId, TimerState.Finished) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 0c6d42c1..22544b96 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -10,7 +10,6 @@ import com.simplemobiletools.clock.activities.SimpleActivity 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 @@ -93,9 +92,7 @@ class TimerAdapter( getItem(position) } removeSelectedItems(positions) - activity.timerHelper.deleteTimers(timersToRemove) { - onRefresh.invoke() - } + timersToRemove.forEach(::deleteTimer) } private fun setupView(view: View, timer: Timer) { @@ -117,7 +114,7 @@ class TimerAdapter( timer_reset.applyColorFilter(textColor) timer_reset.setOnClickListener { - stopTimer(timer) + resetTimer(timer) } timer_play_pause.applyColorFilter(if (adjustedPrimaryColor == Color.WHITE) Color.BLACK else Color.WHITE) @@ -139,8 +136,13 @@ class TimerAdapter( } } - private fun stopTimer(timer: Timer) { + private fun resetTimer(timer: Timer) { EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) - simpleActivity.hideTimerNotification() + simpleActivity.hideTimerNotification(timer.id!!) + } + + private fun deleteTimer(timer: Timer) { + EventBus.getDefault().post(TimerEvent.Delete(timer.id!!)) + simpleActivity.hideTimerNotification(timer.id!!) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index 6bf10623..a261061a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -148,7 +148,7 @@ 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) - return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) + return PendingIntent.getActivity(this, timerId, intent, PendingIntent.FLAG_UPDATE_CURRENT) } fun Context.getAlarmIntent(alarm: Alarm): PendingIntent { @@ -167,7 +167,7 @@ fun Context.hideNotification(id: Int) { manager.cancel(id) } -fun Context.hideTimerNotification() = hideNotification(TIMER_NOTIF_ID) +fun Context.hideTimerNotification(timerId: Int) = hideNotification(timerId) fun Context.updateWidgets() { val widgetsCnt = @@ -321,7 +321,7 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add .setAutoCancel(true) .setSound(Uri.parse(soundUri), STREAM_ALARM) .setChannelId(channelId) - .addAction(R.drawable.ic_cross_vector, getString(R.string.dismiss), if (addDeleteIntent) reminderActivityIntent else getHideTimerPendingIntent()) + .addAction(R.drawable.ic_cross_vector, getString(R.string.dismiss), if (addDeleteIntent) reminderActivityIntent else getHideTimerPendingIntent(timer.id!!)) if (addDeleteIntent) { builder.setDeleteIntent(reminderActivityIntent) @@ -339,9 +339,10 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add return notification } -fun Context.getHideTimerPendingIntent(): PendingIntent { +fun Context.getHideTimerPendingIntent(timerId: Int): PendingIntent { val intent = Intent(this, HideTimerReceiver::class.java) - return PendingIntent.getBroadcast(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT) + intent.putExtra(TIMER_ID, timerId) + return PendingIntent.getBroadcast(this, timerId, intent, PendingIntent.FLAG_UPDATE_CURRENT) } fun Context.getHideAlarmPendingIntent(alarm: Alarm): PendingIntent { diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt index 5e304fef..466098df 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/Constants.kt @@ -38,7 +38,6 @@ const val OPEN_ALARMS_TAB_INTENT_ID = 9996 const val UPDATE_WIDGET_INTENT_ID = 9997 const val OPEN_APP_INTENT_ID = 9998 const val ALARM_NOTIF_ID = 9998 -const val TIMER_NOTIF_ID = 9999 const val TIMER_RUNNING_NOTIF_ID = 10000 const val OPEN_TAB = "open_tab" diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt index 938e3cd5..cc8b64a5 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt @@ -1,6 +1,7 @@ package com.simplemobiletools.clock.models sealed class TimerEvent(open val timerId: Int) { + data class Delete(override val timerId: Int) : TimerEvent(timerId) 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) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt index 7866a314..116a27e0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt @@ -4,12 +4,15 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.simplemobiletools.clock.extensions.hideTimerNotification +import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID +import com.simplemobiletools.clock.helpers.TIMER_ID import com.simplemobiletools.clock.models.TimerState import org.greenrobot.eventbus.EventBus class HideTimerReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - context.hideTimerNotification() + val timerId = intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID) + context.hideTimerNotification(timerId) EventBus.getDefault().post(TimerState.Idle) } } From a91bc07dd3092815316a6ac44c1719bcf9684717 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 8 Sep 2021 09:04:25 +0100 Subject: [PATCH 15/32] Handle notification for multiple timers - update title for timer notification in finished state to be the label if it is empty - remove duration field from the TimerEvent.Reset --- .../com/simplemobiletools/clock/adapters/TimerAdapter.kt | 2 +- .../kotlin/com/simplemobiletools/clock/extensions/Context.kt | 2 +- .../kotlin/com/simplemobiletools/clock/models/TimerEvent.kt | 2 +- .../simplemobiletools/clock/receivers/HideTimerReceiver.kt | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 22544b96..589417e7 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -137,7 +137,7 @@ class TimerAdapter( } private fun resetTimer(timer: Timer) { - EventBus.getDefault().post(TimerEvent.Reset(timer.id!!, timer.seconds.secondsToMillis)) + EventBus.getDefault().post(TimerEvent.Reset(timer.id!!)) simpleActivity.hideTimerNotification(timer.id!!) } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt index a261061a..8dc24594 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/extensions/Context.kt @@ -311,7 +311,7 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add val reminderActivityIntent = getReminderActivityIntent() val builder = NotificationCompat.Builder(this) - .setContentTitle(getString(R.string.timer)) + .setContentTitle(if(timer.label.isEmpty()) getString(R.string.timer) else timer.label) .setContentText(getString(R.string.time_expired)) .setSmallIcon(R.drawable.ic_timer) .setContentIntent(pendingIntent) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt index cc8b64a5..b88a4148 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt @@ -2,7 +2,7 @@ package com.simplemobiletools.clock.models sealed class TimerEvent(open val timerId: Int) { data class Delete(override val timerId: Int) : TimerEvent(timerId) - data class Reset(override val timerId: Int, val duration: Long) : TimerEvent(timerId) + data class Reset(override val timerId: Int) : 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) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt b/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt index 116a27e0..e13fd0cd 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/receivers/HideTimerReceiver.kt @@ -6,13 +6,13 @@ import android.content.Intent import com.simplemobiletools.clock.extensions.hideTimerNotification import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID import com.simplemobiletools.clock.helpers.TIMER_ID -import com.simplemobiletools.clock.models.TimerState +import com.simplemobiletools.clock.models.TimerEvent import org.greenrobot.eventbus.EventBus class HideTimerReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val timerId = intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID) context.hideTimerNotification(timerId) - EventBus.getDefault().post(TimerState.Idle) + EventBus.getDefault().post(TimerEvent.Reset(timerId, )) } } From 94de14b52d3322b4c9a64562efe8da32e4718815 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 8 Sep 2021 09:10:16 +0100 Subject: [PATCH 16/32] Update kotlin version and commons lib --- app/build.gradle | 3 +-- build.gradle | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e82a167e..edff5d1c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,8 +67,7 @@ android { } dependencies { -// implementation 'com.github.SimpleMobileTools:Simple-Commons:554dda71a4' - implementation project(":commons") + implementation 'com.github.SimpleMobileTools:Simple-Commons:a9e600f664' implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.shawnlin:number-picker:2.4.6' diff --git a/build.gradle b/build.gradle index 550f9fcb..e561b3aa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.32' + ext.kotlin_version = '1.5.21' repositories { google() From 9b4aa9e3708a33b7190476fe2f5a256648c762fc Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Wed, 8 Sep 2021 12:16:52 +0200 Subject: [PATCH 17/32] Update build.gradle --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index edff5d1c..0d96cbe0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,7 +67,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:a9e600f664' + implementation 'com.github.SimpleMobileTools:Simple-Commons:b6e2ffb710' implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.shawnlin:number-picker:2.4.6' From 666a9ef44d952a47d8c9c736db480a7e2e034762 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Wed, 8 Sep 2021 20:00:59 +0100 Subject: [PATCH 18/32] Reduce timer icon sizes, add animation for timer removal --- app/build.gradle | 2 +- .../kotlin/com/simplemobiletools/clock/App.kt | 5 +-- .../clock/fragments/TimerFragment.kt | 15 ++++--- .../helpers/DisabledItemChangeAnimator.kt | 39 +++++++++++++++++++ .../clock/models/TimerEvent.kt | 4 +- app/src/main/res/layout/item_timer.xml | 10 ++--- app/src/main/res/values/dimens.xml | 2 + 7 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/clock/helpers/DisabledItemChangeAnimator.kt diff --git a/app/build.gradle b/app/build.gradle index edff5d1c..0d96cbe0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -67,7 +67,7 @@ android { } dependencies { - implementation 'com.github.SimpleMobileTools:Simple-Commons:a9e600f664' + implementation 'com.github.SimpleMobileTools:Simple-Commons:b6e2ffb710' implementation 'com.facebook.stetho:stetho:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'com.shawnlin:number-picker:2.4.6' diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt index af18cac4..e97244a9 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/App.kt @@ -14,7 +14,6 @@ import com.facebook.stetho.Stetho import com.simplemobiletools.clock.extensions.getOpenTimerTabIntent import com.simplemobiletools.clock.extensions.getTimerNotification import com.simplemobiletools.clock.extensions.timerHelper -import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.clock.models.TimerState import com.simplemobiletools.clock.services.TimerStopService @@ -78,7 +77,7 @@ class App : Application(), LifecycleObserver { fun onMessageEvent(event: TimerEvent.Delete) { countDownTimers[event.timerId]?.cancel() timerHelper.deleteTimer(event.timerId){ - EventBus.getDefault().post(TimerEvent.Refresh(INVALID_TIMER_ID)) + EventBus.getDefault().post(TimerEvent.Refresh) } } @@ -120,7 +119,7 @@ class App : Application(), LifecycleObserver { timerHelper.getTimer(timerId) { timer -> val newTimer = timer.copy(state = state) timerHelper.insertOrUpdateTimer(newTimer) { - EventBus.getDefault().post(TimerEvent.Refresh(timerId)) + EventBus.getDefault().post(TimerEvent.Refresh) } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index d5249287..46942ade 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -12,6 +12,7 @@ import com.simplemobiletools.clock.dialogs.EditTimerDialog import com.simplemobiletools.clock.extensions.config import com.simplemobiletools.clock.extensions.createNewTimer import com.simplemobiletools.clock.extensions.timerHelper +import com.simplemobiletools.clock.helpers.DisabledItemChangeAnimator import com.simplemobiletools.clock.models.Timer import com.simplemobiletools.clock.models.TimerEvent import com.simplemobiletools.commons.extensions.hideKeyboard @@ -49,7 +50,7 @@ class TimerFragment : Fragment() { storeStateVariables() timers_list.adapter = timerAdapter - timers_list.itemAnimator = null + timers_list.itemAnimator = DisabledItemChangeAnimator() timer_add.setOnClickListener { activity?.run { @@ -81,11 +82,13 @@ class TimerFragment : Fragment() { activity?.timerHelper?.getTimers { timers -> timerAdapter.submitList(timers) { view.timers_list.post { - if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { - view.timers_list.scrollToPosition(timerPositionToScrollTo) - timerPositionToScrollTo = INVALID_POSITION - } else if (scrollToLatest) { - view.timers_list.scrollToPosition(timers.lastIndex) + if (getView() != null) { + if (timerPositionToScrollTo != INVALID_POSITION && timerAdapter.itemCount > timerPositionToScrollTo) { + view.timers_list.scrollToPosition(timerPositionToScrollTo) + timerPositionToScrollTo = INVALID_POSITION + } else if (scrollToLatest) { + view.timers_list.scrollToPosition(timers.lastIndex) + } } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/helpers/DisabledItemChangeAnimator.kt b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/DisabledItemChangeAnimator.kt new file mode 100644 index 00000000..7a627afe --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/clock/helpers/DisabledItemChangeAnimator.kt @@ -0,0 +1,39 @@ +package com.simplemobiletools.clock.helpers + +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.RecyclerView + +/** + * Simple RecyclerView animator that disable itemChange animations + */ +class DisabledItemChangeAnimator : DefaultItemAnimator() { + override fun animateChange( + oldHolder: RecyclerView.ViewHolder, + newHolder: RecyclerView.ViewHolder, + preInfo: ItemHolderInfo, + postInfo: ItemHolderInfo + ): Boolean { + dispatchChangeFinished(oldHolder, false) + return false + } + + override fun animateChange( + oldHolder: RecyclerView.ViewHolder?, + newHolder: RecyclerView.ViewHolder?, + fromX: Int, + fromY: Int, + toX: Int, + toY: Int + ): Boolean { + dispatchChangeFinished(oldHolder, false) + return false + } + + override fun canReuseUpdatedViewHolder(viewHolder: RecyclerView.ViewHolder): Boolean { + return true + } + + override fun canReuseUpdatedViewHolder(viewHolder: RecyclerView.ViewHolder, payloads: MutableList): Boolean { + return true + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt index b88a4148..a9732eb0 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/models/TimerEvent.kt @@ -1,10 +1,12 @@ package com.simplemobiletools.clock.models +import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID + sealed class TimerEvent(open val timerId: Int) { data class Delete(override val timerId: Int) : TimerEvent(timerId) data class Reset(override val timerId: Int) : 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) + object Refresh : TimerEvent(INVALID_TIMER_ID) } diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index 8a710a00..7574ef0d 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -21,7 +21,7 @@ android:id="@+id/timer_time" android:layout_width="0dp" android:layout_height="wrap_content" - android:textSize="@dimen/stopwatch_text_size" + android:textSize="@dimen/alarm_text_size" app:layout_constraintEnd_toStartOf="@id/timer_reset" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" @@ -43,8 +43,8 @@ 180dp 110dp 68dp + 50dp + 56dp 70sp 60sp From 4e5d7ca060383e61ee23ae7be61b9667058f2529 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Fri, 10 Sep 2021 10:07:40 +0200 Subject: [PATCH 19/32] lets move the label closer to the time --- app/src/main/res/layout/item_timer.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/layout/item_timer.xml b/app/src/main/res/layout/item_timer.xml index 7574ef0d..226af59e 100644 --- a/app/src/main/res/layout/item_timer.xml +++ b/app/src/main/res/layout/item_timer.xml @@ -21,6 +21,7 @@ android:id="@+id/timer_time" android:layout_width="0dp" android:layout_height="wrap_content" + android:includeFontPadding="false" android:textSize="@dimen/alarm_text_size" app:layout_constraintEnd_toStartOf="@id/timer_reset" app:layout_constraintStart_toStartOf="parent" From 2a87d747daf94f40298584ec3fa964e3853103e5 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 10 Sep 2021 14:27:08 +0100 Subject: [PATCH 20/32] Fix timer notification not dismissing on delete --- .../simplemobiletools/clock/services/TimerService.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index 858d06f1..fb691523 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -25,8 +25,8 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode class TimerService : Service() { - private val bus = EventBus.getDefault() + private var isStopping = false override fun onCreate() { super.onCreate() @@ -37,6 +37,7 @@ class TimerService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) + isStopping = false updateNotification() startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timer_notification_msg), INVALID_TIMER_ID)) return START_NOT_STICKY @@ -54,6 +55,8 @@ class TimerService : Service() { else -> getString(R.string.timer_single_notification_msg, runningTimers.size) } startForeground(TIMER_RUNNING_NOTIF_ID, notification(formattedDuration, contextText, firstTimer.id!!)) + } else { + stopService() } } } @@ -65,10 +68,13 @@ class TimerService : Service() { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(event: TimerEvent.Refresh) { - updateNotification() + if(!isStopping){ + updateNotification() + } } private fun stopService() { + isStopping = true if (isOreoPlus()) { stopForeground(true) } else { From a1894cee86ae6b3e41040323b8e2294c58952340 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 10 Sep 2021 14:33:37 +0100 Subject: [PATCH 21/32] Fix play/pause button color --- .../com/simplemobiletools/clock/adapters/TimerAdapter.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt index 589417e7..db7e821f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/adapters/TimerAdapter.kt @@ -1,6 +1,5 @@ package com.simplemobiletools.clock.adapters -import android.graphics.Color import android.view.Menu import android.view.View import android.view.ViewGroup @@ -117,7 +116,7 @@ class TimerAdapter( resetTimer(timer) } - timer_play_pause.applyColorFilter(if (adjustedPrimaryColor == Color.WHITE) Color.BLACK else Color.WHITE) + timer_play_pause.applyColorFilter(textColor) timer_play_pause.setOnClickListener { when (val state = timer.state) { is TimerState.Idle -> EventBus.getDefault().post(TimerEvent.Start(timer.id!!, timer.seconds.secondsToMillis)) @@ -131,8 +130,7 @@ class TimerAdapter( val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished timer_reset.beInvisibleIf(!resetPossible) 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 - timer_play_pause.setImageDrawable(simpleActivity.resources.getColoredDrawableWithColor(drawableId, iconColor)) + timer_play_pause.setImageDrawable(simpleActivity.resources.getColoredDrawableWithColor(drawableId, textColor)) } } From b98d9c9e42d09a29f906bbfe64cbeccf6b6bea24 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 10 Sep 2021 17:39:42 +0100 Subject: [PATCH 22/32] Change edit_timer_initial_time left drawable color with theme --- .../com/simplemobiletools/clock/dialogs/EditTimerDialog.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt b/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt index 0057e9ab..e2e9e89a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/dialogs/EditTimerDialog.kt @@ -23,6 +23,7 @@ class EditTimerDialog(val activity: SimpleActivity, val timer: Timer, val callba view.apply { + edit_timer_initial_time.colorLeftDrawable(textColor) edit_timer_initial_time.text = timer.seconds.getFormattedDuration() edit_timer_initial_time.setTextColor(textColor) edit_timer_initial_time.setOnClickListener { From eee6565d72031e47e6dc5d3243bafec6f44cb184 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Fri, 10 Sep 2021 22:29:33 +0100 Subject: [PATCH 23/32] Recreate adapter when text color changes so it updates correctly --- .../clock/fragments/TimerFragment.kt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt index 46942ade..b974af0a 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/fragments/TimerFragment.kt @@ -45,31 +45,33 @@ 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, timers_list, ::refreshTimers, ::openEditTimer) - storeStateVariables() - - timers_list.adapter = timerAdapter timers_list.itemAnimator = DisabledItemChangeAnimator() - timer_add.setOnClickListener { activity?.run { hideKeyboard() openEditTimer(createNewTimer()) } } - - refreshTimers() } + initAdapter() + refreshTimers() return view } + private fun initAdapter() { + timerAdapter = TimerAdapter(requireActivity() as SimpleActivity, view.timers_list, ::refreshTimers, ::openEditTimer) + view.timers_list.adapter = timerAdapter + } + override fun onResume() { super.onResume() requireContext().updateTextColors(timer_fragment) val configTextColor = requireContext().config.textColor if (storedTextColor != configTextColor) { - (view.timers_list.adapter as TimerAdapter).updateTextColor(configTextColor) + initAdapter() + timerAdapter.updateTextColor(configTextColor) + refreshTimers() } } From acab6a2ef097d93d82959ff5920b088099b75ad9 Mon Sep 17 00:00:00 2001 From: Paul Akhamiogu Date: Sun, 12 Sep 2021 15:01:04 +0100 Subject: [PATCH 24/32] Add Plural strings --- .../clock/services/TimerService.kt | 5 ++--- app/src/main/res/values-az/strings.xml | 9 ++++++--- app/src/main/res/values-cs/strings.xml | 10 +++++++--- app/src/main/res/values-cy/strings.xml | 13 ++++++++++--- app/src/main/res/values-da/strings.xml | 9 ++++++--- app/src/main/res/values-de/strings.xml | 9 ++++++--- app/src/main/res/values-el/strings.xml | 9 ++++++--- app/src/main/res/values-es/strings.xml | 9 ++++++--- app/src/main/res/values-eu/strings.xml | 9 ++++++--- app/src/main/res/values-fi/strings.xml | 9 ++++++--- app/src/main/res/values-fr/strings.xml | 9 ++++++--- app/src/main/res/values-hr/strings.xml | 10 +++++++--- app/src/main/res/values-id/strings.xml | 8 +++++--- app/src/main/res/values-in/strings.xml | 8 +++++--- app/src/main/res/values-it/strings.xml | 9 ++++++--- app/src/main/res/values-ja/strings.xml | 8 +++++--- app/src/main/res/values-lt/strings.xml | 10 +++++++--- app/src/main/res/values-ml/strings.xml | 9 ++++++--- app/src/main/res/values-nb/strings.xml | 9 ++++++--- app/src/main/res/values-nl/strings.xml | 9 ++++++--- app/src/main/res/values-pl/strings.xml | 11 ++++++++--- app/src/main/res/values-pt/strings.xml | 9 ++++++--- app/src/main/res/values-ru/strings.xml | 11 ++++++++--- app/src/main/res/values-sk/strings.xml | 10 +++++++--- app/src/main/res/values-sv/strings.xml | 9 ++++++--- app/src/main/res/values-tr/strings.xml | 9 ++++++--- app/src/main/res/values-uk/strings.xml | 11 ++++++++--- app/src/main/res/values-zh-rCN/strings.xml | 8 +++++--- app/src/main/res/values-zh-rTW/strings.xml | 8 +++++--- app/src/main/res/values/strings.xml | 9 ++++++--- 30 files changed, 185 insertions(+), 90 deletions(-) diff --git a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt index fb691523..ea8e7f36 100644 --- a/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt +++ b/app/src/main/kotlin/com/simplemobiletools/clock/services/TimerService.kt @@ -39,7 +39,7 @@ class TimerService : Service() { super.onStartCommand(intent, flags, startId) isStopping = false updateNotification() - startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timer_notification_msg), INVALID_TIMER_ID)) + startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timers_notification_msg), INVALID_TIMER_ID)) return START_NOT_STICKY } @@ -50,9 +50,8 @@ class TimerService : Service() { val firstTimer = runningTimers.first() val formattedDuration = (firstTimer.state as TimerState.Running).tick.getFormattedDuration() val contextText = when { - runningTimers.size > 1 -> getString(R.string.timer_multiple_notification_msg, runningTimers.size) firstTimer.label.isNotEmpty() -> getString(R.string.timer_single_notification_label_msg, firstTimer.label) - else -> getString(R.string.timer_single_notification_msg, runningTimers.size) + else -> resources.getQuantityString(R.plurals.timer_notification_msg, runningTimers.size, runningTimers.size) } startForeground(TIMER_RUNNING_NOTIF_ID, notification(formattedDuration, contextText, firstTimer.id!!)) } else { diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml index b7242bcd..b3ab3e2d 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Saat bölməsi Siqnal bölməsi diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 59940686..85b2d381 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -18,12 +18,16 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers is running + %d timers are running + + Záložka hodin Záložka budíku diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 292bc0ce..98533ed7 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -18,12 +18,19 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + %d timers are running + %d timers are running + %d timers are running + + Tab cloc Tab larwm diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 0bb34aa7..5118cf65 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Ur Alarm diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 09c45de3..18077558 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Uhr Wecker diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index f3484d66..b3708264 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -19,12 +19,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Ετικέτα Ρολογιού Ετικέτα Αφύπνισης diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 37b4940a..fd962a78 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Pestaña de reloj Pestaña de alarma diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index cb28eae8..5245a7dd 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Erloju fitxa Alarma fitxa diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 661fe387..0626a95c 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -18,11 +18,14 @@ Herätysaika   - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + + %d timer is running + %d timers are running +   Kello-välilehti diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 76321c5e..75dfb29d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Horloge Réveil diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index c8501371..0b0a87cd 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -18,12 +18,16 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + + Kartica sata Kartica alarma diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 79b28da0..18b10c3a 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -18,12 +18,14 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timers are running + + Tab jam Tab alarm diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 79b28da0..18b10c3a 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -18,12 +18,14 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timers are running + + Tab jam Tab alarm diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 711d6397..725866ab 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Scheda orologio Scheda sveglia diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 2930c40b..2bbebd9e 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -18,12 +18,14 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timers are running + + 時計 アラーム diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 796624c4..c885f4a5 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -18,12 +18,16 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + + Laikrodžio skirtukas Žadintuvo skirtukas diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml index 54ee4c5a..28ad128f 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + ക്ലോക്ക് ടാബ് അലാറം ടാബ് diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml index cf5fb931..2eab498b 100644 --- a/app/src/main/res/values-nb/strings.xml +++ b/app/src/main/res/values-nb/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Klokke Alarm diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 884d5c77..d1cfc643 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -18,12 +18,15 @@ Alarmtijd - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Tab Klok Tab Alarm diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 0e07fb25..e04027d2 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -18,12 +18,17 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + %d timers are running + + Zegar Alarm diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 2a5237fc..2c840d4b 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Relógio Alarme diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 88233cad..e2beedb7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -18,12 +18,17 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + %d timers are running + + Часы Будильник diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 6996e0a7..6a95c139 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -18,12 +18,16 @@ Času budíka - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + + Okno s časom Okno s budíkom diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 5a33469a..059a8582 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Fliken Klocka Fliken Alarm diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b1bd6367..aafcb7d6 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Saat sekmesi Alarm sekmesi diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 39dea5fa..2c6bf7e0 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -18,12 +18,17 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + %d timers are running + %d timers are running + + Годинник Будильник diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 62742678..60d1bdbf 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -18,12 +18,14 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timers are running + + 时钟页面 闹钟页面 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 9a47b674..34480d5b 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -18,12 +18,14 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timers are running + + 時鐘頁面 鬧鐘頁面 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5eda1b79..82c2e1a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -18,12 +18,15 @@ Alarm time - Timers are running - %d timers are running + Timers are running Timer for %s is running - %d timer is running New Timer + + %d timer is running + %d timers are running + + Clock tab Alarm tab From e1caf9b83307e22fe9e340adcfcfda384f0533f7 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:20:14 +0200 Subject: [PATCH 25/32] Update strings.xml --- app/src/main/res/values-cs/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 85b2d381..12e4fcb7 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -24,7 +24,7 @@ %d timer is running - %d timers is running + %d timers are running %d timers are running From f791f30b8ffcdd6f6bda8e9420ca970b6d6549c8 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:21:55 +0200 Subject: [PATCH 26/32] Update strings.xml --- app/src/main/res/values-id/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index 18b10c3a..41ff287f 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -23,6 +23,7 @@ New Timer + %d timer is running %d timers are running From e2983e01de098de908c5d13e5913b98eb5cfc146 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:22:37 +0200 Subject: [PATCH 27/32] Update strings.xml --- app/src/main/res/values-in/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 18b10c3a..41ff287f 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -23,6 +23,7 @@ New Timer + %d timer is running %d timers are running From 89f6ca16b3ef62d025695510a11554ffc10da009 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:22:53 +0200 Subject: [PATCH 28/32] Update strings.xml --- app/src/main/res/values-ja/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 2bbebd9e..e0994cc6 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -23,6 +23,7 @@ New Timer + %d timer is running %d timers are running From b9bb9d8b4885250ae3a8d5fbb551bbe75baa2564 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:23:23 +0200 Subject: [PATCH 29/32] Update strings.xml --- app/src/main/res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 60d1bdbf..3d27a5ac 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -23,6 +23,7 @@ New Timer + %d timer is running %d timers are running From 8ef644011afd803f7c8221362298c01dc19eb8a9 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:23:35 +0200 Subject: [PATCH 30/32] Update strings.xml --- app/src/main/res/values-zh-rTW/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 34480d5b..55eee2e0 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -23,6 +23,7 @@ New Timer + %d timer is running %d timers are running From 80a7f89a32114893db959b1486d76063bc3f07a1 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:25:15 +0200 Subject: [PATCH 31/32] Update strings.xml --- app/src/main/res/values-sk/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml index 6a95c139..91351f97 100644 --- a/app/src/main/res/values-sk/strings.xml +++ b/app/src/main/res/values-sk/strings.xml @@ -18,14 +18,14 @@ Času budíka - Timers are running - Timer for %s is running - New Timer + Sú spustené časovače + Beží časovač pre %s + Nový časovač - %d timer is running - %d timers are running - %d timers are running + Beží %d časovač + Bežia %d časovače + Beží %d časovačov From 3bf340d12183843049a3b520670eff903bb08124 Mon Sep 17 00:00:00 2001 From: Tibor Kaputa Date: Sun, 12 Sep 2021 17:26:10 +0200 Subject: [PATCH 32/32] Update strings.xml --- app/src/main/res/values-cy/strings.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml index 98533ed7..bb5f5d8c 100644 --- a/app/src/main/res/values-cy/strings.xml +++ b/app/src/main/res/values-cy/strings.xml @@ -24,7 +24,6 @@ %d timer is running - %d timers are running %d timers are running %d timers are running %d timers are running