@@ -21,6 +21,8 @@ import androidx.core.graphics.drawable.toDrawable
2121import com.tool.tree.ThemeModeState
2222import android.graphics.Bitmap
2323import android.graphics.drawable.BitmapDrawable
24+ import java.util.concurrent.ExecutorService
25+ import java.util.concurrent.Executors
2426
2527class DialogHelper {
2628 class DialogButton (val text : String , val onClick : Runnable ? = null , val dismiss : Boolean = true )
@@ -75,6 +77,11 @@ class DialogHelper {
7577 // 是否禁用模糊背景
7678 var disableBlurBg = false
7779
80+ // Dùng chung 1 thread nền cho việc xử lý blur ảnh nền dialog, tránh tạo/hủy thread
81+ // mới mỗi lần mở dialog. Việc blur (GPU RenderEffect hoặc CPU StackBlur) không đụng
82+ // tới View hierarchy nên chạy an toàn ở đây, tách khỏi main thread.
83+ private val blurExecutor: ExecutorService = Executors .newSingleThreadExecutor()
84+
7885 fun animDialog (dialog : AlertDialog ? ): DialogWrap ? {
7986 if (dialog != null && ! dialog.isShowing) {
8087 dialog.window?.run {
@@ -402,38 +409,78 @@ class DialogHelper {
402409 return ThemeModeState .isDarkMode()
403410 }
404411
412+ // Nền fallback (màu đặc theo theme, hoặc dim nếu là dialog dạng floating).
413+ // Tách riêng để có thể hiện ngay lập tức, không phải đợi bước blur ảnh nền phía sau.
414+ private fun applyFallbackWindowBg (window : Window , activity : Activity , wallpaperMode : Boolean ) {
415+ window.run {
416+ try {
417+ val bg = getWindowBackground(activity)
418+ if (bg == Color .TRANSPARENT ) {
419+ if (isFloating) {
420+ setBackgroundDrawable(bg.toDrawable())
421+ setDimAmount(0.8f )
422+ return
423+ } else {
424+ val d = if (wallpaperMode || isNightMode(context)) {
425+ Color .argb(255 , 18 , 18 , 18 ).toDrawable()
426+ } else {
427+ Color .argb(255 , 245 , 245 , 245 ).toDrawable()
428+ }
429+ setBackgroundDrawable(d)
430+ }
431+ } else {
432+ setBackgroundDrawable(bg.toDrawable())
433+ }
434+ } catch (_: Exception ) {
435+ setBackgroundDrawable(Color .TRANSPARENT .toDrawable())
436+ }
437+ }
438+ }
439+
405440 // Trong setWindowBlurBg
406441 fun setWindowBlurBg (window : Window , activity : Activity ) {
407442 val wallpaperMode = activity.window.attributes.flags and WindowManager .LayoutParams .FLAG_SHOW_WALLPAPER != 0
408- window.run {
409- val blurBitmap = if (disableBlurBg) {
443+
444+ // Hiện nền fallback NGAY LẬP TỨC để dialog xuất hiện tức thì. Nếu blur bật và
445+ // thành công, nền này sẽ được thay bằng ảnh mờ ngay khi xử lý xong ở bước dưới -
446+ // đánh đổi một nhịp hiện nền tạm để đổi lấy việc không giật main thread khi mở
447+ // dialog như cách làm đồng bộ trước đây.
448+ applyFallbackWindowBg(window, activity, wallpaperMode)
449+
450+ if (disableBlurBg) return
451+
452+ // Bước chụp BẮT BUỘC chạy trên main thread vì cần view.draw() với view hierarchy
453+ // hiện tại. Nhờ chụp trực tiếp ở CAPTURE_SCALE (10%) thay vì full-res nên bước
454+ // này rất nhẹ, không đáng kể tới hiệu năng main thread.
455+ val smallBmp = FastBlurUtility .takeScreenShot(activity, FastBlurUtility .CAPTURE_SCALE ) ? : return
456+
457+ val decorView = window.decorView
458+ val targetW = decorView.width
459+ val targetH = decorView.height
460+ if (targetW <= 0 || targetH <= 0 ) {
461+ if (! smallBmp.isRecycled) smallBmp.recycle()
462+ return
463+ }
464+
465+ // Phần xử lý blur (GPU RenderEffect / CPU StackBlur + phóng to) là phần nặng thực
466+ // sự và không đụng tới View hierarchy, nên chạy an toàn trên background thread -
467+ // đây chính là phần trước đây làm main thread bị giật mỗi khi mở dialog.
468+ blurExecutor.execute {
469+ val blurred = try {
470+ FastBlurUtility .blurCapturedBitmap(smallBmp, targetW, targetH)
471+ } catch (e: Throwable ) {
410472 null
411- } else {
412- FastBlurUtility .getBlurBackgroundDrawer(activity)
413473 }
414- if (blurBitmap != null ) {
415- setBackgroundDrawable(blurBitmap.toDrawable(activity.resources))
416- } else {
417- try {
418- val bg = getWindowBackground(activity)
419- if (bg == Color .TRANSPARENT ) {
420- if (isFloating) {
421- setBackgroundDrawable(bg.toDrawable())
422- setDimAmount(0.8f )
423- return
424- } else {
425- val d = if (wallpaperMode || isNightMode(context)) {
426- Color .argb(255 , 18 , 18 , 18 ).toDrawable()
427- } else {
428- Color .argb(255 , 245 , 245 , 245 ).toDrawable()
429- }
430- setBackgroundDrawable(d)
431- }
432- } else {
433- setBackgroundDrawable(bg.toDrawable())
474+ if (blurred != null ) {
475+ decorView.post {
476+ // Dialog có thể đã bị dismiss / activity đã finish trong lúc blur chạy
477+ // nền -> luôn kiểm tra lại trước khi set background để tránh crash
478+ // hoặc rò rỉ view/bitmap.
479+ if (! activity.isFinishing && decorView.isAttachedToWindow) {
480+ window.setBackgroundDrawable(blurred.toDrawable(activity.resources))
481+ } else if (! blurred.isRecycled) {
482+ blurred.recycle()
434483 }
435- } catch (_: Exception ) {
436- setBackgroundDrawable(Color .TRANSPARENT .toDrawable())
437484 }
438485 }
439486 }
0 commit comments