11package com .hjq .toast ;
22
3+ import android .app .AppOpsManager ;
4+ import android .app .Application ;
5+ import android .app .NotificationManager ;
6+ import android .content .Context ;
7+ import android .os .Build ;
38import android .os .Handler ;
49import android .os .Looper ;
510import android .os .Message ;
611import android .widget .Toast ;
712
13+ import java .lang .reflect .Field ;
14+ import java .lang .reflect .InvocationTargetException ;
15+ import java .lang .reflect .Method ;
816import java .util .Queue ;
917import java .util .concurrent .ArrayBlockingQueue ;
1018
@@ -43,6 +51,36 @@ public ToastStrategy() {
4351 mQueue = getToastQueue ();
4452 }
4553
54+ @ Override
55+ public Toast create (Application application ) {
56+ Toast toast ;
57+ // 初始化吐司
58+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
59+ // 适配 Android 11 无法使用自定义 Toast 的问题
60+ // 官方文档:https://developer.android.google.cn/preview/features/toasts
61+ toast = new CustomToast (application );
62+ } else if (Build .VERSION .SDK_INT == Build .VERSION_CODES .N_MR1 ) {
63+ // 处理 Android 7.1 上 Toast 在主线程被阻塞后会导致报错的问题
64+ toast = new SafeToast (application );
65+ } else {
66+ boolean check =
67+ // 对比不同版本的 NMS 的源码发现这个问题在 Android 9.0 已经被谷歌修复了
68+ Build .VERSION .SDK_INT >= Build .VERSION_CODES .Q ||
69+ // 判断当前应用是否有通知栏权限,如果关闭会导致弹 Toast 无法显示
70+ areNotificationsEnabled (application ) ||
71+ // 判断当前是否是小米手机,因为只有小米手机做了特殊处理,就算没有通知栏权限也能弹吐司
72+ "xiaomi" .equals (Build .MANUFACTURER .toLowerCase ());
73+ if (check ) {
74+ // 检查通过,返回正常类型的 Toast 即可
75+ toast = new NormalToast (application );
76+ } else {
77+ // 修复关闭通知栏权限后 Toast 不显示的问题
78+ toast = new CustomToast (application );
79+ }
80+ }
81+ return toast ;
82+ }
83+
4684 @ Override
4785 public void bind (Toast toast ) {
4886 mToast = toast ;
@@ -122,7 +160,30 @@ public Queue<CharSequence> getToastQueue() {
122160 * 根据文本来获取吐司的显示时长
123161 */
124162 public int getToastDuration (CharSequence text ) {
125- // 如果显示的文字超过了10个就显示长吐司 ,否则显示短吐司
163+ // 如果显示的文字超过了 20 个字符就显示长吐司 ,否则显示短吐司
126164 return text .length () > 20 ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT ;
127165 }
166+
167+ /**
168+ * 检查通知栏权限有没有开启
169+ *
170+ * 参考 SupportCompat 包中的方法: NotificationManagerCompat.from(context).areNotificationsEnabled();
171+ */
172+ private static boolean areNotificationsEnabled (Context context ) {
173+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
174+ return context .getSystemService (NotificationManager .class ).areNotificationsEnabled ();
175+ } else if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .KITKAT ) {
176+ AppOpsManager appOps = (AppOpsManager ) context .getSystemService (Context .APP_OPS_SERVICE );
177+ try {
178+ Method method = appOps .getClass ().getMethod ("checkOpNoThrow" , Integer .TYPE , Integer .TYPE , String .class );
179+ Field field = appOps .getClass ().getDeclaredField ("OP_POST_NOTIFICATION" );
180+ int value = (Integer ) field .get (Integer .class );
181+ return ((int ) method .invoke (appOps , value , context .getApplicationInfo ().uid , context .getPackageName ())) == AppOpsManager .MODE_ALLOWED ;
182+ } catch (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException ignored ) {
183+ return true ;
184+ }
185+ } else {
186+ return true ;
187+ }
188+ }
128189}
0 commit comments