├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml └── runConfigurations.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── windowheadtoast │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── windowheadtoast │ │ │ ├── MainActivity.java │ │ │ ├── RomUtils │ │ │ ├── FloatWindowManager.java │ │ │ ├── HuaweiUtils.java │ │ │ ├── MeizuUtils.java │ │ │ ├── MiuiUtils.java │ │ │ ├── QikuUtils.java │ │ │ └── RomUtils.java │ │ │ └── WindowHeadToast.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── headview.gif │ │ ├── ic_launcher_background.xml │ │ └── shap_header_toast_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── header_toast.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── windowheadtoast │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanmusi2323/WindowHeadToast/f04b5b3b530a7f04684aac076cfa08eeb03b08db/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WindowHeadToast 2 | 模仿微信、QQ来消息时候的通知弹窗 3 | 效果图如下所示 4 | https://github.com/zhanmusi2323/WindowHeadToast/blob/master/app/src/main/res/drawable/headview.gif 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.example.windowheadtoast" 7 | minSdkVersion 15 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/windowheadtoast/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.windowheadtoast; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.windowheadtoast", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.windowheadtoast; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Button; 7 | 8 | import com.example.windowheadtoast.RomUtils.FloatWindowManager; 9 | 10 | public class MainActivity extends AppCompatActivity implements View.OnClickListener { 11 | private Button button; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | initView(); 18 | FloatWindowManager.getInstance().applyOrShowFloatWindow(this); 19 | // windowHeadToast.initHeadToastView(); 20 | } 21 | 22 | private void initView() { 23 | button = findViewById(R.id.button); 24 | button.setOnClickListener(this); 25 | } 26 | 27 | @Override 28 | protected void onResume() { 29 | super.onResume(); 30 | } 31 | 32 | @Override 33 | public void onClick(View v) { 34 | int id = v.getId(); 35 | if (id == R.id.button) { 36 | WindowHeadToast windowHeadToast = new WindowHeadToast(this); 37 | windowHeadToast.showCustomToast(); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/RomUtils/FloatWindowManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.example.windowheadtoast.RomUtils; 5 | 6 | import android.app.AlertDialog; 7 | import android.content.Context; 8 | import android.content.DialogInterface; 9 | import android.content.Intent; 10 | import android.net.Uri; 11 | import android.os.Build; 12 | import android.provider.Settings; 13 | 14 | /** 15 | * Description: 16 | * 17 | * @author zhaozp 18 | * @since 2016-10-17 19 | */ 20 | 21 | public class FloatWindowManager { 22 | private static final String TAG = "FloatWindowManager"; 23 | 24 | private static volatile FloatWindowManager instance; 25 | 26 | public static FloatWindowManager getInstance() { 27 | if (instance == null) { 28 | synchronized (FloatWindowManager.class) { 29 | if (instance == null) { 30 | instance = new FloatWindowManager(); 31 | } 32 | } 33 | } 34 | return instance; 35 | } 36 | 37 | public void applyOrShowFloatWindow(Context context) { 38 | if (!checkPermission(context)) { 39 | showConfirmDialog(context); 40 | } 41 | } 42 | 43 | private boolean checkPermission(Context context) { 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 45 | //6.0 版本之后由于 google 增加了对悬浮窗权限的管理,所以方式就统一了 46 | return Settings.canDrawOverlays(context); 47 | } else if (RomUtils.checkIsMiuiRom()) { 48 | return MiuiUtils.checkFloatWindowPermission(context); 49 | } else if (RomUtils.checkIsMeizuRom()) { 50 | return MeizuUtils.checkFloatWindowPermission(context); 51 | } else if (RomUtils.checkIsHuaweiRom()) { 52 | return HuaweiUtils.checkFloatWindowPermission(context); 53 | } else if (RomUtils.checkIs360Rom()) { 54 | return QikuUtils.checkFloatWindowPermission(context); 55 | } else { 56 | return true; 57 | } 58 | } 59 | 60 | private void showConfirmDialog(final Context context) { 61 | new AlertDialog.Builder(context).setCancelable(true).setTitle("") 62 | .setMessage("您的手机没有授予悬浮窗权限,请开启后再试") 63 | .setPositiveButton("现在去开启", 64 | new DialogInterface.OnClickListener() { 65 | @Override 66 | public void onClick(DialogInterface dialog, int which) { 67 | confirmResult(context, true); 68 | dialog.dismiss(); 69 | } 70 | }).setNegativeButton("暂不开启", 71 | new DialogInterface.OnClickListener() { 72 | 73 | @Override 74 | public void onClick(DialogInterface dialog, int which) { 75 | confirmResult(context, false); 76 | dialog.dismiss(); 77 | } 78 | }) 79 | .create() 80 | .show(); 81 | } 82 | 83 | private void confirmResult(Context context, boolean bool) { 84 | if (!bool) { 85 | //do something 86 | return; 87 | } 88 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 89 | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName())); 90 | context.startActivity(intent); 91 | } else if (RomUtils.checkIsMiuiRom()) { 92 | MiuiUtils.applyMiuiPermission(context); 93 | } else if (RomUtils.checkIsMeizuRom()) { 94 | MeizuUtils.applyPermission(context); 95 | } else if (RomUtils.checkIsHuaweiRom()) { 96 | HuaweiUtils.applyPermission(context); 97 | } else if (RomUtils.checkIs360Rom()) { 98 | QikuUtils.applyPermission(context); 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/RomUtils/HuaweiUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.example.windowheadtoast.RomUtils; 5 | 6 | import android.annotation.TargetApi; 7 | import android.app.AppOpsManager; 8 | import android.content.ActivityNotFoundException; 9 | import android.content.ComponentName; 10 | import android.content.Context; 11 | import android.content.Intent; 12 | import android.os.Binder; 13 | import android.os.Build; 14 | import android.util.Log; 15 | import android.widget.Toast; 16 | 17 | import java.lang.reflect.Method; 18 | 19 | public class HuaweiUtils { 20 | private static final String TAG = "HuaweiUtils"; 21 | 22 | /** 23 | * 检测 Huawei 悬浮窗权限 24 | */ 25 | public static boolean checkFloatWindowPermission(Context context) { 26 | final int version = Build.VERSION.SDK_INT; 27 | if (version >= 19) { 28 | return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; 29 | } 30 | return true; 31 | } 32 | 33 | /** 34 | * 去华为权限申请页面 35 | */ 36 | public static void applyPermission(Context context) { 37 | try { 38 | Intent intent = new Intent(); 39 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 40 | // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理 41 | // ComponentName comp = new ComponentName("com.huawei.systemmanager", 42 | // "com.huawei.permissionmanager.ui.SingleAppActivity");//华为权限管理,跳转到指定app的权限管理位置需要华为接口权限,未解决 43 | ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面 44 | intent.setComponent(comp); 45 | if (RomUtils.getEmuiVersion() == 3.1) { 46 | //emui 3.1 的适配 47 | context.startActivity(intent); 48 | } else { 49 | //emui 3.0 的适配 50 | comp = new ComponentName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity");//悬浮窗管理页面 51 | intent.setComponent(comp); 52 | context.startActivity(intent); 53 | } 54 | } catch (SecurityException e) { 55 | Intent intent = new Intent(); 56 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 57 | // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理 58 | ComponentName comp = new ComponentName("com.huawei.systemmanager", 59 | "com.huawei.permissionmanager.ui.MainActivity");//华为权限管理,跳转到本app的权限管理页面,这个需要华为接口权限,未解决 60 | // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面 61 | intent.setComponent(comp); 62 | context.startActivity(intent); 63 | Log.e(TAG, Log.getStackTraceString(e)); 64 | } catch (ActivityNotFoundException e) { 65 | /** 66 | * 手机管家版本较低 HUAWEI SC-UL10 67 | */ 68 | // Toast.makeText(MainActivity.this, "act找不到", Toast.LENGTH_LONG).show(); 69 | Intent intent = new Intent(); 70 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 71 | ComponentName comp = new ComponentName("com.Android.settings", "com.android.settings.permission.TabItem");//权限管理页面 android4.4 72 | // ComponentName comp = new ComponentName("com.android.settings","com.android.settings.permission.single_app_activity");//此处可跳转到指定app对应的权限管理页面,但是需要相关权限,未解决 73 | intent.setComponent(comp); 74 | context.startActivity(intent); 75 | e.printStackTrace(); 76 | Log.e(TAG, Log.getStackTraceString(e)); 77 | } catch (Exception e) { 78 | //抛出异常时提示信息 79 | Toast.makeText(context, "进入设置页面失败,请手动设置", Toast.LENGTH_LONG).show(); 80 | Log.e(TAG, Log.getStackTraceString(e)); 81 | } 82 | } 83 | 84 | @TargetApi(Build.VERSION_CODES.KITKAT) 85 | private static boolean checkOp(Context context, int op) { 86 | final int version = Build.VERSION.SDK_INT; 87 | if (version >= 19) { 88 | AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 89 | try { 90 | Class clazz = AppOpsManager.class; 91 | Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); 92 | return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); 93 | } catch (Exception e) { 94 | Log.e(TAG, Log.getStackTraceString(e)); 95 | } 96 | } else { 97 | Log.e(TAG, "Below API 19 cannot invoke!"); 98 | } 99 | return false; 100 | } 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/RomUtils/MeizuUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.example.windowheadtoast.RomUtils; 5 | 6 | import android.annotation.TargetApi; 7 | import android.app.AppOpsManager; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.os.Binder; 11 | import android.os.Build; 12 | import android.util.Log; 13 | 14 | import java.lang.reflect.Method; 15 | 16 | public class MeizuUtils { 17 | private static final String TAG = "MeizuUtils"; 18 | 19 | /** 20 | * 检测 meizu 悬浮窗权限 21 | */ 22 | public static boolean checkFloatWindowPermission(Context context) { 23 | final int version = Build.VERSION.SDK_INT; 24 | if (version >= 19) { 25 | return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; 26 | } 27 | return true; 28 | } 29 | 30 | /** 31 | * 去魅族权限申请页面 32 | */ 33 | public static void applyPermission(Context context){ 34 | Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); 35 | intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity"); 36 | intent.putExtra("packageName", context.getPackageName()); 37 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 38 | context.startActivity(intent); 39 | } 40 | 41 | @TargetApi(Build.VERSION_CODES.KITKAT) 42 | private static boolean checkOp(Context context, int op) { 43 | final int version = Build.VERSION.SDK_INT; 44 | if (version >= 19) { 45 | AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 46 | try { 47 | Class clazz = AppOpsManager.class; 48 | Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); 49 | return AppOpsManager.MODE_ALLOWED == (int)method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); 50 | } catch (Exception e) { 51 | Log.e(TAG, Log.getStackTraceString(e)); 52 | } 53 | } else { 54 | Log.e(TAG, "Below API 19 cannot invoke!"); 55 | } 56 | return false; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/RomUtils/MiuiUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.example.windowheadtoast.RomUtils; 5 | 6 | import android.annotation.TargetApi; 7 | import android.app.AppOpsManager; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.pm.PackageManager; 11 | import android.net.Uri; 12 | import android.os.Binder; 13 | import android.os.Build; 14 | import android.provider.Settings; 15 | import android.util.Log; 16 | 17 | import java.lang.reflect.Method; 18 | 19 | public class MiuiUtils { 20 | private static final String TAG = "MiuiUtils"; 21 | 22 | /** 23 | * 获取小米 rom 版本号,获取失败返回 -1 24 | * 25 | * @return miui rom version code, if fail , return -1 26 | */ 27 | public static int getMiuiVersion() { 28 | String version = RomUtils.getSystemProperty("ro.miui.ui.version.name"); 29 | if (version != null) { 30 | try { 31 | return Integer.parseInt(version.substring(1)); 32 | } catch (Exception e) { 33 | Log.e(TAG, "get miui version code error, version : " + version); 34 | Log.e(TAG, Log.getStackTraceString(e)); 35 | } 36 | } 37 | return -1; 38 | } 39 | 40 | /** 41 | * 检测 miui 悬浮窗权限 42 | */ 43 | public static boolean checkFloatWindowPermission(Context context) { 44 | final int version = Build.VERSION.SDK_INT; 45 | 46 | if (version >= 19) { 47 | return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; 48 | } else { 49 | // if ((context.getApplicationInfo().flags & 1 << 27) == 1) { 50 | // return true; 51 | // } else { 52 | // return false; 53 | // } 54 | return true; 55 | } 56 | } 57 | 58 | @TargetApi(Build.VERSION_CODES.KITKAT) 59 | private static boolean checkOp(Context context, int op) { 60 | final int version = Build.VERSION.SDK_INT; 61 | if (version >= 19) { 62 | AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 63 | try { 64 | Class clazz = AppOpsManager.class; 65 | Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); 66 | return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); 67 | } catch (Exception e) { 68 | Log.e(TAG, Log.getStackTraceString(e)); 69 | } 70 | } else { 71 | Log.e(TAG, "Below API 19 cannot invoke!"); 72 | } 73 | return false; 74 | } 75 | 76 | /** 77 | * 小米 ROM 权限申请 78 | */ 79 | public static void applyMiuiPermission(Context context) { 80 | int versionCode = getMiuiVersion(); 81 | if (versionCode == 5) { 82 | goToMiuiPermissionActivity_V5(context); 83 | } else if (versionCode == 6) { 84 | goToMiuiPermissionActivity_V6(context); 85 | } else if (versionCode == 7) { 86 | goToMiuiPermissionActivity_V7(context); 87 | } else if (versionCode == 8) { 88 | goToMiuiPermissionActivity_V8(context); 89 | } else { 90 | Log.e(TAG, "this is a special MIUI rom version, its version code " + versionCode); 91 | } 92 | } 93 | 94 | private static boolean isIntentAvailable(Intent intent, Context context) { 95 | if (intent == null) { 96 | return false; 97 | } 98 | return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 99 | } 100 | 101 | /** 102 | * 小米 V5 版本 ROM权限申请 103 | */ 104 | public static void goToMiuiPermissionActivity_V5(Context context) { 105 | Intent intent = null; 106 | String packageName = context.getPackageName(); 107 | intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 108 | Uri uri = Uri.fromParts("package", packageName, null); 109 | intent.setData(uri); 110 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 111 | if (isIntentAvailable(intent, context)) { 112 | context.startActivity(intent); 113 | } else { 114 | Log.e(TAG, "intent is not available!"); 115 | } 116 | 117 | //设置页面在应用详情页面 118 | // Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 119 | // PackageInfo pInfo = null; 120 | // try { 121 | // pInfo = context.getPackageManager().getPackageInfo 122 | // (HostInterfaceManager.getHostInterface().getApp().getPackageName(), 0); 123 | // } catch (PackageManager.NameNotFoundException e) { 124 | // AVLogUtils.e(TAG, e.getMessage()); 125 | // } 126 | // intent.setClassName("com.android.settings", "com.miui.securitycenter.permission.AppPermissionsEditor"); 127 | // intent.putExtra("extra_package_uid", pInfo.applicationInfo.uid); 128 | // intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 129 | // if (isIntentAvailable(intent, context)) { 130 | // context.startActivity(intent); 131 | // } else { 132 | // AVLogUtils.e(TAG, "Intent is not available!"); 133 | // } 134 | } 135 | 136 | /** 137 | * 小米 V6 版本 ROM权限申请 138 | */ 139 | public static void goToMiuiPermissionActivity_V6(Context context) { 140 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 141 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); 142 | intent.putExtra("extra_pkgname", context.getPackageName()); 143 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 144 | 145 | if (isIntentAvailable(intent, context)) { 146 | context.startActivity(intent); 147 | } else { 148 | Log.e(TAG, "Intent is not available!"); 149 | } 150 | } 151 | 152 | /** 153 | * 小米 V7 版本 ROM权限申请 154 | */ 155 | public static void goToMiuiPermissionActivity_V7(Context context) { 156 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 157 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); 158 | intent.putExtra("extra_pkgname", context.getPackageName()); 159 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 160 | 161 | if (isIntentAvailable(intent, context)) { 162 | context.startActivity(intent); 163 | } else { 164 | Log.e(TAG, "Intent is not available!"); 165 | } 166 | } 167 | 168 | /** 169 | * 小米 V8 版本 ROM权限申请 170 | */ 171 | public static void goToMiuiPermissionActivity_V8(Context context) { 172 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 173 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); 174 | // intent.setPackage("com.miui.securitycenter"); 175 | intent.putExtra("extra_pkgname", context.getPackageName()); 176 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 177 | 178 | if (isIntentAvailable(intent, context)) { 179 | context.startActivity(intent); 180 | } else { 181 | intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 182 | intent.setPackage("com.miui.securitycenter"); 183 | intent.putExtra("extra_pkgname", context.getPackageName()); 184 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 185 | 186 | if (isIntentAvailable(intent, context)) { 187 | context.startActivity(intent); 188 | } else { 189 | Log.e(TAG, "Intent is not available!"); 190 | } 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/RomUtils/QikuUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.example.windowheadtoast.RomUtils; 5 | 6 | import android.annotation.TargetApi; 7 | import android.app.AppOpsManager; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | import android.content.pm.PackageManager; 11 | import android.os.Binder; 12 | import android.os.Build; 13 | import android.util.Log; 14 | 15 | import java.lang.reflect.Method; 16 | 17 | public class QikuUtils { 18 | private static final String TAG = "QikuUtils"; 19 | 20 | /** 21 | * 检测 360 悬浮窗权限 22 | */ 23 | public static boolean checkFloatWindowPermission(Context context) { 24 | final int version = Build.VERSION.SDK_INT; 25 | if (version >= 19) { 26 | return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24; 27 | } 28 | return true; 29 | } 30 | 31 | @TargetApi(Build.VERSION_CODES.KITKAT) 32 | private static boolean checkOp(Context context, int op) { 33 | final int version = Build.VERSION.SDK_INT; 34 | if (version >= 19) { 35 | AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 36 | try { 37 | Class clazz = AppOpsManager.class; 38 | Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); 39 | return AppOpsManager.MODE_ALLOWED == (int)method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); 40 | } catch (Exception e) { 41 | Log.e(TAG, Log.getStackTraceString(e)); 42 | } 43 | } else { 44 | Log.e("", "Below API 19 cannot invoke!"); 45 | } 46 | return false; 47 | } 48 | 49 | /** 50 | * 去360权限申请页面 51 | */ 52 | public static void applyPermission(Context context) { 53 | Intent intent = new Intent(); 54 | intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity"); 55 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 56 | if (isIntentAvailable(intent, context)) { 57 | context.startActivity(intent); 58 | } else { 59 | intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity"); 60 | if (isIntentAvailable(intent, context)) { 61 | context.startActivity(intent); 62 | } else { 63 | Log.e(TAG, "can't open permission page with particular name, please use " + 64 | "\"adb shell dumpsys activity\" command and tell me the name of the float window permission page"); 65 | } 66 | } 67 | } 68 | 69 | private static boolean isIntentAvailable(Intent intent, Context context) { 70 | if (intent == null) { 71 | return false; 72 | } 73 | return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/RomUtils/RomUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.example.windowheadtoast.RomUtils; 5 | 6 | import android.os.Build; 7 | import android.text.TextUtils; 8 | import android.util.Log; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.IOException; 12 | import java.io.InputStreamReader; 13 | 14 | /** 15 | * Description: 16 | * 17 | * @author zhaozp 18 | * @since 2016-05-23 19 | */ 20 | public class RomUtils { 21 | private static final String TAG = "RomUtils"; 22 | 23 | /** 24 | * 获取 emui 版本号 25 | * @return 26 | */ 27 | public static double getEmuiVersion() { 28 | try { 29 | String emuiVersion = getSystemProperty("ro.build.version.emui"); 30 | String version = emuiVersion.substring(emuiVersion.indexOf("_") + 1); 31 | return Double.parseDouble(version); 32 | } catch (Exception e) { 33 | e.printStackTrace(); 34 | } 35 | return 4.0; 36 | } 37 | 38 | /** 39 | * 获取小米 rom 版本号,获取失败返回 -1 40 | * 41 | * @return miui rom version code, if fail , return -1 42 | */ 43 | public static int getMiuiVersion() { 44 | String version = getSystemProperty("ro.miui.ui.version.name"); 45 | if (version != null) { 46 | try { 47 | return Integer.parseInt(version.substring(1)); 48 | } catch (Exception e) { 49 | Log.e(TAG, "get miui version code error, version : " + version); 50 | } 51 | } 52 | return -1; 53 | } 54 | 55 | public static String getSystemProperty(String propName) { 56 | String line; 57 | BufferedReader input = null; 58 | try { 59 | Process p = Runtime.getRuntime().exec("getprop " + propName); 60 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); 61 | line = input.readLine(); 62 | input.close(); 63 | } catch (IOException ex) { 64 | Log.e(TAG, "Unable to read sysprop " + propName, ex); 65 | return null; 66 | } finally { 67 | if (input != null) { 68 | try { 69 | input.close(); 70 | } catch (IOException e) { 71 | Log.e(TAG, "Exception while closing InputStream", e); 72 | } 73 | } 74 | } 75 | return line; 76 | } 77 | public static boolean checkIsHuaweiRom() { 78 | return Build.MANUFACTURER.contains("HUAWEI"); 79 | } 80 | 81 | /** 82 | * check if is miui ROM 83 | */ 84 | public static boolean checkIsMiuiRom() { 85 | return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name")); 86 | } 87 | 88 | public static boolean checkIsMeizuRom() { 89 | //return Build.MANUFACTURER.contains("Meizu"); 90 | String meizuFlymeOSFlag = getSystemProperty("ro.build.display.id"); 91 | if (TextUtils.isEmpty(meizuFlymeOSFlag)){ 92 | return false; 93 | }else if (meizuFlymeOSFlag.contains("flyme") || meizuFlymeOSFlag.toLowerCase().contains("flyme")){ 94 | return true; 95 | }else { 96 | return false; 97 | } 98 | } 99 | 100 | public static boolean checkIs360Rom() { 101 | //fix issue https://github.com/zhaozepeng/FloatWindowPermission/issues/9 102 | return Build.MANUFACTURER.contains("QiKU") 103 | || Build.MANUFACTURER.contains("360"); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/windowheadtoast/WindowHeadToast.java: -------------------------------------------------------------------------------- 1 | package com.example.windowheadtoast; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.ObjectAnimator; 6 | import android.content.Context; 7 | import android.os.Message; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.view.WindowManager; 11 | import android.widget.LinearLayout; 12 | 13 | /** 14 | * 用于自定义一个Toast,模仿类似于QQ和微信来消息时候在顶部弹出的消息提示框 15 | */ 16 | public class WindowHeadToast implements View.OnTouchListener { 17 | private Context mContext; 18 | private View headToastView; 19 | private LinearLayout linearLayout; 20 | private final static int ANIM_DURATION = 600; 21 | private final static int ANIM_DISMISSS_DURATION = 2000; 22 | private final static int ANIM_CLOSE = 20; 23 | private android.os.Handler mHander = new android.os.Handler() { 24 | @Override 25 | public void handleMessage(Message msg) { 26 | super.handleMessage(msg); 27 | if (msg.what == ANIM_CLOSE) { 28 | animDismiss(); 29 | } 30 | } 31 | }; 32 | private WindowManager wm; 33 | private int downX; 34 | private int downY; 35 | 36 | public WindowHeadToast(Context context) { 37 | mContext = context; 38 | } 39 | 40 | public void showCustomToast() { 41 | initHeadToastView(); 42 | setHeadToastViewAnim(); 43 | // 延迟2s后关闭 44 | mHander.sendEmptyMessageDelayed(ANIM_CLOSE, 2000); 45 | } 46 | 47 | private void setHeadToastViewAnim() { 48 | ObjectAnimator animator = ObjectAnimator.ofFloat(linearLayout, "translationY", -700, 0); 49 | animator.setDuration(ANIM_DURATION); 50 | animator.start(); 51 | } 52 | 53 | private void animDismiss() { 54 | if (linearLayout == null || linearLayout.getParent() == null) { 55 | return; 56 | } 57 | ObjectAnimator animator = ObjectAnimator.ofFloat(linearLayout, "translationY", 0, -700); 58 | animator.setDuration(ANIM_DURATION); 59 | animator.start(); 60 | animator.addListener(new AnimatorListenerAdapter() { 61 | @Override 62 | public void onAnimationCancel(Animator animation) { 63 | super.onAnimationCancel(animation); 64 | } 65 | 66 | @Override 67 | public void onAnimationEnd(Animator animation) { 68 | super.onAnimationEnd(animation); 69 | dismiss(); 70 | } 71 | 72 | @Override 73 | public void onAnimationRepeat(Animator animation) { 74 | super.onAnimationRepeat(animation); 75 | } 76 | 77 | @Override 78 | public void onAnimationStart(Animator animation) { 79 | super.onAnimationStart(animation); 80 | } 81 | 82 | @Override 83 | public void onAnimationPause(Animator animation) { 84 | super.onAnimationPause(animation); 85 | } 86 | 87 | @Override 88 | public void onAnimationResume(Animator animation) { 89 | super.onAnimationResume(animation); 90 | } 91 | }); 92 | } 93 | 94 | /** 95 | * 移除HeaderToast (一定要在动画结束的时候移除,不然下次进来的时候由于wm里边已经有控件了,所以会导致卡死) 96 | */ 97 | private void dismiss() { 98 | if (null != linearLayout && null != linearLayout.getParent()) { 99 | wm.removeView(linearLayout); 100 | } 101 | } 102 | 103 | public void initHeadToastView() { 104 | //准备Window要添加的View 105 | linearLayout = new LinearLayout(mContext); 106 | final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); 107 | linearLayout.setLayoutParams(layoutParams); 108 | headToastView = View.inflate(mContext, R.layout.header_toast, null); 109 | // 为headToastView设置Touch事件 110 | headToastView.setOnTouchListener(this); 111 | linearLayout.addView(headToastView); 112 | // 定义WindowManager 并且将View添加到WindowManagar中去 113 | wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); 114 | WindowManager.LayoutParams wm_params = new WindowManager.LayoutParams(); 115 | wm_params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 116 | | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 117 | | WindowManager.LayoutParams.FLAG_FULLSCREEN 118 | | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 119 | wm_params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; 120 | wm_params.x = 0; 121 | wm_params.y = 100; 122 | wm_params.format = -3; // 会影响Toast中的布局消失的时候父控件和子控件消失的时机不一致,比如设置为-1之后就会不同步 123 | wm_params.alpha = 1f; 124 | wm.addView(linearLayout, wm_params); 125 | } 126 | 127 | @Override 128 | public boolean onTouch(View v, MotionEvent event) { 129 | switch (event.getAction()) { 130 | case MotionEvent.ACTION_DOWN: 131 | downX = (int) event.getRawX(); 132 | downY = (int) event.getRawY(); 133 | break; 134 | case MotionEvent.ACTION_MOVE: 135 | int currentX = (int) event.getRawX(); 136 | int currentY = (int) event.getRawY(); 137 | if (Math.abs(currentX - downX) > 50 || Math.abs(currentY - downY) > 40) { 138 | animDismiss(); 139 | } 140 | break; 141 | default: 142 | break; 143 | } 144 | return true; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/headview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhanmusi2323/WindowHeadToast/f04b5b3b530a7f04684aac076cfa08eeb03b08db/app/src/main/res/drawable/headview.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shap_header_toast_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |