[V2X]弹窗互斥逻辑和更改道路事件展示弹窗的方式

This commit is contained in:
renwj
2022-01-26 16:47:21 +08:00
parent 19ae5c4250
commit 5002b28ef7
73 changed files with 1434 additions and 4681 deletions

View File

@@ -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)
}

View File

@@ -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
}
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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();
}
}
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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>