mirror of
https://github.com/SimpleMobileTools/Simple-Clock.git
synced 2025-02-07 15:38:41 +01:00
Merge pull request #285 from KryptKode/feat/multiple-timers
Feat/multiple timers
This commit is contained in:
commit
84326846c0
@ -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()
|
||||
@ -66,7 +67,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.SimpleMobileTools:Simple-Commons:ac45c5e893'
|
||||
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'
|
||||
@ -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'
|
||||
}
|
||||
|
@ -37,7 +37,8 @@
|
||||
|
||||
<activity
|
||||
android:name=".activities.MainActivity"
|
||||
android:launchMode="singleTask" />
|
||||
android:launchMode="singleTask"
|
||||
android:windowSoftInputMode="adjustPan"/>
|
||||
|
||||
<activity
|
||||
android:name=".activities.ReminderActivity"
|
||||
|
@ -11,10 +11,10 @@ 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.helpers.TIMER_NOTIF_ID
|
||||
import com.simplemobiletools.clock.extensions.timerHelper
|
||||
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 +25,7 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class App : Application(), LifecycleObserver {
|
||||
|
||||
private var timer: CountDownTimer? = null
|
||||
private var lastTick = 0L
|
||||
private var countDownTimers = mutableMapOf<Int, CountDownTimer>()
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
@ -48,63 +47,80 @@ 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 ->
|
||||
if (countDownTimers[timer.id] == null) {
|
||||
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) {
|
||||
updateTimerState(event.timerId, TimerState.Idle)
|
||||
countDownTimers[event.timerId]?.cancel()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(state: TimerState.Start) {
|
||||
timer = object : CountDownTimer(state.duration, 1000) {
|
||||
fun onMessageEvent(event: TimerEvent.Delete) {
|
||||
countDownTimers[event.timerId]?.cancel()
|
||||
timerHelper.deleteTimer(event.timerId){
|
||||
EventBus.getDefault().post(TimerEvent.Refresh)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: TimerEvent.Start) {
|
||||
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
|
||||
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()
|
||||
countDownTimers[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) {
|
||||
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(event.timerId, notification)
|
||||
updateTimerState(event.timerId, TimerState.Finished)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(state: TimerState.Finished) {
|
||||
config.timerState = state
|
||||
fun onMessageEvent(event: TimerEvent.Pause) {
|
||||
timerHelper.getTimer(event.timerId) { timer ->
|
||||
updateTimerState(event.timerId, TimerState.Paused(event.duration, (timer.state as TimerState.Running).tick))
|
||||
countDownTimers[event.timerId]?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: TimerState.Pause) {
|
||||
EventBus.getDefault().post(TimerState.Paused(event.duration, lastTick))
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(state: TimerState.Paused) {
|
||||
config.timerState = state
|
||||
timer?.cancel()
|
||||
private fun updateTimerState(timerId: Int, state: TimerState) {
|
||||
timerHelper.getTimer(timerId) { timer ->
|
||||
val newTimer = timer.copy(state = state)
|
||||
timerHelper.insertOrUpdateTimer(newTimer) {
|
||||
EventBus.getDefault().post(TimerEvent.Refresh)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ 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 var storedTextColor = 0
|
||||
@ -30,7 +31,6 @@ class MainActivity : SimpleActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
appLaunched(BuildConfig.APPLICATION_ID)
|
||||
|
||||
storeStateVariables()
|
||||
initFragments()
|
||||
|
||||
@ -101,8 +101,14 @@ class MainActivity : SimpleActivity() {
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
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.getIntExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
(view_pager.adapter as ViewPagerAdapter).updateTimerPosition(timerId)
|
||||
}
|
||||
}
|
||||
super.onNewIntent(intent)
|
||||
}
|
||||
|
||||
private fun storeStateVariables() {
|
||||
@ -136,7 +142,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 +151,10 @@ class MainActivity : SimpleActivity() {
|
||||
|
||||
val tabToOpen = intent.getIntExtra(OPEN_TAB, config.lastUsedViewPagerPage)
|
||||
intent.removeExtra(OPEN_TAB)
|
||||
if (tabToOpen == TAB_TIMER) {
|
||||
val timerId = intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
viewPagerAdapter.updateTimerPosition(timerId)
|
||||
}
|
||||
view_pager.currentItem = tabToOpen
|
||||
view_pager.offscreenPageLimit = TABS_COUNT - 1
|
||||
main_tabs_holder.onTabSelectionChanged(
|
||||
|
@ -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.getIntExtra(TIMER_ID, INVALID_TIMER_ID))
|
||||
startActivity(this)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,146 @@
|
||||
package com.simplemobiletools.clock.adapters
|
||||
|
||||
import android.view.Menu
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import com.simplemobiletools.clock.R
|
||||
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.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 simpleActivity: SimpleActivity,
|
||||
recyclerView: MyRecyclerView,
|
||||
onRefresh: () -> Unit,
|
||||
onItemClick: (Timer) -> Unit,
|
||||
) : MyRecyclerViewListAdapter<Timer>(simpleActivity, recyclerView, diffUtil, null, onItemClick, onRefresh) {
|
||||
|
||||
companion object {
|
||||
private val diffUtil = object : DiffUtil.ItemCallback<Timer>() {
|
||||
override fun areItemsTheSame(oldItem: Timer, newItem: Timer): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: Timer, newItem: Timer): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
setupDragListener(true)
|
||||
}
|
||||
|
||||
override fun getActionMenuId() = R.menu.cab_alarms
|
||||
|
||||
override fun prepareActionMode(menu: Menu) {}
|
||||
|
||||
override fun actionItemPressed(id: Int) {
|
||||
if (selectedKeys.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
timersToRemove.forEach(::deleteTimer)
|
||||
}
|
||||
|
||||
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_reset.applyColorFilter(textColor)
|
||||
timer_reset.setOnClickListener {
|
||||
resetTimer(timer)
|
||||
}
|
||||
|
||||
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))
|
||||
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
|
||||
timer_reset.beInvisibleIf(!resetPossible)
|
||||
val drawableId = if (state is TimerState.Running) R.drawable.ic_pause_vector else R.drawable.ic_play_vector
|
||||
timer_play_pause.setImageDrawable(simpleActivity.resources.getColoredDrawableWithColor(drawableId, textColor))
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetTimer(timer: Timer) {
|
||||
EventBus.getDefault().post(TimerEvent.Reset(timer.id!!))
|
||||
simpleActivity.hideTimerNotification(timer.id!!)
|
||||
}
|
||||
|
||||
private fun deleteTimer(timer: Timer) {
|
||||
EventBus.getDefault().post(TimerEvent.Delete(timer.id!!))
|
||||
simpleActivity.hideTimerNotification(timer.id!!)
|
||||
}
|
||||
}
|
@ -53,4 +53,8 @@ class ViewPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
|
||||
fun updateTimerTabAlarmSound(alarmSound: AlarmSound) {
|
||||
(fragments[TAB_TIMER] as? TimerFragment)?.updateAlarmSound(alarmSound)
|
||||
}
|
||||
|
||||
fun updateTimerPosition(timerId: Int) {
|
||||
(fragments[TAB_TIMER] as? TimerFragment)?.updatePosition(timerId)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,66 @@
|
||||
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 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)
|
||||
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")
|
||||
.addCallback(object : Callback() {
|
||||
override fun onCreate(db: SupportSQLiteDatabase) {
|
||||
super.onCreate(db)
|
||||
insertDefaultTimer(context)
|
||||
}
|
||||
})
|
||||
.build()
|
||||
db!!.openHelper.setWriteAheadLoggingEnabled(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
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.colorLeftDrawable(textColor)
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@ -20,9 +20,12 @@ 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.models.Timer
|
||||
import com.simplemobiletools.clock.receivers.AlarmReceiver
|
||||
import com.simplemobiletools.clock.receivers.DateTimeWidgetUpdateReceiver
|
||||
import com.simplemobiletools.clock.receivers.HideAlarmReceiver
|
||||
@ -39,6 +42,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
|
||||
@ -80,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
|
||||
@ -135,10 +144,11 @@ 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: Int): PendingIntent {
|
||||
val intent = getLaunchIntent() ?: Intent(this, SplashActivity::class.java)
|
||||
intent.putExtra(OPEN_TAB, TAB_TIMER)
|
||||
return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
intent.putExtra(TIMER_ID, timerId)
|
||||
return PendingIntent.getActivity(this, timerId, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
}
|
||||
|
||||
fun Context.getAlarmIntent(alarm: Alarm): PendingIntent {
|
||||
@ -157,10 +167,11 @@ 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 = 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 {
|
||||
@ -173,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
|
||||
}
|
||||
@ -256,8 +268,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 {
|
||||
@ -265,8 +277,8 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent:
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -288,7 +300,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent:
|
||||
lightColor = getAdjustedPrimaryColor()
|
||||
setSound(Uri.parse(soundUri), audioAttributes)
|
||||
|
||||
if (!config.timerVibrate) {
|
||||
if (!timer.vibrate) {
|
||||
vibrationPattern = longArrayOf(0L)
|
||||
}
|
||||
|
||||
@ -299,7 +311,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent:
|
||||
|
||||
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)
|
||||
@ -309,7 +321,7 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent:
|
||||
.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)
|
||||
@ -317,7 +329,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)
|
||||
}
|
||||
@ -327,9 +339,10 @@ fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent:
|
||||
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 {
|
||||
|
@ -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)
|
||||
|
@ -7,11 +7,8 @@ import com.simplemobiletools.clock.models.TimerState
|
||||
|
||||
val timerStates = valueOf<TimerState>()
|
||||
.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 <reified T : Any> valueOf(): RuntimeTypeAdapterFactory<T> = RuntimeTypeAdapterFactory.of(T::class.java)
|
||||
|
@ -1,8 +1,5 @@
|
||||
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
|
||||
@ -10,22 +7,31 @@ import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
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.TimerState
|
||||
import com.simplemobiletools.commons.dialogs.SelectAlarmSoundDialog
|
||||
import com.simplemobiletools.commons.extensions.*
|
||||
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.helpers.DisabledItemChangeAnimator
|
||||
import com.simplemobiletools.clock.models.Timer
|
||||
import com.simplemobiletools.clock.models.TimerEvent
|
||||
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.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
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class TimerFragment : Fragment() {
|
||||
|
||||
lateinit var view: ViewGroup
|
||||
private val INVALID_POSITION = -1
|
||||
private lateinit var view: ViewGroup
|
||||
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)
|
||||
@ -39,151 +45,90 @@ 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
|
||||
|
||||
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_play_pause.setOnClickListener {
|
||||
val state = config.timerState
|
||||
|
||||
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))
|
||||
else -> {
|
||||
}
|
||||
storeStateVariables()
|
||||
timers_list.itemAnimator = DisabledItemChangeAnimator()
|
||||
timer_add.setOnClickListener {
|
||||
activity?.run {
|
||||
hideKeyboard()
|
||||
openEditTimer(createNewTimer())
|
||||
}
|
||||
}
|
||||
|
||||
timer_reset.setOnClickListener {
|
||||
stopTimer()
|
||||
}
|
||||
|
||||
timer_time.setOnClickListener {
|
||||
changeDuration()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
initAdapter()
|
||||
refreshTimers()
|
||||
return view
|
||||
}
|
||||
|
||||
private fun stopTimer() {
|
||||
EventBus.getDefault().post(TimerState.Idle)
|
||||
activity?.hideTimerNotification()
|
||||
view.timer_time.text = activity?.config?.timerSeconds?.getFormattedDuration()
|
||||
private fun initAdapter() {
|
||||
timerAdapter = TimerAdapter(requireActivity() as SimpleActivity, view.timers_list, ::refreshTimers, ::openEditTimer)
|
||||
view.timers_list.adapter = timerAdapter
|
||||
}
|
||||
|
||||
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
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
requireContext().updateTextColors(timer_fragment)
|
||||
val configTextColor = requireContext().config.textColor
|
||||
if (storedTextColor != configTextColor) {
|
||||
initAdapter()
|
||||
timerAdapter.updateTextColor(configTextColor)
|
||||
refreshTimers()
|
||||
}
|
||||
}
|
||||
|
||||
if (view.timer_reset.isGone()) {
|
||||
stopTimer()
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
storeStateVariables()
|
||||
}
|
||||
|
||||
private fun refreshTimers(scrollToLatest: Boolean = false) {
|
||||
activity?.timerHelper?.getTimers { timers ->
|
||||
timerAdapter.submitList(timers) {
|
||||
view.timers_list.post {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(state: TimerState.Idle) {
|
||||
view.timer_time.text = requiredActivity.config.timerSeconds.getFormattedDuration()
|
||||
updateViewStates(state)
|
||||
private fun storeStateVariables() {
|
||||
storedTextColor = requireContext().config.textColor
|
||||
}
|
||||
|
||||
@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)
|
||||
}
|
||||
|
||||
private fun updateViewStates(state: TimerState) {
|
||||
val resetPossible = state is TimerState.Running || state is TimerState.Paused || state is TimerState.Finished
|
||||
view.timer_reset.beVisibleIf(resetPossible)
|
||||
|
||||
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 onMessageEvent(event: TimerEvent.Refresh) {
|
||||
refreshTimers()
|
||||
}
|
||||
|
||||
fun updateAlarmSound(alarmSound: AlarmSound) {
|
||||
activity?.config?.timerSoundTitle = alarmSound.title
|
||||
activity?.config?.timerSoundUri = alarmSound.uri
|
||||
view.timer_sound.text = alarmSound.title
|
||||
currentEditAlarmDialog?.updateAlarmSound(alarmSound)
|
||||
}
|
||||
|
||||
fun updatePosition(timerId: Int) {
|
||||
activity?.timerHelper?.getTimers { timers ->
|
||||
val position = timers.indexOfFirst { it.id == timerId }
|
||||
if (position != INVALID_POSITION) {
|
||||
activity?.runOnUiThread {
|
||||
if (timerAdapter.itemCount > position) {
|
||||
view.timers_list.scrollToPosition(position)
|
||||
} else {
|
||||
timerPositionToScrollTo = position
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openEditTimer(timer: Timer) {
|
||||
currentEditAlarmDialog = EditTimerDialog(activity as SimpleActivity, timer) {
|
||||
currentEditAlarmDialog = null
|
||||
refreshTimers()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
@ -18,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"
|
||||
@ -35,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"
|
||||
@ -43,6 +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_id"
|
||||
const val INVALID_TIMER_ID = -1
|
||||
|
||||
// stopwatch sorting
|
||||
const val SORT_BY_LAP = 1
|
||||
@ -56,6 +60,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 {
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.simplemobiletools.clock.helpers
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import com.simplemobiletools.clock.extensions.gson.gson
|
||||
import com.simplemobiletools.clock.models.StateWrapper
|
||||
import com.simplemobiletools.clock.models.TimerState
|
||||
|
||||
class Converters {
|
||||
|
||||
@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))
|
||||
}
|
@ -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<Any>): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.simplemobiletools.clock.helpers
|
||||
|
||||
import android.content.Context
|
||||
import com.simplemobiletools.clock.extensions.timerDb
|
||||
import com.simplemobiletools.clock.models.Timer
|
||||
import com.simplemobiletools.commons.helpers.ensureBackgroundThread
|
||||
|
||||
class TimerHelper(val context: Context) {
|
||||
private val timerDao = context.timerDb
|
||||
|
||||
fun getTimers(callback: (timers: List<Timer>) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
callback.invoke(timerDao.getTimers())
|
||||
}
|
||||
}
|
||||
|
||||
fun getTimer(timerId: Int, callback: (timer: Timer) -> Unit) {
|
||||
ensureBackgroundThread {
|
||||
callback.invoke(timerDao.getTimer(timerId))
|
||||
}
|
||||
}
|
||||
|
||||
fun insertOrUpdateTimer(timer: Timer, callback: () -> Unit = {}) {
|
||||
ensureBackgroundThread {
|
||||
timerDao.insertOrUpdateTimer(timer)
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteTimer(id: Int, callback: () -> Unit = {}) {
|
||||
ensureBackgroundThread {
|
||||
timerDao.deleteTimer(id)
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteTimers(timers: List<Timer>, callback: () -> Unit = {}) {
|
||||
ensureBackgroundThread {
|
||||
timerDao.deleteTimers(timers)
|
||||
callback.invoke()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.simplemobiletools.clock.interfaces
|
||||
|
||||
import androidx.room.*
|
||||
import com.simplemobiletools.clock.models.Timer
|
||||
|
||||
@Dao
|
||||
interface TimerDao {
|
||||
|
||||
@Query("SELECT * FROM timers ORDER BY createdAt ASC")
|
||||
fun getTimers(): List<Timer>
|
||||
|
||||
@Query("SELECT * FROM timers WHERE id=:id")
|
||||
fun getTimer(id: Int): Timer
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertOrUpdateTimer(timer: Timer): Long
|
||||
|
||||
@Query("DELETE FROM timers WHERE id=:id")
|
||||
fun deleteTimer(id: Int)
|
||||
|
||||
@Delete
|
||||
fun deleteTimers(list: List<Timer>)
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.simplemobiletools.clock.models
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "timers")
|
||||
data class Timer(
|
||||
@PrimaryKey(autoGenerate = true) var id: Int?,
|
||||
var seconds: Int,
|
||||
val state: TimerState,
|
||||
var vibrate: Boolean,
|
||||
var soundUri: String,
|
||||
var soundTitle: String,
|
||||
var label: String,
|
||||
var createdAt: Long,
|
||||
var channelId: String? = null,
|
||||
)
|
@ -0,0 +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)
|
||||
object Refresh : TimerEvent(INVALID_TIMER_ID)
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
@ -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.models.TimerState
|
||||
import com.simplemobiletools.clock.helpers.INVALID_TIMER_ID
|
||||
import com.simplemobiletools.clock.helpers.TIMER_ID
|
||||
import com.simplemobiletools.clock.models.TimerEvent
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class HideTimerReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
context.hideTimerNotification()
|
||||
EventBus.getDefault().post(TimerState.Idle)
|
||||
val timerId = intent.getIntExtra(TIMER_ID, INVALID_TIMER_ID)
|
||||
context.hideTimerNotification(timerId)
|
||||
EventBus.getDefault().post(TimerEvent.Reset(timerId, ))
|
||||
}
|
||||
}
|
||||
|
@ -12,18 +12,21 @@ 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.INVALID_TIMER_ID
|
||||
import com.simplemobiletools.clock.helpers.TIMER_RUNNING_NOTIF_ID
|
||||
import com.simplemobiletools.commons.extensions.getFormattedDuration
|
||||
import com.simplemobiletools.clock.models.TimerEvent
|
||||
import com.simplemobiletools.clock.models.TimerState
|
||||
import com.simplemobiletools.commons.helpers.isOreoPlus
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
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()
|
||||
@ -34,19 +37,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))
|
||||
|
||||
isStopping = false
|
||||
updateNotification()
|
||||
startForeground(TIMER_RUNNING_NOTIF_ID, notification(getString(R.string.app_name), getString(R.string.timers_notification_msg), INVALID_TIMER_ID))
|
||||
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 {
|
||||
firstTimer.label.isNotEmpty() -> getString(R.string.timer_single_notification_label_msg, firstTimer.label)
|
||||
else -> resources.getQuantityString(R.plurals.timer_notification_msg, runningTimers.size, runningTimers.size)
|
||||
}
|
||||
startForeground(TIMER_RUNNING_NOTIF_ID, notification(formattedDuration, contextText, firstTimer.id!!))
|
||||
} else {
|
||||
stopService()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: TimerStopService) {
|
||||
stopService()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onMessageEvent(event: TimerEvent.Refresh) {
|
||||
if(!isStopping){
|
||||
updateNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopService() {
|
||||
isStopping = true
|
||||
if (isOreoPlus()) {
|
||||
stopForeground(true)
|
||||
} else {
|
||||
@ -60,7 +87,7 @@ class TimerService : Service() {
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
private fun notification(formattedDuration: String): 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
|
||||
@ -73,15 +100,18 @@ class TimerService : Service() {
|
||||
}
|
||||
|
||||
val builder = NotificationCompat.Builder(this)
|
||||
.setContentTitle(label)
|
||||
.setContentText(formattedDuration)
|
||||
.setSmallIcon(R.drawable.ic_timer)
|
||||
.setContentIntent(this.getOpenTimerTabIntent())
|
||||
.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 != INVALID_TIMER_ID) {
|
||||
builder.setContentIntent(this.getOpenTimerTabIntent(firstRunningTimerId))
|
||||
}
|
||||
|
||||
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
return builder.build()
|
||||
|
96
app/src/main/res/layout/dialog_edit_timer.xml
Normal file
96
app/src/main/res/layout/dialog_edit_timer.xml
Normal file
@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/edit_timer_scrollview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/edit_timer_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin">
|
||||
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/edit_timer_initial_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableLeft="@drawable/ic_timer"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="05:00" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/edit_timer_vibrate_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/activity_margin"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_timer_initial_time">
|
||||
|
||||
<com.simplemobiletools.commons.views.MySwitchCompat
|
||||
android:id="@+id/edit_timer_vibrate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:drawableLeft="@drawable/ic_vibrate_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:text="@string/vibrate"
|
||||
android:textSize="@dimen/bigger_text_size" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/edit_timer_sound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableLeft="@drawable/ic_bell_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_timer_vibrate_holder"
|
||||
tools:text="Default Alarm" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/edit_timer_label_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingBottom="@dimen/medium_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/edit_timer_sound"
|
||||
app:layout_constraintVertical_bias="0"
|
||||
app:layout_constraintVertical_chainStyle="spread_inside">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edit_timer_label_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_label_vector" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/edit_timer_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:hint="@string/label"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textCursorDrawable="@null"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
@ -6,141 +6,32 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/timer_scroll"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/timer_play_pause"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/timer_constraint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_time"
|
||||
android:layout_width="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" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_initial_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/activity_margin"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableLeft="@drawable/ic_timer"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_time"
|
||||
tools:text="05:00" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/timer_vibrate_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:padding="@dimen/activity_margin"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_initial_time">
|
||||
|
||||
<com.simplemobiletools.commons.views.MySwitchCompat
|
||||
android:id="@+id/timer_vibrate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:drawableLeft="@drawable/ic_vibrate_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:text="@string/vibrate"
|
||||
android:textSize="@dimen/bigger_text_size" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_sound"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:drawableLeft="@drawable/ic_bell_vector"
|
||||
android:drawablePadding="@dimen/normal_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_vibrate_holder"
|
||||
tools:text="Default alarm" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/timer_label_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingEnd="@dimen/activity_margin"
|
||||
android:paddingBottom="@dimen/medium_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/timer_sound">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_label_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_label_vector" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyEditText
|
||||
android:id="@+id/timer_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:hint="@string/label"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textCursorDrawable="@null"
|
||||
android:textSize="@dimen/normal_text_size" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</ScrollView>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_play_pause"
|
||||
android:layout_width="@dimen/stopwatch_button_size"
|
||||
android:layout_height="@dimen/stopwatch_button_size"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginBottom="@dimen/big_margin"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:src="@drawable/ic_play_vector"
|
||||
<com.simplemobiletools.commons.views.MyRecyclerView
|
||||
android:id="@+id/timers_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/fab_list_bottom_padding"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_reset"
|
||||
android:layout_width="@dimen/stopwatch_button_small_size"
|
||||
android:layout_height="@dimen/stopwatch_button_small_size"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/normal_margin"
|
||||
android:src="@drawable/ic_reset_vector"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/timer_play_pause"
|
||||
app:layout_constraintEnd_toStartOf="@+id/timer_play_pause"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/timer_play_pause"
|
||||
tools:visibility="visible" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:itemCount="3"
|
||||
tools:listitem="@layout/item_timer" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyFloatingActionButton
|
||||
android:id="@+id/timer_add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/activity_margin"
|
||||
android:contentDescription="@string/new_timer"
|
||||
android:src="@drawable/ic_plus_vector"
|
||||
app:backgroundTint="@color/color_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:rippleColor="@color/pressed_item_foreground" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
78
app/src/main/res/layout/item_timer.xml
Normal file
78
app/src/main/res/layout/item_timer.xml
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/timer_frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:foreground="@drawable/selector">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/activity_margin"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingBottom="@dimen/medium_margin">
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
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"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="00:00" />
|
||||
|
||||
<com.simplemobiletools.commons.views.MyTextView
|
||||
android:id="@+id/timer_label"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:textSize="@dimen/bigger_text_size"
|
||||
app:layout_constraintEnd_toEndOf="@id/timer_time"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/timer_time"
|
||||
tools:text="Cook rice" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_reset"
|
||||
android:layout_width="@dimen/timer_button_small_size"
|
||||
android:layout_height="@dimen/timer_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_toEndOf="@id/timer_time"
|
||||
app:layout_constraintTop_toTopOf="@id/timer_play_pause" />
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/timer_play_pause"
|
||||
android:layout_width="@dimen/timer_button_size"
|
||||
android:layout_height="@dimen/timer_button_size"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginStart="@dimen/bigger_margin"
|
||||
android:layout_marginEnd="@dimen/bigger_margin"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:src="@drawable/ic_play_vector"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/timer_reset"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Saat bölməsi</string>
|
||||
<string name="alarm_tab">Siqnal bölməsi</string>
|
||||
|
@ -17,6 +17,17 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Záložka hodin</string>
|
||||
<string name="alarm_tab">Záložka budíku</string>
|
||||
|
@ -17,6 +17,19 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="two">%d timers are running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="many">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Tab cloc</string>
|
||||
<string name="alarm_tab">Tab larwm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Ur</string>
|
||||
<string name="alarm_tab">Alarm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Uhr</string>
|
||||
<string name="alarm_tab">Wecker</string>
|
||||
|
@ -18,6 +18,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Ετικέτα Ρολογιού</string>
|
||||
<string name="alarm_tab">Ετικέτα Αφύπνισης</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Pestaña de reloj</string>
|
||||
<string name="alarm_tab">Pestaña de alarma</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Erloju fitxa</string>
|
||||
<string name="alarm_tab">Alarma fitxa</string>
|
||||
|
@ -16,6 +16,16 @@
|
||||
<string name="swipe_right_to_dismiss">Pyyhkäise oikealle sammuttaaksesi tai vasemmalle torkuttaaksesi.</string>
|
||||
<string name="sort_by_creation_order">Luontijärjestys</string>
|
||||
<string name="sort_by_alarm_time">Herätysaika</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Kello-välilehti</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Horloge</string>
|
||||
<string name="alarm_tab">Réveil</string>
|
||||
|
@ -17,6 +17,17 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Kartica sata</string>
|
||||
<string name="alarm_tab">Kartica alarma</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Tab jam</string>
|
||||
<string name="alarm_tab">Tab alarm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Tab jam</string>
|
||||
<string name="alarm_tab">Tab alarm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Scheda orologio</string>
|
||||
<string name="alarm_tab">Scheda sveglia</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">時計</string>
|
||||
<string name="alarm_tab">アラーム</string>
|
||||
|
@ -17,6 +17,17 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Laikrodžio skirtukas</string>
|
||||
<string name="alarm_tab">Žadintuvo skirtukas</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">ക്ലോക്ക് ടാബ്</string>
|
||||
<string name="alarm_tab">അലാറം ടാബ്</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Klokke</string>
|
||||
<string name="alarm_tab">Alarm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Aanmaakvolgorde</string>
|
||||
<string name="sort_by_alarm_time">Alarmtijd</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Tab Klok</string>
|
||||
<string name="alarm_tab">Tab Alarm</string>
|
||||
|
@ -17,6 +17,18 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="many">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Zegar</string>
|
||||
<string name="alarm_tab">Alarm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Relógio</string>
|
||||
<string name="alarm_tab">Alarme</string>
|
||||
|
@ -17,6 +17,18 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="many">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Часы</string>
|
||||
<string name="alarm_tab">Будильник</string>
|
||||
|
@ -17,6 +17,17 @@
|
||||
<string name="sort_by_creation_order">Poradia vytvorenia</string>
|
||||
<string name="sort_by_alarm_time">Času budíka</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Sú spustené časovače</string>
|
||||
<string name="timer_single_notification_label_msg">Beží časovač pre %s</string>
|
||||
<string name="new_timer">Nový časovač</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">Beží %d časovač</item>
|
||||
<item quantity="few">Bežia %d časovače</item>
|
||||
<item quantity="other">Beží %d časovačov</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Okno s časom</string>
|
||||
<string name="alarm_tab">Okno s budíkom</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Fliken Klocka</string>
|
||||
<string name="alarm_tab">Fliken Alarm</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Saat sekmesi</string>
|
||||
<string name="alarm_tab">Alarm sekmesi</string>
|
||||
|
@ -17,6 +17,18 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="few">%d timers are running</item>
|
||||
<item quantity="many">%d timers are running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Годинник</string>
|
||||
<string name="alarm_tab">Будильник</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">时钟页面</string>
|
||||
<string name="alarm_tab">闹钟页面</string>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">時鐘頁面</string>
|
||||
<string name="alarm_tab">鬧鐘頁面</string>
|
||||
|
@ -10,6 +10,8 @@
|
||||
<dimen name="min_widget_width">180dp</dimen>
|
||||
<dimen name="min_widget_resize_width">110dp</dimen>
|
||||
<dimen name="fab_list_bottom_padding">68dp</dimen>
|
||||
<dimen name="timer_button_small_size">50dp</dimen>
|
||||
<dimen name="timer_button_size">56dp</dimen>
|
||||
|
||||
<dimen name="clock_text_size">70sp</dimen>
|
||||
<dimen name="clock_text_size_smaller">60sp</dimen>
|
||||
|
@ -17,6 +17,16 @@
|
||||
<string name="sort_by_creation_order">Creation order</string>
|
||||
<string name="sort_by_alarm_time">Alarm time</string>
|
||||
|
||||
<!--Timer-->
|
||||
<string name="timers_notification_msg">Timers are running</string>
|
||||
<string name="timer_single_notification_label_msg">Timer for %s is running</string>
|
||||
<string name="new_timer">New Timer</string>
|
||||
|
||||
<plurals name="timer_notification_msg">
|
||||
<item quantity="one">%d timer is running</item>
|
||||
<item quantity="other">%d timers are running</item>
|
||||
</plurals>
|
||||
|
||||
<!-- Settings -->
|
||||
<string name="clock_tab">Clock tab</string>
|
||||
<string name="alarm_tab">Alarm tab</string>
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user