Skip to content

Commit 9ff6870

Browse files
committed
Update
1 parent e075f6b commit 9ff6870

1 file changed

Lines changed: 23 additions & 32 deletions

File tree

app/src/main/java/com/omarea/common/ui/BannerNotificationManager.kt

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,12 @@ package com.omarea.common.ui
33
import android.animation.Animator
44
import android.animation.AnimatorListenerAdapter
55
import android.app.Activity
6-
import android.graphics.PixelFormat
76
import android.graphics.drawable.GradientDrawable
87
import android.os.Handler
98
import android.os.Looper
10-
import android.view.Gravity
119
import android.view.LayoutInflater
1210
import android.view.View
1311
import android.view.ViewGroup
14-
import android.view.WindowManager
1512
import android.widget.ImageView
1613
import android.widget.TextView
1714
import androidx.core.view.ViewCompat
@@ -21,15 +18,15 @@ import com.tool.tree.R
2118
enum 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

Comments
 (0)