Android System date changed event

谁说胖子不能爱 提交于 2019-11-28 02:13:36

There are broadcasts for those events. ACTION_TIME_CHANGED and ACTION_DATE_CHANGED ACTION docs at http://developer.android.com/reference/android/content/Intent.html#ACTION_DATE_CHANGED

A potential bug and some implementation details available at http://code.google.com/p/android/issues/detail?id=2880

I've made a nice way to detect and update the UI whenever needed, when the date has changed. It's based on the broadcastReceiver, which should be registered on the onStart method, and unregistered on the onStop method (unregistration is done automatically in my code).

build.gradle

implementation 'com.jakewharton.threetenabp:threetenabp:1.0.5'

LocalDateEx.kt

object LocalDateEx {
    /**an alternative of LocalDate.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalDate = Calendar.getInstance().toLocalDate()

}

fun Calendar.toLocalDate(): LocalDate = LocalDate.of(get(Calendar.YEAR), get(Calendar.MONTH) + 1, get(Calendar.DAY_OF_MONTH))

DateChangedBroadcastReceiver.kt

abstract class DateChangedBroadcastReceiver : BroadcastReceiver() {
    private var curDate = LocalDateEx.getNow()

    /**called when the receiver detected the date has changed. You should still check it yourself, because you might already be synced with the new date*/
    abstract fun onDateChanged(previousDate: LocalDate, newDate: LocalDate)

    @Suppress("MemberVisibilityCanBePrivate")
    fun register(context: Context, date: LocalDate) {
        curDate = date
        val filter = IntentFilter()
        filter.addAction(Intent.ACTION_TIME_CHANGED)
        filter.addAction(Intent.ACTION_DATE_CHANGED)
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
        context.registerReceiver(this, filter)
        val newDate = LocalDateEx.getNow()
        if (newDate != curDate) {
            curDate = newDate
            onDateChanged(date, newDate)
        }
    }

    /**a convenient way to auto-unregister when activity/fragment has stopped. This should be called on the onStart method of the fragment/activity*/
    fun registerOnStart(activity: AppCompatActivity, date: LocalDate, fragment: Fragment? = null) {
        register(activity, date)
        val lifecycle = fragment?.lifecycle ?: activity.lifecycle
        lifecycle.addObserver(object : LifecycleObserver {
            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            fun onStop() {
                Log.d("AppLog", "onStop, so unregistering")
                lifecycle.removeObserver(this)
                activity.unregisterReceiver(this@DateChangedBroadcastReceiver)
            }
        })
    }

    override fun onReceive(context: Context, intent: Intent) {
        val newDate = LocalDateEx.getNow()
        Log.d("AppLog", "got intent:" + intent.action + " curDate:" + curDate + " newDate:" + newDate)
        if (newDate != curDate) {
            Log.d("AppLog", "cur date is different, so posting event")
            val previousDate = curDate
            curDate = newDate
            onDateChanged(previousDate, newDate)
        }
    }

}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    var today = LocalDateEx.getNow()
    val receiver = object : DateChangedBroadcastReceiver() {
        override fun onDateChanged(previousDate: LocalDate, newDate: LocalDate) {
            Log.d("AppLog", "onDateChangedEvent:" + newDate + " previousDate:" + previousDate)
            if (newDate != today) {
                today = newDate
                textView.text = "date updated:" + today
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("AppLog", "onCreate")
        setContentView(R.layout.activity_main)
        textView.text = today.toString()
    }

    override fun onStart() {
        super.onStart()
        receiver.registerOnStart(this, today)
    }
}

activity_main.xml

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:layout_gravity="center"/>

</FrameLayout>

Alternative to the above, using Android's Calendar class only, without the need to use another library:

fun Calendar.equalInDateAlone(cal: Calendar): Boolean =
        get(Calendar.YEAR) == cal.get(Calendar.YEAR) && get(Calendar.MONTH) == cal.get(Calendar.MONTH) && get(Calendar.DAY_OF_MONTH) == cal.get(Calendar.DAY_OF_MONTH)

fun Calendar.resetTimeFields(): Calendar {
    set(Calendar.HOUR_OF_DAY, 0)
    set(Calendar.SECOND, 0)
    set(Calendar.MINUTE, 0)
    set(Calendar.MILLISECOND, 0)
    return this
}

abstract class DateChangedBroadcastReceiver : BroadcastReceiver() {
    private var curDate = Calendar.getInstance().resetTimeFields()

    /**called when the receiver detected the date has changed. You should still check it yourself, because you might already be synced with the new date*/
    abstract fun onDateChanged(previousDate: Calendar, newDate: Calendar)

    @Suppress("MemberVisibilityCanBePrivate")
    fun register(context: Context, date: Calendar) {
        curDate = (date.clone() as Calendar).resetTimeFields()
        val filter = IntentFilter()
        filter.addAction(Intent.ACTION_TIME_CHANGED)
        filter.addAction(Intent.ACTION_DATE_CHANGED)
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED)
        context.registerReceiver(this, filter)
        val newDate = Calendar.getInstance().resetTimeFields()
        if (!newDate.equalInDateAlone(curDate)) {
            curDate = newDate.clone() as Calendar
            onDateChanged(date, newDate)
        }
    }

    /**a convenient way to auto-unregister when activity/fragment has stopped. This should be called on the onStart method of the fragment/activity*/
    @Suppress("unused")
    fun registerOnStart(activity: AppCompatActivity, date: Calendar, fragment: Fragment? = null) {
        register(activity, date)
        val lifecycle = fragment?.lifecycle ?: activity.lifecycle
        lifecycle.addObserver(object : LifecycleObserver {
            @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
            fun onStop() {
//                Log.d("AppLog", "onStop, so unregistering")
                lifecycle.removeObserver(this)
                activity.unregisterReceiver(this@DateChangedBroadcastReceiver)
            }
        })
    }

    override fun onReceive(context: Context, intent: Intent) {
        val newDate = Calendar.getInstance().resetTimeFields()
//        Log.d("AppLog", "got intent:" + intent.action + " curDate:" + curDate.toSimpleDateString() + " newDate:" + newDate.toSimpleDateString())
        if (!newDate.equalInDateAlone(curDate)) {
//            Log.d("AppLog", "cur date is different, so posting event")
            val previousDate = curDate
            curDate = newDate
            onDateChanged(previousDate, newDate)
        }
    }

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!