├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── 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
│ │ │ ├── layout
│ │ │ │ └── activity_main.xml
│ │ │ └── values-v21
│ │ │ │ └── styles.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── wildma
│ │ │ │ └── mqttandroidclient
│ │ │ │ ├── MyApplication.java
│ │ │ │ ├── permission
│ │ │ │ ├── DialogHelper.java
│ │ │ │ ├── PermissionConstants.java
│ │ │ │ └── PermissionUtils.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MyMqttService.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── wildma
│ │ │ └── mqttandroidclient
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── wildma
│ │ └── mqttandroidclient
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .gitignore
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── README.md
├── gradle.properties
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | .idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MqttAndroidClient
2 | Android消息推送MQTT
3 |
4 | 详细介绍请看文章:[Android消息推送MQTT实战](https://www.jianshu.com/p/73436a5cf855)
5 |
6 | ps:如果对你有帮助,点下star就是对我最大的认可。
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wildma/MqttAndroidClient/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Nov 08 18:05:42 CST 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MqttAndroidClient
3 | 我们需要一些您已经拒绝的权限,请同意授权,否则功能无法正常使用!
4 | 我们需要一些您已经拒绝的权限,请到设置页面手动授权,否则功能无法正常使用!
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/test/java/com/wildma/mqttandroidclient/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/wildma/mqttandroidclient/MyApplication.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | /**
7 | * Desc ${MyApplication}
8 | */
9 | public class MyApplication extends Application {
10 |
11 | private static Context mContext;
12 |
13 | @Override
14 | public void onCreate() {
15 | super.onCreate();
16 | mContext = this;
17 | }
18 |
19 | public static Context getContext() {
20 | return mContext;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/wildma/mqttandroidclient/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient;
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 | * Instrumentation 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.wildma.mqttandroidclient", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Android_studio\SDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | buildToolsVersion "26.0.0"
6 | defaultConfig {
7 | applicationId "com.wildma.mqttandroidclient"
8 | minSdkVersion 14
9 | targetSdkVersion 26
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | repositories {
23 | maven {
24 | url "https://repo.eclipse.org/content/repositories/paho-snapshots/" //MQTT
25 | }
26 | }
27 |
28 | dependencies {
29 | compile fileTree(dir: 'libs', include: ['*.jar'])
30 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
31 | exclude group: 'com.android.support', module: 'support-annotations'
32 | })
33 | compile 'com.android.support:appcompat-v7:26.+'
34 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
35 | testCompile 'junit:junit:4.12'
36 |
37 | /*MQTT*/
38 | compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'
39 | compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wildma/mqttandroidclient/permission/DialogHelper.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient.permission;
2 |
3 | import android.app.Activity;
4 | import android.content.DialogInterface;
5 | import android.support.v7.app.AlertDialog;
6 |
7 | import com.wildma.mqttandroidclient.R;
8 |
9 |
10 | /**
11 | * Desc ${DialogHelper}
12 | */
13 | public class DialogHelper {
14 |
15 | public static void showRationaleDialog(final PermissionUtils.OnRationaleListener.ShouldRequest shouldRequest, Activity activity) {
16 | if (activity == null)
17 | return;
18 | new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Light_Dialog_Alert)
19 | .setTitle(android.R.string.dialog_alert_title)
20 | .setMessage(R.string.permission_denied_hint)
21 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
22 | @Override
23 | public void onClick(DialogInterface dialog, int which) {
24 | shouldRequest.again(true);
25 | }
26 | })
27 | .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
28 | @Override
29 | public void onClick(DialogInterface dialog, int which) {
30 | shouldRequest.again(false);
31 | }
32 | })
33 | .setCancelable(false)
34 | .create()
35 | .show();
36 | }
37 |
38 | public static void showOpenAppSettingDialog(Activity activity) {
39 | if (activity == null)
40 | return;
41 | new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Light_Dialog_Alert)
42 | .setTitle(android.R.string.dialog_alert_title)
43 | .setMessage(R.string.permission_denied_forever_hint)
44 | .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
45 | @Override
46 | public void onClick(DialogInterface dialog, int which) {
47 | PermissionUtils.launchAppDetailsSettings();
48 | }
49 | })
50 | .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
51 | @Override
52 | public void onClick(DialogInterface dialog, int which) {
53 |
54 | }
55 | })
56 | .setCancelable(false)
57 | .create()
58 | .show();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wildma/mqttandroidclient/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.util.Log;
7 | import android.view.View;
8 |
9 | import com.wildma.mqttandroidclient.permission.DialogHelper;
10 | import com.wildma.mqttandroidclient.permission.PermissionConstants;
11 | import com.wildma.mqttandroidclient.permission.PermissionUtils;
12 |
13 | import java.util.List;
14 |
15 | public class MainActivity extends AppCompatActivity {
16 |
17 | private String TAG = this.getClass().getSimpleName();
18 | private Intent mIntent;
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.activity_main);
24 | requestPermission();
25 | }
26 |
27 | public void publish(View view) {
28 | //模拟闸机设备发送消息过来
29 | MyMqttService.publish("tourist enter");
30 | }
31 |
32 | /**
33 | * 申请权限
34 | */
35 | public void requestPermission() {
36 | PermissionUtils.permission(PermissionConstants.PHONE)
37 | .rationale(new PermissionUtils.OnRationaleListener() {
38 | @Override
39 | public void rationale(final ShouldRequest shouldRequest) {
40 | Log.d(TAG, "onDenied: 权限被拒绝后弹框提示");
41 | DialogHelper.showRationaleDialog(shouldRequest, MainActivity.this);
42 | }
43 | })
44 | .callback(new PermissionUtils.FullCallback() {
45 | @Override
46 | public void onGranted(List permissionsGranted) {
47 | mIntent = new Intent(MainActivity.this, MyMqttService.class);
48 | //开启服务
49 | startService(mIntent);
50 | }
51 |
52 | @Override
53 | public void onDenied(List permissionsDeniedForever,
54 | List permissionsDenied) {
55 | Log.d(TAG, "onDenied: 权限被拒绝");
56 | if (!permissionsDeniedForever.isEmpty()) {
57 | DialogHelper.showOpenAppSettingDialog(MainActivity.this);
58 | }
59 | }
60 | })
61 | .request();
62 | }
63 |
64 | @Override
65 | protected void onDestroy() {
66 | super.onDestroy();
67 | //停止服务
68 | stopService(mIntent);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wildma/mqttandroidclient/permission/PermissionConstants.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient.permission;
2 |
3 | import android.Manifest;
4 | import android.Manifest.permission;
5 | import android.annotation.SuppressLint;
6 | import android.support.annotation.StringDef;
7 |
8 | import java.lang.annotation.Retention;
9 | import java.lang.annotation.RetentionPolicy;
10 |
11 |
12 | /**
13 | * Desc ${权限相关常量}
14 | */
15 | @SuppressLint("InlinedApi")
16 | public final class PermissionConstants {
17 |
18 | public static final String CALENDAR = Manifest.permission_group.CALENDAR;
19 | public static final String CAMERA = Manifest.permission_group.CAMERA;
20 | public static final String CONTACTS = Manifest.permission_group.CONTACTS;
21 | public static final String LOCATION = Manifest.permission_group.LOCATION;
22 | public static final String MICROPHONE = Manifest.permission_group.MICROPHONE;
23 | public static final String PHONE = Manifest.permission_group.PHONE;
24 | public static final String SENSORS = Manifest.permission_group.SENSORS;
25 | public static final String SMS = Manifest.permission_group.SMS;
26 | public static final String STORAGE = Manifest.permission_group.STORAGE;
27 |
28 | private static final String[] GROUP_CALENDAR = {
29 | permission.READ_CALENDAR, permission.WRITE_CALENDAR
30 | };
31 | private static final String[] GROUP_CAMERA = {
32 | permission.CAMERA
33 | };
34 | private static final String[] GROUP_CONTACTS = {
35 | permission.READ_CONTACTS, permission.WRITE_CONTACTS, permission.GET_ACCOUNTS
36 | };
37 | private static final String[] GROUP_LOCATION = {
38 | permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION
39 | };
40 | private static final String[] GROUP_MICROPHONE = {
41 | permission.RECORD_AUDIO
42 | };
43 | private static final String[] GROUP_PHONE = {
44 | permission.READ_PHONE_STATE, permission.READ_PHONE_NUMBERS, permission.CALL_PHONE,
45 | permission.ANSWER_PHONE_CALLS, permission.READ_CALL_LOG, permission.WRITE_CALL_LOG,
46 | permission.ADD_VOICEMAIL, permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS
47 | };
48 | private static final String[] GROUP_SENSORS = {
49 | permission.BODY_SENSORS
50 | };
51 | private static final String[] GROUP_SMS = {
52 | permission.SEND_SMS, permission.RECEIVE_SMS, permission.READ_SMS,
53 | permission.RECEIVE_WAP_PUSH, permission.RECEIVE_MMS,
54 | };
55 | private static final String[] GROUP_STORAGE = {
56 | permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE
57 | };
58 |
59 | @StringDef({CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,})
60 | @Retention(RetentionPolicy.SOURCE)
61 | public @interface Permission {
62 | }
63 |
64 | public static String[] getPermissions(@Permission final String permission) {
65 | switch (permission) {
66 | case CALENDAR:
67 | return GROUP_CALENDAR;
68 | case CAMERA:
69 | return GROUP_CAMERA;
70 | case CONTACTS:
71 | return GROUP_CONTACTS;
72 | case LOCATION:
73 | return GROUP_LOCATION;
74 | case MICROPHONE:
75 | return GROUP_MICROPHONE;
76 | case PHONE:
77 | return GROUP_PHONE;
78 | case SENSORS:
79 | return GROUP_SENSORS;
80 | case SMS:
81 | return GROUP_SMS;
82 | case STORAGE:
83 | return GROUP_STORAGE;
84 | }
85 | return new String[]{permission};
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wildma/mqttandroidclient/MyMqttService.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Service;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.net.ConnectivityManager;
8 | import android.net.NetworkInfo;
9 | import android.os.Build;
10 | import android.os.Handler;
11 | import android.os.IBinder;
12 | import android.support.annotation.Nullable;
13 | import android.util.Log;
14 | import android.widget.Toast;
15 |
16 | import org.eclipse.paho.android.service.MqttAndroidClient;
17 | import org.eclipse.paho.client.mqttv3.IMqttActionListener;
18 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
19 | import org.eclipse.paho.client.mqttv3.IMqttToken;
20 | import org.eclipse.paho.client.mqttv3.MqttCallback;
21 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
22 | import org.eclipse.paho.client.mqttv3.MqttException;
23 | import org.eclipse.paho.client.mqttv3.MqttMessage;
24 |
25 | /**
26 | * Author wildma
27 | * Github https://github.com/wildma
28 | * CreateDate 2018/11/08
29 | * Desc ${MQTT服务}
30 | */
31 |
32 | public class MyMqttService extends Service {
33 |
34 | public final String TAG = MyMqttService.class.getSimpleName();
35 | private static MqttAndroidClient mqttAndroidClient;
36 | private MqttConnectOptions mMqttConnectOptions;
37 | public String HOST = "tcp://192.168.1.10:61613";//服务器地址(协议+地址+端口号)
38 | public String USERNAME = "admin";//用户名
39 | public String PASSWORD = "password";//密码
40 | public static String PUBLISH_TOPIC = "tourist_enter";//发布主题
41 | public static String RESPONSE_TOPIC = "message_arrived";//响应主题
42 | @SuppressLint("MissingPermission")
43 | public String CLIENTID = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
44 | ? Build.getSerial() : Build.SERIAL;//客户端ID,一般以客户端唯一标识符表示,这里用设备序列号表示
45 |
46 | @Override
47 | public int onStartCommand(Intent intent, int flags, int startId) {
48 | init();
49 | return super.onStartCommand(intent, flags, startId);
50 | }
51 |
52 | @Nullable
53 | @Override
54 | public IBinder onBind(Intent intent) {
55 | return null;
56 | }
57 |
58 | /**
59 | * 发布 (模拟其他客户端发布消息)
60 | *
61 | * @param message 消息
62 | */
63 | public static void publish(String message) {
64 | String topic = PUBLISH_TOPIC;
65 | Integer qos = 2;
66 | Boolean retained = false;
67 | try {
68 | //参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息
69 | mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
70 | } catch (MqttException e) {
71 | e.printStackTrace();
72 | }
73 | }
74 |
75 | /**
76 | * 响应 (收到其他客户端的消息后,响应给对方告知消息已到达或者消息有问题等)
77 | *
78 | * @param message 消息
79 | */
80 | public void response(String message) {
81 | String topic = RESPONSE_TOPIC;
82 | Integer qos = 2;
83 | Boolean retained = false;
84 | try {
85 | //参数分别为:主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息
86 | mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
87 | } catch (MqttException e) {
88 | e.printStackTrace();
89 | }
90 | }
91 |
92 | /**
93 | * 初始化
94 | */
95 | private void init() {
96 | String serverURI = HOST; //服务器地址(协议+地址+端口号)
97 | mqttAndroidClient = new MqttAndroidClient(this, serverURI, CLIENTID);
98 | mqttAndroidClient.setCallback(mqttCallback); //设置监听订阅消息的回调
99 | mMqttConnectOptions = new MqttConnectOptions();
100 | mMqttConnectOptions.setCleanSession(true); //设置是否清除缓存
101 | mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间,单位:秒
102 | mMqttConnectOptions.setKeepAliveInterval(20); //设置心跳包发送间隔,单位:秒
103 | mMqttConnectOptions.setUserName(USERNAME); //设置用户名
104 | mMqttConnectOptions.setPassword(PASSWORD.toCharArray()); //设置密码
105 |
106 | // last will message
107 | boolean doConnect = true;
108 | String message = "{\"terminal_uid\":\"" + CLIENTID + "\"}";
109 | String topic = PUBLISH_TOPIC;
110 | Integer qos = 2;
111 | Boolean retained = false;
112 | if ((!message.equals("")) || (!topic.equals(""))) {
113 | // 最后的遗嘱
114 | try {
115 | mMqttConnectOptions.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue());
116 | } catch (Exception e) {
117 | Log.i(TAG, "Exception Occured", e);
118 | doConnect = false;
119 | iMqttActionListener.onFailure(null, e);
120 | }
121 | }
122 | if (doConnect) {
123 | doClientConnection();
124 | }
125 | }
126 |
127 | /**
128 | * 连接MQTT服务器
129 | */
130 | private void doClientConnection() {
131 | if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {
132 | try {
133 | mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);
134 | } catch (MqttException e) {
135 | e.printStackTrace();
136 | }
137 | }
138 | }
139 |
140 | /**
141 | * 判断网络是否连接
142 | */
143 | private boolean isConnectIsNomarl() {
144 | ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
145 | NetworkInfo info = connectivityManager.getActiveNetworkInfo();
146 | if (info != null && info.isAvailable()) {
147 | String name = info.getTypeName();
148 | Log.i(TAG, "当前网络名称:" + name);
149 | return true;
150 | } else {
151 | Log.i(TAG, "没有可用网络");
152 | /*没有可用网络的时候,延迟3秒再尝试重连*/
153 | new Handler().postDelayed(new Runnable() {
154 | @Override
155 | public void run() {
156 | doClientConnection();
157 | }
158 | }, 3000);
159 | return false;
160 | }
161 | }
162 |
163 | //MQTT是否连接成功的监听
164 | private IMqttActionListener iMqttActionListener = new IMqttActionListener() {
165 |
166 | @Override
167 | public void onSuccess(IMqttToken arg0) {
168 | Log.i(TAG, "连接成功 ");
169 | try {
170 | mqttAndroidClient.subscribe(PUBLISH_TOPIC, 2);//订阅主题,参数:主题、服务质量
171 | } catch (MqttException e) {
172 | e.printStackTrace();
173 | }
174 | }
175 |
176 | @Override
177 | public void onFailure(IMqttToken arg0, Throwable arg1) {
178 | arg1.printStackTrace();
179 | Log.i(TAG, "连接失败 ");
180 | doClientConnection();//连接失败,重连(可关闭服务器进行模拟)
181 | }
182 | };
183 |
184 | //订阅主题的回调
185 | private MqttCallback mqttCallback = new MqttCallback() {
186 |
187 | @Override
188 | public void messageArrived(String topic, MqttMessage message) throws Exception {
189 | Log.i(TAG, "收到消息: " + new String(message.getPayload()));
190 | //收到消息,这里弹出Toast表示。如果需要更新UI,可以使用广播或者EventBus进行发送
191 | Toast.makeText(getApplicationContext(), "messageArrived: " + new String(message.getPayload()), Toast.LENGTH_LONG).show();
192 | //收到其他客户端的消息后,响应给对方告知消息已到达或者消息有问题等
193 | response("message arrived");
194 | }
195 |
196 | @Override
197 | public void deliveryComplete(IMqttDeliveryToken arg0) {
198 |
199 | }
200 |
201 | @Override
202 | public void connectionLost(Throwable arg0) {
203 | Log.i(TAG, "连接断开 ");
204 | doClientConnection();//连接断开,重连
205 | }
206 | };
207 |
208 | @Override
209 | public void onDestroy() {
210 | try {
211 | mqttAndroidClient.disconnect(); //断开连接
212 | } catch (MqttException e) {
213 | e.printStackTrace();
214 | }
215 | super.onDestroy();
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/app/src/main/java/com/wildma/mqttandroidclient/permission/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package com.wildma.mqttandroidclient.permission;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.pm.PackageManager;
7 | import android.net.Uri;
8 | import android.os.Build;
9 | import android.os.Bundle;
10 | import android.support.annotation.NonNull;
11 | import android.support.annotation.Nullable;
12 | import android.support.annotation.RequiresApi;
13 | import android.support.v4.content.ContextCompat;
14 | import android.util.Log;
15 | import android.view.MotionEvent;
16 | import android.view.WindowManager;
17 |
18 | import com.wildma.mqttandroidclient.MyApplication;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Arrays;
22 | import java.util.Collections;
23 | import java.util.LinkedHashSet;
24 | import java.util.List;
25 | import java.util.Set;
26 |
27 | /**
28 | * Desc ${权限工具类}
29 | * 注意:需要在清单文件注册PermissionActivity
30 | */
31 | public final class PermissionUtils {
32 |
33 | private static final List PERMISSIONS = getPermissions();
34 |
35 | private static PermissionUtils sInstance;
36 |
37 | private OnRationaleListener mOnRationaleListener;
38 | private SimpleCallback mSimpleCallback;
39 | private FullCallback mFullCallback;
40 | private ThemeCallback mThemeCallback;
41 | private Set mPermissions;
42 | private List mPermissionsRequest;
43 | private List mPermissionsGranted;
44 | private List mPermissionsDenied;
45 | private List mPermissionsDeniedForever;
46 |
47 | /**
48 | * 获取应用权限
49 | *
50 | * @return 清单文件中的权限列表
51 | */
52 | public static List getPermissions() {
53 | return getPermissions(MyApplication.getContext().getPackageName());
54 | }
55 |
56 | /**
57 | * 获取应用权限
58 | *
59 | * @param packageName 包名
60 | * @return 清单文件中的权限列表
61 | */
62 | public static List getPermissions(final String packageName) {
63 | PackageManager pm = MyApplication.getContext().getPackageManager();
64 | try {
65 | return Arrays.asList(
66 | pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)
67 | .requestedPermissions
68 | );
69 | } catch (PackageManager.NameNotFoundException e) {
70 | e.printStackTrace();
71 | return Collections.emptyList();
72 | }
73 | }
74 |
75 | /**
76 | * 判断权限是否已授权
77 | *
78 | * @param permissions
79 | * @return
80 | */
81 | public static boolean isGranted(final String... permissions) {
82 | for (String permission : permissions) {
83 | if (!isGranted(permission)) {
84 | return false;
85 | }
86 | }
87 | return true;
88 | }
89 |
90 | private static boolean isGranted(final String permission) {
91 | return Build.VERSION.SDK_INT < Build.VERSION_CODES.M
92 | || PackageManager.PERMISSION_GRANTED
93 | == ContextCompat.checkSelfPermission(MyApplication.getContext(), permission);
94 | }
95 |
96 | /**
97 | * 打开应用详情设置界面
98 | */
99 | public static void launchAppDetailsSettings() {
100 | Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
101 | intent.setData(Uri.parse("package:" + MyApplication.getContext().getPackageName()));
102 | MyApplication.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
103 | }
104 |
105 | /**
106 | * 设置请求权限
107 | *
108 | * @param permissions 要请求的权限
109 | * @return {@link PermissionUtils}
110 | */
111 | public static PermissionUtils permission(@PermissionConstants.Permission final String... permissions) {
112 | return new PermissionUtils(permissions);
113 | }
114 |
115 | private PermissionUtils(final String... permissions) {
116 | mPermissions = new LinkedHashSet<>();
117 | for (String permission : permissions) {
118 | for (String aPermission : PermissionConstants.getPermissions(permission)) {
119 | if (PERMISSIONS.contains(aPermission)) {
120 | mPermissions.add(aPermission);
121 | }
122 | }
123 | }
124 | sInstance = this;
125 | }
126 |
127 | /**
128 | * Set rationale listener.
129 | *
130 | * @param listener The rationale listener.
131 | * @return the single {@link PermissionUtils} instance
132 | */
133 | public PermissionUtils rationale(final OnRationaleListener listener) {
134 | mOnRationaleListener = listener;
135 | return this;
136 | }
137 |
138 | /**
139 | * Set the simple call back.
140 | *
141 | * @param callback the simple call back
142 | * @return the single {@link PermissionUtils} instance
143 | */
144 | public PermissionUtils callback(final SimpleCallback callback) {
145 | mSimpleCallback = callback;
146 | return this;
147 | }
148 |
149 | /**
150 | * Set the full call back.
151 | *
152 | * @param callback the full call back
153 | * @return the single {@link PermissionUtils} instance
154 | */
155 | public PermissionUtils callback(final FullCallback callback) {
156 | mFullCallback = callback;
157 | return this;
158 | }
159 |
160 | /**
161 | * Set the theme callback.
162 | *
163 | * @param callback The theme callback.
164 | * @return the single {@link PermissionUtils} instance
165 | */
166 | public PermissionUtils theme(final ThemeCallback callback) {
167 | mThemeCallback = callback;
168 | return this;
169 | }
170 |
171 | /**
172 | * 开始请求
173 | */
174 | public void request() {
175 | mPermissionsGranted = new ArrayList<>();
176 | mPermissionsRequest = new ArrayList<>();
177 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
178 | mPermissionsGranted.addAll(mPermissions);
179 | requestCallback();
180 | } else {
181 | for (String permission : mPermissions) {
182 | if (isGranted(permission)) {
183 | mPermissionsGranted.add(permission);
184 | } else {
185 | mPermissionsRequest.add(permission);
186 | }
187 | }
188 | if (mPermissionsRequest.isEmpty()) {
189 | requestCallback();
190 | } else {
191 | startPermissionActivity();
192 | }
193 | }
194 | }
195 |
196 | @RequiresApi(api = Build.VERSION_CODES.M)
197 | private void startPermissionActivity() {
198 | mPermissionsDenied = new ArrayList<>();
199 | mPermissionsDeniedForever = new ArrayList<>();
200 | PermissionActivity.start(MyApplication.getContext());
201 | }
202 |
203 | @RequiresApi(api = Build.VERSION_CODES.M)
204 | private boolean rationale(final Activity activity) {
205 | boolean isRationale = false;
206 | if (mOnRationaleListener != null) {
207 | for (String permission : mPermissionsRequest) {
208 | if (activity.shouldShowRequestPermissionRationale(permission)) {
209 | getPermissionsStatus(activity);
210 | mOnRationaleListener.rationale(new OnRationaleListener.ShouldRequest() {
211 | @Override
212 | public void again(boolean again) {
213 | if (again) {
214 | startPermissionActivity();
215 | } else {
216 | requestCallback();
217 | }
218 | }
219 | });
220 | isRationale = true;
221 | break;
222 | }
223 | }
224 | mOnRationaleListener = null;
225 | }
226 | return isRationale;
227 | }
228 |
229 | @RequiresApi(api = Build.VERSION_CODES.M)
230 | private void getPermissionsStatus(final Activity activity) {
231 | for (String permission : mPermissionsRequest) {
232 | if (isGranted(permission)) {
233 | mPermissionsGranted.add(permission);
234 | } else {
235 | mPermissionsDenied.add(permission);
236 | if (!activity.shouldShowRequestPermissionRationale(permission)) {
237 | mPermissionsDeniedForever.add(permission);
238 | }
239 | }
240 | }
241 | }
242 |
243 | private void requestCallback() {
244 | if (mSimpleCallback != null) {
245 | if (mPermissionsRequest.size() == 0
246 | || mPermissions.size() == mPermissionsGranted.size()) {
247 | mSimpleCallback.onGranted();
248 | } else {
249 | if (!mPermissionsDenied.isEmpty()) {
250 | mSimpleCallback.onDenied();
251 | }
252 | }
253 | mSimpleCallback = null;
254 | }
255 | if (mFullCallback != null) {
256 | if (mPermissionsRequest.size() == 0
257 | || mPermissions.size() == mPermissionsGranted.size()) {
258 | mFullCallback.onGranted(mPermissionsGranted);
259 | } else {
260 | if (!mPermissionsDenied.isEmpty()) {
261 | mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied);
262 | }
263 | }
264 | mFullCallback = null;
265 | }
266 | mOnRationaleListener = null;
267 | mThemeCallback = null;
268 | }
269 |
270 | @RequiresApi(api = Build.VERSION_CODES.M)
271 | private void onRequestPermissionsResult(final Activity activity) {
272 | getPermissionsStatus(activity);
273 | requestCallback();
274 | }
275 |
276 |
277 | @RequiresApi(api = Build.VERSION_CODES.M)
278 | public static class PermissionActivity extends Activity {
279 |
280 | public static void start(final Context context) {
281 | Intent starter = new Intent(context, PermissionActivity.class);
282 | starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
283 | context.startActivity(starter);
284 | }
285 |
286 | @Override
287 | protected void onCreate(@Nullable Bundle savedInstanceState) {
288 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
289 | | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
290 | if (sInstance == null) {
291 | super.onCreate(savedInstanceState);
292 | Log.e("PermissionUtils", "request permissions failed");
293 | finish();
294 | return;
295 | }
296 | if (sInstance.mThemeCallback != null) {
297 | sInstance.mThemeCallback.onActivityCreate(this);
298 | }
299 | super.onCreate(savedInstanceState);
300 |
301 | if (sInstance.rationale(this)) {
302 | finish();
303 | return;
304 | }
305 | if (sInstance.mPermissionsRequest != null) {
306 | int size = sInstance.mPermissionsRequest.size();
307 | if (size <= 0) {
308 | finish();
309 | return;
310 | }
311 | requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1);
312 | }
313 | }
314 |
315 | @Override
316 | public void onRequestPermissionsResult(int requestCode,
317 | @NonNull String[] permissions,
318 | @NonNull int[] grantResults) {
319 | sInstance.onRequestPermissionsResult(this);
320 | finish();
321 | }
322 |
323 | @Override
324 | public boolean dispatchTouchEvent(MotionEvent ev) {
325 | finish();
326 | return true;
327 | }
328 | }
329 |
330 | ///////////////////////////////////////////////////////////////////////////
331 | // interface
332 | ///////////////////////////////////////////////////////////////////////////
333 |
334 | /*设置拒绝权限后再次请求的回调接口*/
335 | public interface OnRationaleListener {
336 |
337 | void rationale(ShouldRequest shouldRequest);
338 |
339 | interface ShouldRequest {
340 | void again(boolean again);
341 | }
342 | }
343 |
344 | /*是否授权回调接口*/
345 | public interface SimpleCallback {
346 | void onGranted();
347 |
348 | void onDenied();
349 | }
350 |
351 | /*是否授权回调接口*/
352 | public interface FullCallback {
353 | void onGranted(List permissionsGranted);
354 |
355 | void onDenied(List permissionsDeniedForever, List permissionsDenied);
356 | }
357 |
358 | /*主题回调接口*/
359 | public interface ThemeCallback {
360 | void onActivityCreate(Activity activity);
361 | }
362 | }
--------------------------------------------------------------------------------