@@ -20,22 +20,10 @@ enum class BannerPosition { TOP, BOTTOM }
2020 * đang có Dialog mở (ví dụ ProgressBarDialog).
2121 *
2222 * Kỹ thuật: dùng chính cơ chế của Toast (custom view Toast, type cửa sổ TYPE_TOAST) thay vì
23- * tự add view vào content của Activity/Dialog. Lý do: Dialog là 1 Window riêng, kích thước
24- * chỉ vừa đủ khung dialog (không phải full màn hình), nên add view thường vào bên trong nó
25- * sẽ bị bó hẹp/cắt theo đúng khung dialog nhỏ đó. TYPE_TOAST là loại cửa sổ đặc biệt của hệ
26- * thống luôn nổi trên mọi Dialog/Activity của app (đây cũng chính là lý do ToastReceiver có
27- * sẵn của bạn luôn hiện được dù đang có dialog hay không).
28- *
29- * Đánh đổi: cửa sổ Toast KHÔNG nhận sự kiện chạm (không bấm tắt sớm được), và không thể tùy
30- * chỉnh thời gian hiển thị (Android giới hạn cứng ở mức LENGTH_LONG ~3.5s cho mọi Toast).
31- *
32- * Gọi từ shell: am broadcast -a <applicationId>.broadcast.BANNER --es text "..." --es type "success" --es position "bottom"
23+ * tự add view vào content của Activity/Dialog.
3324 */
3425object BannerNotificationManager {
3526 private val mainHandler = Handler (Looper .getMainLooper())
36-
37- // Thời gian hiển thị thực tế của Toast.LENGTH_LONG do hệ thống quy định, dùng để giãn cách
38- // các banner trong hàng đợi, tránh cái sau ghi đè ngay lên cái trước.
3927 private const val TOAST_LONG_DURATION_MS = 3500L
4028
4129 private data class BannerRequest (
@@ -49,12 +37,6 @@ object BannerNotificationManager {
4937 private val queue = ArrayDeque <BannerRequest >()
5038 private var isShowing = false
5139
52- /* *
53- * @param icon Tên resource drawable/mipmap tùy chỉnh (vd "ic_my_icon"). Nếu bỏ trống hoặc
54- * không tìm thấy resource tương ứng, mặc định dùng icon của chính app.
55- * @param onNoActivity gọi khi không có Activity nào đang foreground (app ở background),
56- * dùng để nơi gọi có thể fallback sang Toast thường hoặc bỏ qua.
57- */
5840 fun show (
5941 title : String? = null,
6042 message : String ,
@@ -120,33 +102,25 @@ object BannerNotificationManager {
120102 messageView.text = req.message
121103
122104 val density = activity.resources.displayMetrics.density
105+ val yOffset = (50 * density).toInt()
123106
124107 val toast = Toast (activity.applicationContext)
125108 toast.duration = Toast .LENGTH_LONG
126109 @Suppress(" DEPRECATION" )
127110 toast.view = view
128111
129- when (req.position) {
130- BannerPosition .TOP -> toast.setGravity(
131- Gravity .TOP or Gravity .CENTER_HORIZONTAL , 0 , (50 * density).toInt()
132- )
133- BannerPosition .BOTTOM -> toast.setGravity(
134- Gravity .BOTTOM or Gravity .CENTER_HORIZONTAL , 0 , (50 * density).toInt()
135- )
112+ // Xác định gravity gọn gàng, tránh lỗi ngắt dòng khi gọi hàm
113+ val gravity = when (req.position) {
114+ BannerPosition .TOP -> Gravity .TOP or Gravity .CENTER_HORIZONTAL
115+ BannerPosition .BOTTOM -> Gravity .BOTTOM or Gravity .CENTER_HORIZONTAL
136116 }
117+ toast.setGravity(gravity, 0 , yOffset)
118+
137119 toast.show()
138120
139- // Toast tự ẩn theo thời lượng hệ thống quy định (LENGTH_LONG), chỉ cần đợi tương ứng
140- // rồi xử lý banner tiếp theo trong hàng đợi (nếu có).
141121 mainHandler.postDelayed({ showNext() }, TOAST_LONG_DURATION_MS )
142122 }
143123
144- /* *
145- * Tìm drawable cho icon theo thứ tự ưu tiên:
146- * 1. Nếu `name` là đường dẫn file ảnh tồn tại trên máy (vd "/sdcard/.../icon.png") -> decode trực tiếp từ file.
147- * 2. Nếu `name` là tên resource drawable/mipmap có sẵn trong app -> dùng resource đó.
148- * 3. Nếu không có gì khớp -> mặc định trả về icon của chính app.
149- */
150124 private fun resolveIcon (activity : Activity , name : String? ): android.graphics.drawable.Drawable ? {
151125 if (! name.isNullOrEmpty()) {
152126 if (name.startsWith(" /" ) || name.startsWith(" file://" )) {
@@ -180,4 +154,4 @@ object BannerNotificationManager {
180154 null
181155 }
182156 }
183- }
157+ }
0 commit comments