[V2X]弹窗互斥逻辑和更改道路事件展示弹窗的方式
This commit is contained in:
@@ -0,0 +1,331 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import androidx.lifecycle.*
|
||||
import androidx.lifecycle.Lifecycle.Event
|
||||
import androidx.lifecycle.Lifecycle.Event.ON_CREATE
|
||||
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.impl.ActivityReminder
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.android.asCoroutineDispatcher
|
||||
import java.lang.IllegalStateException
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
|
||||
object Reminder {
|
||||
|
||||
private val heap by lazy {
|
||||
PriorityQueue<IReminder>()
|
||||
}
|
||||
|
||||
private val queue by lazy {
|
||||
LinkedList<IReminder>()
|
||||
}
|
||||
|
||||
private val listeners by lazy {
|
||||
CopyOnWriteArrayList<IReminder.IGlobalStateChangeListener>()
|
||||
}
|
||||
|
||||
private val reminderListeners by lazy {
|
||||
ConcurrentHashMap<IReminder, IReminder.IStateChangeListener>()
|
||||
}
|
||||
|
||||
private val enqueued by lazy {
|
||||
CopyOnWriteArraySet<String>()
|
||||
}
|
||||
|
||||
private val showed by lazy {
|
||||
ConcurrentHashMap<String, State>()
|
||||
}
|
||||
|
||||
private val attaches by lazy {
|
||||
ConcurrentHashMap<LifecycleOwner, MutableList<IReminder>>()
|
||||
}
|
||||
|
||||
private val scope by lazy {
|
||||
CoroutineScope(Handler(Looper.getMainLooper()).asCoroutineDispatcher("reminder-scope") + SupervisorJob())
|
||||
}
|
||||
|
||||
private const val MSG_WHAT_FOR_OVERRIDE_MAX_PROTECT_DURATION = 0x1010
|
||||
private const val MSG_WHAT_FOR_UN_OVERRIDE_MAX_PROTECT_DURATION = 0x1011
|
||||
|
||||
private val handler by lazy { Handler(Looper.getMainLooper()) {
|
||||
if (it.what == MSG_WHAT_FOR_OVERRIDE_MAX_PROTECT_DURATION) {
|
||||
val reminder = it.obj as? IReminder
|
||||
if (reminder != null) {
|
||||
markUnShowed(reminder.key())
|
||||
if (reminder.isOverride()) {
|
||||
queue.remove(reminder)
|
||||
reminderListeners.remove(reminder)
|
||||
enqueued -= reminder.key()
|
||||
dequeue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (it.what == MSG_WHAT_FOR_UN_OVERRIDE_MAX_PROTECT_DURATION) {
|
||||
val reminder = it.obj as? IReminder
|
||||
if (reminder != null) {
|
||||
markUnShowed(reminder.key())
|
||||
if (!reminder.isOverride()) {
|
||||
heap.remove(reminder)
|
||||
enqueued -= reminder.key()
|
||||
reminderListeners.remove(reminder)
|
||||
dequeueHeap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return@Handler true
|
||||
} }
|
||||
|
||||
private val attachToObserver = LifecycleEventObserver { source, event ->
|
||||
if (event == ON_DESTROY) {
|
||||
recycle(source)
|
||||
}
|
||||
}
|
||||
|
||||
fun enqueue(attachTo: LifecycleOwner, reminder: IReminder) {
|
||||
enqueue(attachTo, reminder, null)
|
||||
}
|
||||
|
||||
fun enqueue(attachTo: LifecycleOwner, reminder: IReminder, listener: IReminder.IStateChangeListener?) {
|
||||
val key = reminder.key()
|
||||
if (key.isEmpty()) {
|
||||
throw IllegalStateException("reminder: ${reminder.javaClass.name}'s key can't be empty.")
|
||||
}
|
||||
if (enqueued.contains(key)) {
|
||||
return
|
||||
}
|
||||
enqueued += key
|
||||
attaches.getOrPut(attachTo, {
|
||||
mutableListOf()
|
||||
}).also {
|
||||
it.add(reminder)
|
||||
}
|
||||
listener?.let {
|
||||
reminderListeners[reminder] = it
|
||||
}
|
||||
scope.launch {
|
||||
if (reminder is ActivityReminder) {
|
||||
attachTo.lifecycleScope.launchWhenCreated {
|
||||
tryDequeue(reminder)
|
||||
}
|
||||
} else {
|
||||
attachTo.lifecycleScope.launchWhenResumed {
|
||||
tryDequeue(reminder)
|
||||
}
|
||||
}
|
||||
attachTo.lifecycle.addObserver(attachToObserver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryDequeue(reminder: IReminder) {
|
||||
if (!reminder.isOverride()) {
|
||||
heap += reminder
|
||||
dequeueHeap()
|
||||
} else {
|
||||
queue += reminder
|
||||
val pre = findPreShowedReminder()
|
||||
if (pre != null) {
|
||||
pre.hide()
|
||||
} else {
|
||||
dequeue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun recycle(attachTo: LifecycleOwner) {
|
||||
val reminders = attaches.remove(attachTo) ?: return
|
||||
try {
|
||||
reminders.forEach {
|
||||
if (it.isShowing()) {
|
||||
it.hide()
|
||||
}
|
||||
enqueued.remove(it.key())
|
||||
showed.remove(it.key())
|
||||
if (it.isOverride()) {
|
||||
queue.remove(it)
|
||||
} else {
|
||||
heap.remove(it)
|
||||
}
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
private fun dequeueHeap() {
|
||||
if (heap.isEmpty()) return
|
||||
val head = heap.peek() ?: return
|
||||
val key = head.key()
|
||||
if (isMarkShowed(key) || head.isShowing()) {
|
||||
return
|
||||
}
|
||||
if (head.doShowOnTurn()) {
|
||||
head.show()
|
||||
markShowed(key, false)
|
||||
doProtectAction(head)
|
||||
head.lifecycleOwner().lifecycle.addObserver(object : LifecycleEventObserver {
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Event) {
|
||||
if (event == ON_CREATE) {
|
||||
removeProtectAction(false)
|
||||
notifyGlobalStateChanged(head, true)
|
||||
reminderListeners[head]?.onShow(head)
|
||||
}
|
||||
if (event == ON_DESTROY) {
|
||||
notifyGlobalStateChanged(head, false)
|
||||
reminderListeners[head]?.onHide(head)
|
||||
reminderListeners.remove(head)
|
||||
enqueued -= key
|
||||
heap.poll()
|
||||
markUnShowed(key)
|
||||
this@Reminder.dequeueHeap()
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
heap.poll()
|
||||
dequeueHeap()
|
||||
}
|
||||
}
|
||||
|
||||
private fun markShowed(key: String, isOverride: Boolean) {
|
||||
var state = showed[key]
|
||||
if (state == null) {
|
||||
state = State(key, isOverride = isOverride, isShowing = true)
|
||||
showed[key] = state
|
||||
}
|
||||
}
|
||||
|
||||
private fun isMarkShowed(key: String): Boolean = showed[key]?.isShowing ?: false
|
||||
|
||||
private fun markUnShowed(key: String) = showed.remove(key)
|
||||
|
||||
private fun dequeue() {
|
||||
while (!queue.isEmpty()) {
|
||||
val head = queue.peek()
|
||||
if (head != null) {
|
||||
val key = head.key()
|
||||
if (isMarkShowed(key)) {
|
||||
break
|
||||
}
|
||||
if (head.doShowOnTurn()) {
|
||||
head.show()
|
||||
markShowed(key, true)
|
||||
doProtectAction(head)
|
||||
head.lifecycleOwner().lifecycle.addObserver(object : LifecycleEventObserver {
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Event) {
|
||||
if (event == ON_CREATE) {
|
||||
removeProtectAction(true)
|
||||
notifyGlobalStateChanged(head, true)
|
||||
reminderListeners[head]?.onShow(head)
|
||||
}
|
||||
if (event == ON_DESTROY) {
|
||||
notifyGlobalStateChanged(head, false)
|
||||
reminderListeners[head]?.onHide(head)
|
||||
reminderListeners.remove(head)
|
||||
queue -= head
|
||||
enqueued -= head.key()
|
||||
markUnShowed(key)
|
||||
dequeue()
|
||||
}
|
||||
}
|
||||
})
|
||||
break
|
||||
} else {
|
||||
queue.poll()
|
||||
}
|
||||
} else {
|
||||
queue.poll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun doProtectAction(reminder: IReminder) {
|
||||
val isOverride = reminder.isOverride()
|
||||
val duration = reminder.maxProtectDuration()
|
||||
if (duration > 0) {
|
||||
if (isOverride) {
|
||||
handler.removeMessages(MSG_WHAT_FOR_OVERRIDE_MAX_PROTECT_DURATION)
|
||||
handler.sendMessageDelayed(Message.obtain(handler, MSG_WHAT_FOR_OVERRIDE_MAX_PROTECT_DURATION, reminder), duration)
|
||||
} else {
|
||||
handler.removeMessages(MSG_WHAT_FOR_UN_OVERRIDE_MAX_PROTECT_DURATION)
|
||||
handler.sendMessageDelayed(Message.obtain(handler, MSG_WHAT_FOR_UN_OVERRIDE_MAX_PROTECT_DURATION, reminder), duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeProtectAction(isOverride: Boolean) {
|
||||
if (isOverride) {
|
||||
handler.removeMessages(MSG_WHAT_FOR_OVERRIDE_MAX_PROTECT_DURATION)
|
||||
} else {
|
||||
handler.removeMessages(MSG_WHAT_FOR_UN_OVERRIDE_MAX_PROTECT_DURATION)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPreShowedReminder(): IReminder? {
|
||||
val key = showed.values.find { it.isOverride && it.isShowing }?.key ?: return null
|
||||
return queue.find { it.key() == key }
|
||||
}
|
||||
|
||||
fun registerGlobalStateChangeListener(listener: IReminder.IGlobalStateChangeListener) {
|
||||
if (listeners.contains(listener)) {
|
||||
return
|
||||
}
|
||||
listeners += listener
|
||||
}
|
||||
|
||||
fun unRegisterGlobalStateChangeListener(listener: IReminder.IGlobalStateChangeListener) {
|
||||
if (!listeners.contains(listener)) {
|
||||
return
|
||||
}
|
||||
listeners -= listener
|
||||
}
|
||||
|
||||
private fun notifyGlobalStateChanged(reminder: IReminder, isShow: Boolean) {
|
||||
listeners.forEach {
|
||||
if (isShow) {
|
||||
it.onShow(reminder)
|
||||
} else {
|
||||
it.onHide(reminder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun hasShowedReminder(attachTo: LifecycleOwner): Boolean {
|
||||
val reminders = attaches[attachTo]
|
||||
if (reminders == null || reminders.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
return reminders.find { it.isShowing() } != null
|
||||
}
|
||||
|
||||
fun dismissAll(attachTo: LifecycleOwner) {
|
||||
val reminders = attaches[attachTo]
|
||||
if (reminders == null || reminders.isEmpty()) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
reminders.forEach {
|
||||
if (it.isOverride()) {
|
||||
queue.remove(it)
|
||||
} else {
|
||||
heap.remove(it)
|
||||
}
|
||||
if (it.isShowing()) {
|
||||
it.hide()
|
||||
}
|
||||
}
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
internal data class State(val key: String, val isOverride: Boolean = false, var isShowing: Boolean = false)
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder.api
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ListPopupWindow
|
||||
import android.widget.PopupWindow
|
||||
import androidx.annotation.IntRange
|
||||
import androidx.core.view.doOnAttach
|
||||
import androidx.core.view.doOnDetach
|
||||
import androidx.lifecycle.*
|
||||
import com.mogo.eagle.core.utilcode.util.R
|
||||
import com.mogo.eagle.core.utilcode.util.Utils
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
interface IReminder : Comparable<IReminder> {
|
||||
|
||||
fun key(): String = this.javaClass.name + this.hashCode().toString()
|
||||
|
||||
/**
|
||||
* 返回此[IReminder]生命周期感知的LifecycleOwner,如:
|
||||
* - [androidx.fragment.app.FragmentActivity]
|
||||
* - [androidx.fragment.app.Fragment]
|
||||
* - [androidx.fragment.app.DialogFragment]
|
||||
* - [android.view.View]
|
||||
* - [PopupWindow]
|
||||
* - [ListPopupWindow]
|
||||
* - 其它具有生命周期感知的类
|
||||
*/
|
||||
fun lifecycleOwner(): LifecycleOwner
|
||||
|
||||
/**
|
||||
* 当前[IReminder]的显示优先级,只针对不可覆盖的[IReminder]([IReminder.isOverride]返回false)生效;
|
||||
* 注:此返回值越大,优先级越高
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
fun priority(): Int = 0
|
||||
|
||||
/**
|
||||
* 子类实现此方法时,要执行真正的展示逻辑
|
||||
*/
|
||||
fun show()
|
||||
|
||||
/**
|
||||
* 子类实现此方法时,要调用真实的显示控件消失方法
|
||||
*/
|
||||
fun hide()
|
||||
|
||||
/**
|
||||
* 子类实现此方法时,要返回真实显示控件的显示状态
|
||||
*/
|
||||
fun isShowing(): Boolean
|
||||
|
||||
|
||||
/**
|
||||
* 是否是可覆盖的:
|
||||
* 说明:
|
||||
* 1.可覆盖的[IReminder]和不覆盖的[IReminder]可以同时展示, 上层业务可以根据[IReminder]的显示位置和具体业务决定是返回true/false
|
||||
* 2.可覆盖的[IReminder]之前是互斥显示的
|
||||
* 当中只有一个可覆盖的[IReminder] 展示, 后一个会将当前替换显示。
|
||||
* 3.不可覆盖的[IReminder]之间,按优先级([IReminder.priority]返回值大小)排队展示
|
||||
*/
|
||||
fun isOverride() = false
|
||||
|
||||
/**
|
||||
* 轮到此[IReminder]展示的时候是否展示
|
||||
*/
|
||||
fun doShowOnTurn() = true
|
||||
|
||||
/**
|
||||
* 调用[IReminder.show]方法后,如果在此时间内没有真正展示,会将此[IReminder]从队列中移除,执行dequeue操作
|
||||
*/
|
||||
fun maxProtectDuration(): Long = TimeUnit.SECONDS.toMillis(2)
|
||||
|
||||
|
||||
override fun compareTo(other: IReminder): Int {
|
||||
return other.priority() - this.priority()
|
||||
}
|
||||
|
||||
interface IStateChangeListener {
|
||||
|
||||
fun onShow(reminder: IReminder)
|
||||
|
||||
fun onHide(reminder: IReminder)
|
||||
}
|
||||
|
||||
interface IGlobalStateChangeListener : IStateChangeListener
|
||||
|
||||
|
||||
// ------------------------ 以下是扩展函数,只能在IReminder类中使用 --------------------------------------------------
|
||||
companion object {
|
||||
|
||||
private val popupWindowLifecycleMap by lazy {
|
||||
ConcurrentHashMap<PopupWindow, LifecycleOwner>()
|
||||
}
|
||||
|
||||
private val listPopupWindowLifecycleMap by lazy {
|
||||
ConcurrentHashMap<ListPopupWindow, LifecycleOwner>()
|
||||
}
|
||||
|
||||
private val activityNameLifeCycleMap by lazy {
|
||||
ConcurrentHashMap<String, LifecycleOwner>()
|
||||
}
|
||||
}
|
||||
|
||||
val <T: View> T.lifecycleOwner: LifecycleOwner
|
||||
get() = getTag(R.id.view_lifecycle_owner) as? LifecycleOwner ?: object : LifecycleOwner, LifecycleEventObserver {
|
||||
|
||||
private val lifecycle = LifecycleRegistry(this)
|
||||
|
||||
init {
|
||||
addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
|
||||
override fun onViewAttachedToWindow(v: View?) {
|
||||
lifecycle.let {
|
||||
if (it.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
|
||||
it.currentState = Lifecycle.State.CREATED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewDetachedFromWindow(v: View?) {
|
||||
lifecycle.let {
|
||||
if (it.currentState.isAtLeast(Lifecycle.State.CREATED)) {
|
||||
it.currentState = Lifecycle.State.DESTROYED
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
lifecycle.currentState = event.targetState
|
||||
}
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
return lifecycle
|
||||
}
|
||||
|
||||
}.also {
|
||||
setTag(R.id.view_lifecycle_owner, it)
|
||||
}
|
||||
|
||||
|
||||
val <T: PopupWindow> T.lifecycleOwner: LifecycleOwner
|
||||
get() = popupWindowLifecycleMap[this]
|
||||
?: object : LifecycleOwner, LifecycleEventObserver {
|
||||
|
||||
private val lifecycle = LifecycleRegistry(this)
|
||||
|
||||
private var register = false
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
val contentView = this@lifecycleOwner.contentView
|
||||
?: throw IllegalStateException("Please ensure ${this@lifecycleOwner.javaClass.simpleName}'s getContentView() can't return null.")
|
||||
if (!register) {
|
||||
contentView.lifecycleOwner.lifecycle.addObserver(this)
|
||||
register = true
|
||||
}
|
||||
return lifecycle
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
val state = event.targetState
|
||||
lifecycle.currentState = state
|
||||
if (state == Lifecycle.State.DESTROYED) {
|
||||
popupWindowLifecycleMap.remove(this@lifecycleOwner)
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
popupWindowLifecycleMap[this] = it
|
||||
}
|
||||
|
||||
|
||||
val <T: ListPopupWindow> T.lifecycleOwner: LifecycleOwner
|
||||
|
||||
get() = listPopupWindowLifecycleMap[this] ?: object : LifecycleOwner, LifecycleEventObserver {
|
||||
|
||||
private val lifecycle = LifecycleRegistry(this)
|
||||
private var register = false
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
val contentView = listView ?: throw IllegalStateException("Please ensure ${this@lifecycleOwner.javaClass.simpleName}'s getListView[() can't return null.")
|
||||
if (!register) {
|
||||
contentView.lifecycleOwner.lifecycle.addObserver(this)
|
||||
register = true
|
||||
}
|
||||
return lifecycle
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
val state = event.targetState
|
||||
lifecycle.currentState = state
|
||||
if (state == Lifecycle.State.DESTROYED) {
|
||||
listPopupWindowLifecycleMap.remove(this@lifecycleOwner)
|
||||
}
|
||||
|
||||
}
|
||||
}.also {
|
||||
listPopupWindowLifecycleMap[this] = it
|
||||
}
|
||||
|
||||
|
||||
fun createLifeCycleOwnerByActivityName(activityClassName: String): LifecycleOwner {
|
||||
|
||||
return activityNameLifeCycleMap[activityClassName] ?: object : LifecycleOwner, Application.ActivityLifecycleCallbacks {
|
||||
|
||||
private val lifecycle = LifecycleRegistry(this)
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
if (activity.javaClass.name == activityClassName) {
|
||||
lifecycle.currentState = Lifecycle.State.CREATED
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
if (activity.javaClass.name == activityClassName) {
|
||||
lifecycle.currentState = Lifecycle.State.STARTED
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
if (activity.javaClass.name == activityClassName) {
|
||||
lifecycle.currentState = Lifecycle.State.RESUMED
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {
|
||||
if (activity.javaClass.name == activityClassName) {
|
||||
lifecycle.currentState = Lifecycle.State.DESTROYED
|
||||
activityNameLifeCycleMap.remove(activityClassName)
|
||||
Utils.getApp().unregisterActivityLifecycleCallbacks(this)
|
||||
}
|
||||
}
|
||||
|
||||
private var register = false
|
||||
|
||||
override fun getLifecycle(): Lifecycle {
|
||||
if (!register) {
|
||||
Utils.getApp().registerActivityLifecycleCallbacks(this)
|
||||
register = true
|
||||
}
|
||||
return lifecycle
|
||||
}
|
||||
}.also {
|
||||
activityNameLifeCycleMap[activityClassName] = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder.api.impl
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
import com.mogo.eagle.core.utilcode.util.AppStateManager
|
||||
|
||||
abstract class ActivityReminder(private val activityClassName: String): IReminder {
|
||||
|
||||
override fun lifecycleOwner(): LifecycleOwner = createLifeCycleOwnerByActivityName(activityClassName)
|
||||
|
||||
|
||||
override fun isShowing() = if (AppStateManager.isActive()) AppStateManager.currentActivity()?.javaClass?.name == activityClassName else false
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder.api.impl
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
|
||||
class FragmentReminder(private val fm: FragmentManager? = null, private val fragment: Fragment): IReminder {
|
||||
|
||||
override fun lifecycleOwner(): LifecycleOwner = fragment
|
||||
|
||||
override fun show() {
|
||||
fm?.let {
|
||||
val bt = it.beginTransaction()
|
||||
bt.add(fragment, "").commitAllowingStateLoss()
|
||||
try {
|
||||
it.executePendingTransactions()
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun hide() {
|
||||
fm?.let {
|
||||
val bt = it.beginTransaction()
|
||||
bt.remove(fragment)
|
||||
bt.commitAllowingStateLoss()
|
||||
try {
|
||||
it.executePendingTransactions()
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun isShowing(): Boolean = fragment.isVisible
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder.api.impl
|
||||
|
||||
import android.widget.ListPopupWindow
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
|
||||
abstract class ListPopupWindowReminder(private val pop: ListPopupWindow): IReminder {
|
||||
|
||||
override fun lifecycleOwner(): LifecycleOwner = pop.lifecycleOwner
|
||||
|
||||
override fun hide() {
|
||||
pop.dismiss()
|
||||
}
|
||||
|
||||
override fun isShowing(): Boolean = pop.isShowing
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder.api.impl
|
||||
|
||||
import android.widget.PopupWindow
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
|
||||
abstract class PopupWindowReminder(private val popupWindow: PopupWindow): IReminder {
|
||||
|
||||
override fun lifecycleOwner(): LifecycleOwner = popupWindow.lifecycleOwner
|
||||
|
||||
override fun hide() {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
override fun isShowing(): Boolean = popupWindow.isShowing
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mogo.eagle.core.utilcode.reminder.api.impl
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.mogo.eagle.core.utilcode.reminder.api.IReminder
|
||||
|
||||
abstract class ViewReminder(private val content: View): IReminder {
|
||||
|
||||
override fun lifecycleOwner(): LifecycleOwner = content.lifecycleOwner
|
||||
|
||||
override fun isShowing() = ViewCompat.isAttachedToWindow(content) && content.visibility == View.VISIBLE
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
package com.mogo.eagle.core.utilcode.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ActivityLifecycleManager {
|
||||
private Application mApplication;
|
||||
/**
|
||||
* 当前出于 Start 状态的 Activity
|
||||
*/
|
||||
private ArrayList<Activity> mStartedActivities = new ArrayList<>();
|
||||
|
||||
private ArrayList<AppStateListener> mAppStateListeners = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 当前存活的 Activity
|
||||
*/
|
||||
private HashMap<Activity, ActivityTrace> mAliveActivities = new HashMap<>();
|
||||
|
||||
/**
|
||||
* App 是否出于前台
|
||||
*/
|
||||
private volatile boolean mIsAppActive = false;
|
||||
|
||||
private Activity mCurrentResumedActivity;
|
||||
|
||||
/**
|
||||
* Home 键事件广播的接受器
|
||||
*/
|
||||
private HomeKeyEventReceiver mHomeKeyEventReceiver;
|
||||
/**
|
||||
* Home 键事件监听者列表
|
||||
*/
|
||||
private ArrayList<HomeKeyEventListener> mHomeKeyEventListeners = new ArrayList<>();
|
||||
|
||||
private DefActivityLifecycleCallbacks mInnerActivityListener = new DefActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
if (mStartedActivities.isEmpty()) {
|
||||
mIsAppActive = true;
|
||||
notifyAppStateChanged(AppStateListener.ACTIVE);
|
||||
}
|
||||
mStartedActivities.add(activity);
|
||||
|
||||
ActivityTrace trace = mAliveActivities.get(activity);
|
||||
if (trace != null) {
|
||||
trace.startCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
mStartedActivities.remove(activity);
|
||||
if (mStartedActivities.isEmpty()) {
|
||||
mIsAppActive = false;
|
||||
notifyAppStateChanged(AppStateListener.INACTIVE);
|
||||
}
|
||||
|
||||
ActivityTrace trace = mAliveActivities.get(activity);
|
||||
if (trace != null) {
|
||||
trace.stopCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
mCurrentResumedActivity = activity;
|
||||
ActivityTrace trace = mAliveActivities.get(activity);
|
||||
if (trace != null) {
|
||||
trace.resumeCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
ActivityTrace trace = mAliveActivities.get(activity);
|
||||
if (trace != null) {
|
||||
trace.pauseCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
mAliveActivities.put(activity, new ActivityTrace(activity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
mAliveActivities.remove(activity);
|
||||
}
|
||||
};
|
||||
|
||||
private ActivityLifecycleManager() {
|
||||
}
|
||||
|
||||
public static ActivityLifecycleManager getInstance() {
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SingletonHolder {
|
||||
private static final ActivityLifecycleManager INSTANCE = new ActivityLifecycleManager();
|
||||
}
|
||||
|
||||
public void start(Application application) {
|
||||
this.mApplication = application;
|
||||
registerInnerActivityListener();
|
||||
registerHomeKeyEventReceiver();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
unregisterActivityLifecycleCallbacks(mInnerActivityListener);
|
||||
unregisterHomeKeyEventReceiver();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 Activity 生命周期的回调
|
||||
*
|
||||
* @param callbacks Activity 生命周期的回调
|
||||
*/
|
||||
public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callbacks) {
|
||||
if (mApplication == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return;
|
||||
}
|
||||
|
||||
mApplication.registerActivityLifecycleCallbacks(callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册 Activity 生命周期的回调
|
||||
*
|
||||
* @param callbacks Activity 生命周期的回调
|
||||
*/
|
||||
public void unregisterActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callbacks) {
|
||||
if (mApplication == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return;
|
||||
}
|
||||
|
||||
mApplication.unregisterActivityLifecycleCallbacks(callbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用是否出于前台
|
||||
*/
|
||||
public boolean isAppActive() {
|
||||
return mIsAppActive;
|
||||
}
|
||||
|
||||
public void setAppActive(boolean appActive) {
|
||||
mIsAppActive = appActive;
|
||||
}
|
||||
|
||||
public Activity getCurrentActivity() {
|
||||
return mCurrentResumedActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加应用状态的监听
|
||||
*/
|
||||
public void addAppStateListener(AppStateListener listener) {
|
||||
synchronized (mAppStateListeners) {
|
||||
mAppStateListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除应用状态的监听
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void removeAppStateListener(AppStateListener listener) {
|
||||
synchronized (mAppStateListeners) {
|
||||
mAppStateListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 home 键的事件监听
|
||||
*/
|
||||
public void addHomeKeyEventListener(HomeKeyEventListener listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (mHomeKeyEventListeners) {
|
||||
mHomeKeyEventListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除 home 键的事件监听
|
||||
*/
|
||||
public void removeHomeKeyEventListener(HomeKeyEventListener listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mHomeKeyEventListeners) {
|
||||
mHomeKeyEventListeners.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册Activity生命周期的监听
|
||||
*/
|
||||
private void registerInnerActivityListener() {
|
||||
registerActivityLifecycleCallbacks(mInnerActivityListener);
|
||||
}
|
||||
|
||||
private void notifyAppStateChanged(int state) {
|
||||
Object[] listeners = collectAppStateListeners();
|
||||
if (listeners != null) {
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
((AppStateListener) listeners[i]).onStateChanged(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] collectAppStateListeners() {
|
||||
Object[] listeners = null;
|
||||
synchronized (mAppStateListeners) {
|
||||
if (mAppStateListeners.size() > 0) {
|
||||
listeners = mAppStateListeners.toArray();
|
||||
}
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
private Object[] collectHomeKeyEventListeners() {
|
||||
Object[] listeners = null;
|
||||
synchronized (mHomeKeyEventListeners) {
|
||||
if (mHomeKeyEventListeners.size() > 0) {
|
||||
listeners = mHomeKeyEventListeners.toArray();
|
||||
}
|
||||
}
|
||||
return listeners;
|
||||
}
|
||||
|
||||
private void registerHomeKeyEventReceiver() {
|
||||
android.content.IntentFilter filter = new android.content.IntentFilter();
|
||||
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
|
||||
|
||||
mHomeKeyEventReceiver = new HomeKeyEventReceiver();
|
||||
mApplication.registerReceiver(mHomeKeyEventReceiver, filter);
|
||||
}
|
||||
|
||||
private void unregisterHomeKeyEventReceiver() {
|
||||
mApplication.unregisterReceiver(mHomeKeyEventReceiver);
|
||||
}
|
||||
|
||||
private void onHomeKeyPressed() {
|
||||
Object[] listeners = collectHomeKeyEventListeners();
|
||||
if (listeners != null) {
|
||||
for (int i = 0; i < listeners.length; i++) {
|
||||
((HomeKeyEventListener) listeners[i]).onHomeKeyPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
public static abstract class DefActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
}
|
||||
}
|
||||
|
||||
public interface AppStateListener {
|
||||
int INACTIVE = 0;
|
||||
int ACTIVE = 1;
|
||||
|
||||
/**
|
||||
* App 状态的回调
|
||||
*/
|
||||
void onStateChanged(int state);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* home 键的监听
|
||||
*/
|
||||
public interface HomeKeyEventListener {
|
||||
void onHomeKeyPressed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activity 的生命周期调用痕迹
|
||||
*/
|
||||
static class ActivityTrace {
|
||||
Activity activity;
|
||||
int resumeCnt;
|
||||
int pauseCnt;
|
||||
int startCnt;
|
||||
int stopCnt;
|
||||
|
||||
ActivityTrace(Activity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
}
|
||||
|
||||
private final class HomeKeyEventReceiver extends BroadcastReceiver {
|
||||
private final String SYSTEM_DIALOG_REASON_KEY = "reason";
|
||||
private final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
|
||||
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
|
||||
if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) {
|
||||
onHomeKeyPressed();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.mogo.eagle.core.utilcode.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.Lifecycle.Event
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
|
||||
interface IAppStateListener {
|
||||
|
||||
fun onAppStateChanged(isForeground: Boolean)
|
||||
}
|
||||
|
||||
object AppStateManager {
|
||||
|
||||
|
||||
private val currentActivityHolder by lazy {
|
||||
AtomicReference<WeakReference<Activity>>()
|
||||
}
|
||||
|
||||
private val appStateListeners by lazy {
|
||||
CopyOnWriteArrayList<IAppStateListener>()
|
||||
}
|
||||
|
||||
|
||||
private val isActive by lazy { AtomicBoolean(false) }
|
||||
|
||||
private val activityLifeCycleCallback = object : ActivityLifecycleCallbacks {
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
currentActivityHolder.set(WeakReference(activity))
|
||||
}
|
||||
|
||||
override fun onActivityPaused(activity: Activity) {
|
||||
currentActivityHolder.set(null)
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {}
|
||||
|
||||
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
|
||||
|
||||
override fun onActivityDestroyed(activity: Activity) {}
|
||||
}
|
||||
|
||||
fun init(app: Application) {
|
||||
app.registerActivityLifecycleCallbacks(activityLifeCycleCallback)
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(object : LifecycleEventObserver {
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Event) {
|
||||
if (event == Event.ON_START) {
|
||||
isActive.set(true)
|
||||
notifyAppStateChanged(true)
|
||||
}
|
||||
|
||||
if (event == Event.ON_STOP) {
|
||||
isActive.set(false)
|
||||
notifyAppStateChanged(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun isActive(): Boolean = isActive.get()
|
||||
|
||||
fun currentActivity() = currentActivityHolder.get()?.get()
|
||||
|
||||
fun registerAppStateListener(listener: IAppStateListener) {
|
||||
if (appStateListeners.contains(listener)) {
|
||||
return
|
||||
}
|
||||
appStateListeners += listener
|
||||
}
|
||||
|
||||
fun unRegisterAppStateListener(listener: IAppStateListener) {
|
||||
if (!appStateListeners.contains(listener)) {
|
||||
return
|
||||
}
|
||||
appStateListeners -= listener
|
||||
}
|
||||
|
||||
private fun notifyAppStateChanged(isForeground: Boolean) {
|
||||
appStateListeners.forEach {
|
||||
it.onAppStateChanged(isForeground)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="tag_click_time" type="id" />
|
||||
<item name="view_lifecycle_owner" type="id" />
|
||||
</resources>
|
||||
Reference in New Issue
Block a user