@@ -3,15 +3,12 @@ package com.omarea.common.ui
33import android.animation.Animator
44import android.animation.AnimatorListenerAdapter
55import android.app.Activity
6- import android.graphics.PixelFormat
76import android.graphics.drawable.GradientDrawable
87import android.os.Handler
98import android.os.Looper
10- import android.view.Gravity
119import android.view.LayoutInflater
1210import android.view.View
1311import android.view.ViewGroup
14- import android.view.WindowManager
1512import android.widget.ImageView
1613import android.widget.TextView
1714import androidx.core.view.ViewCompat
@@ -21,15 +18,15 @@ import com.tool.tree.R
2118enum class BannerType { INFO , SUCCESS , WARNING , ERROR }
2219
2320/* *
24- * Hiện 1 banner thông báo đè lên trên cùng của Activity đang foreground .
21+ * Hiện 1 banner thông báo đè lên trên cùng của Activity (hoặc Dialog) đang hiển thị .
2522 * Khác với Toast: hiển thị trong nội dung ứng dụng, có thể tùy biến màu/icon/tiêu đề,
2623 * và chỉ hoạt động khi app đang mở (cần 1 Activity foreground để add vào).
2724 *
28- * Banner được thêm vào dưới dạng 1 Window độc lập (WindowManager.addView), KHÔNG phải
29- * child view của content Activity. Lý do: nếu chỉ addView vào content, banner sẽ bị các
30- * Dialog/AlertDialog (vốn là 1 Window riêng nổi trên Window của Activity) che mất, vì
31- * Dialog luôn ở lớp trên bất kể elevation. Thêm dưới dạng Window riêng, add sau cùng,
32- * đảm bảo banner luôn nổi trên mọi Dialog đang mở tại thời điểm gọi .
25+ * Nếu đang có Dialog mở (đăng ký qua [CurrentDialogHolder], ví dụ [ProgressBarDialog]),
26+ * banner sẽ được add vào chính Window của Dialog đó để chắc chắn nổi lên trên — vì Dialog
27+ * là 1 Window riêng biệt luôn nổi trên Window của Activity, add vào content Activity sẽ bị
28+ * Dialog che mất bất kể elevation. Nếu không có Dialog nào mở, banner add vào content
29+ * của Activity như bình thường .
3330 *
3431 * Gọi từ shell: am broadcast -a <applicationId>.broadcast.BANNER --es text "..." --es type "success"
3532 */
@@ -83,20 +80,28 @@ object BannerNotificationManager {
8380 try {
8481 showOn(activity, req)
8582 } catch (e: Exception ) {
86- // Activity có thể vừa bị hủy đúng lúc addView (BadTokenException...) -> bỏ qua, sang cái tiếp theo
8783 showNext()
8884 }
8985 }
9086
87+ /* * Tìm ViewGroup gốc để add banner vào: ưu tiên Window của Dialog đang mở, nếu không có thì dùng content của Activity. */
88+ private fun resolveRoot (activity : Activity ): ViewGroup ? {
89+ val topDialog = CurrentDialogHolder .getTopVisible()
90+ val dialogWindow = topDialog?.window
91+ if (dialogWindow != null && dialogWindow.decorView.isAttachedToWindow) {
92+ val dialogContent = dialogWindow.findViewById<ViewGroup >(android.R .id.content)
93+ if (dialogContent != null ) return dialogContent
94+ }
95+ return activity.findViewById(android.R .id.content)
96+ }
97+
9198 private fun showOn (activity : Activity , req : BannerRequest ) {
92- val decorView = activity.window?.decorView
93- if (decorView == null || ! decorView.isAttachedToWindow ) {
99+ val root = resolveRoot( activity)
100+ if (root == null ) {
94101 showNext()
95102 return
96103 }
97- val windowManager = activity.windowManager
98-
99- val view = LayoutInflater .from(activity).inflate(R .layout.banner_notification, null , false )
104+ val view = LayoutInflater .from(activity).inflate(R .layout.banner_notification, root, false )
100105
101106 val bannerRoot = view.findViewById<View >(R .id.banner_root)
102107 val icon = view.findViewById<ImageView >(R .id.banner_icon)
@@ -130,21 +135,7 @@ object BannerNotificationManager {
130135 insets
131136 }
132137
133- val params = WindowManager .LayoutParams (
134- ViewGroup .LayoutParams .MATCH_PARENT ,
135- ViewGroup .LayoutParams .WRAP_CONTENT ,
136- WindowManager .LayoutParams .TYPE_APPLICATION ,
137- WindowManager .LayoutParams .FLAG_NOT_TOUCH_MODAL or
138- WindowManager .LayoutParams .FLAG_NOT_FOCUSABLE or
139- WindowManager .LayoutParams .FLAG_LAYOUT_IN_SCREEN or
140- WindowManager .LayoutParams .FLAG_LAYOUT_NO_LIMITS ,
141- PixelFormat .TRANSLUCENT
142- ).apply {
143- gravity = Gravity .TOP
144- token = decorView.windowToken
145- }
146-
147- windowManager.addView(view, params)
138+ root.addView(view)
148139 ViewCompat .requestApplyInsets(view)
149140
150141 var dismissed = false
@@ -159,9 +150,9 @@ object BannerNotificationManager {
159150 .setListener(object : AnimatorListenerAdapter () {
160151 override fun onAnimationEnd (animation : Animator ) {
161152 try {
162- windowManager.removeViewImmediate (view)
153+ root.removeView (view)
163154 } catch (e: Exception ) {
164- // Window có thể đã bị hệ thống dọn (activity destroyed) -> bỏ qua
155+ // root (vd. Window của Dialog) có thể đã bị đóng -> bỏ qua
165156 }
166157 showNext()
167158 }
0 commit comments