[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n.idea\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": "README.md",
    "content": "# MqttAndroidClient\nAndroid消息推送MQTT\n\n详细介绍请看文章：[Android消息推送MQTT实战](https://www.jianshu.com/p/73436a5cf855)\n\nps:如果对你有帮助，点下star就是对我最大的认可。\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 26\n    buildToolsVersion \"26.0.0\"\n    defaultConfig {\n        applicationId \"com.wildma.mqttandroidclient\"\n        minSdkVersion 14\n        targetSdkVersion 26\n        versionCode 1\n        versionName \"1.0\"\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\nrepositories {\n    maven {\n        url \"https://repo.eclipse.org/content/repositories/paho-snapshots/\" //MQTT\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile 'com.android.support:appcompat-v7:26.+'\n    compile 'com.android.support.constraint:constraint-layout:1.0.2'\n    testCompile 'junit:junit:4.12'\n\n    /*MQTT*/\n    compile 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.1.0'\n    compile 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1'\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in E:\\Android_studio\\SDK/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/androidTest/java/com/wildma/mqttandroidclient/ExampleInstrumentedTest.java",
    "content": "package com.wildma.mqttandroidclient;\n\nimport android.content.Context;\nimport android.support.test.InstrumentationRegistry;\nimport android.support.test.runner.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.wildma.mqttandroidclient\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest package=\"com.wildma.mqttandroidclient\"\n          xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.WAKE_LOCK\" />\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>\n\n    <application\n        android:name=\".MyApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity android:name=\".MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".permission.PermissionUtils$PermissionActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:multiprocess=\"true\"\n            android:theme=\"@style/ActivityTransparent\"\n            android:windowSoftInputMode=\"stateHidden|stateAlwaysHidden\"/>\n\n        <service android:name=\"org.eclipse.paho.android.service.MqttService\" /> <!--MqttService-->\n        <service android:name=\".MyMqttService\"/> <!--MyMqttService-->\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/wildma/mqttandroidclient/MainActivity.java",
    "content": "package com.wildma.mqttandroidclient;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.wildma.mqttandroidclient.permission.DialogHelper;\nimport com.wildma.mqttandroidclient.permission.PermissionConstants;\nimport com.wildma.mqttandroidclient.permission.PermissionUtils;\n\nimport java.util.List;\n\npublic class MainActivity extends AppCompatActivity {\n\n    private String TAG = this.getClass().getSimpleName();\n    private Intent mIntent;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        requestPermission();\n    }\n\n    public void publish(View view) {\n        //模拟闸机设备发送消息过来\n        MyMqttService.publish(\"tourist enter\");\n    }\n\n    /**\n     * 申请权限\n     */\n    public void requestPermission() {\n        PermissionUtils.permission(PermissionConstants.PHONE)\n                .rationale(new PermissionUtils.OnRationaleListener() {\n                    @Override\n                    public void rationale(final ShouldRequest shouldRequest) {\n                        Log.d(TAG, \"onDenied: 权限被拒绝后弹框提示\");\n                        DialogHelper.showRationaleDialog(shouldRequest, MainActivity.this);\n                    }\n                })\n                .callback(new PermissionUtils.FullCallback() {\n                    @Override\n                    public void onGranted(List<String> permissionsGranted) {\n                        mIntent = new Intent(MainActivity.this, MyMqttService.class);\n                        //开启服务\n                        startService(mIntent);\n                    }\n\n                    @Override\n                    public void onDenied(List<String> permissionsDeniedForever,\n                                         List<String> permissionsDenied) {\n                        Log.d(TAG, \"onDenied: 权限被拒绝\");\n                        if (!permissionsDeniedForever.isEmpty()) {\n                            DialogHelper.showOpenAppSettingDialog(MainActivity.this);\n                        }\n                    }\n                })\n                .request();\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        //停止服务\n        stopService(mIntent);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wildma/mqttandroidclient/MyApplication.java",
    "content": "package com.wildma.mqttandroidclient;\n\nimport android.app.Application;\nimport android.content.Context;\n\n/**\n * Desc\t        ${MyApplication}\n */\npublic class MyApplication extends Application {\n\n    private static Context mContext;\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        mContext = this;\n    }\n\n    public static Context getContext() {\n        return mContext;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wildma/mqttandroidclient/MyMqttService.java",
    "content": "package com.wildma.mqttandroidclient;\n\nimport android.annotation.SuppressLint;\nimport android.app.Service;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.support.annotation.Nullable;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport org.eclipse.paho.android.service.MqttAndroidClient;\nimport org.eclipse.paho.client.mqttv3.IMqttActionListener;\nimport org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;\nimport org.eclipse.paho.client.mqttv3.IMqttToken;\nimport org.eclipse.paho.client.mqttv3.MqttCallback;\nimport org.eclipse.paho.client.mqttv3.MqttConnectOptions;\nimport org.eclipse.paho.client.mqttv3.MqttException;\nimport org.eclipse.paho.client.mqttv3.MqttMessage;\n\n/**\n * Author       wildma\n * Github       https://github.com/wildma\n * CreateDate   2018/11/08\n * Desc\t        ${MQTT服务}\n */\n\npublic class MyMqttService extends Service {\n\n    public final   String             TAG            = MyMqttService.class.getSimpleName();\n    private static MqttAndroidClient  mqttAndroidClient;\n    private        MqttConnectOptions mMqttConnectOptions;\n    public         String             HOST           = \"tcp://192.168.1.10:61613\";//服务器地址（协议+地址+端口号）\n    public         String             USERNAME       = \"admin\";//用户名\n    public         String             PASSWORD       = \"password\";//密码\n    public static  String             PUBLISH_TOPIC  = \"tourist_enter\";//发布主题\n    public static  String             RESPONSE_TOPIC = \"message_arrived\";//响应主题\n    @SuppressLint(\"MissingPermission\")\n    public         String             CLIENTID       = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O\n            ? Build.getSerial() : Build.SERIAL;//客户端ID，一般以客户端唯一标识符表示，这里用设备序列号表示\n\n    @Override\n    public int onStartCommand(Intent intent, int flags, int startId) {\n        init();\n        return super.onStartCommand(intent, flags, startId);\n    }\n\n    @Nullable\n    @Override\n    public IBinder onBind(Intent intent) {\n        return null;\n    }\n\n    /**\n     * 发布 （模拟其他客户端发布消息）\n     *\n     * @param message 消息\n     */\n    public static void publish(String message) {\n        String topic = PUBLISH_TOPIC;\n        Integer qos = 2;\n        Boolean retained = false;\n        try {\n            //参数分别为：主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息\n            mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());\n        } catch (MqttException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 响应 （收到其他客户端的消息后，响应给对方告知消息已到达或者消息有问题等）\n     *\n     * @param message 消息\n     */\n    public void response(String message) {\n        String topic = RESPONSE_TOPIC;\n        Integer qos = 2;\n        Boolean retained = false;\n        try {\n            //参数分别为：主题、消息的字节数组、服务质量、是否在服务器保留断开连接后的最后一条消息\n            mqttAndroidClient.publish(topic, message.getBytes(), qos.intValue(), retained.booleanValue());\n        } catch (MqttException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * 初始化\n     */\n    private void init() {\n        String serverURI = HOST; //服务器地址（协议+地址+端口号）\n        mqttAndroidClient = new MqttAndroidClient(this, serverURI, CLIENTID);\n        mqttAndroidClient.setCallback(mqttCallback); //设置监听订阅消息的回调\n        mMqttConnectOptions = new MqttConnectOptions();\n        mMqttConnectOptions.setCleanSession(true); //设置是否清除缓存\n        mMqttConnectOptions.setConnectionTimeout(10); //设置超时时间，单位：秒\n        mMqttConnectOptions.setKeepAliveInterval(20); //设置心跳包发送间隔，单位：秒\n        mMqttConnectOptions.setUserName(USERNAME); //设置用户名\n        mMqttConnectOptions.setPassword(PASSWORD.toCharArray()); //设置密码\n\n        // last will message\n        boolean doConnect = true;\n        String message = \"{\\\"terminal_uid\\\":\\\"\" + CLIENTID + \"\\\"}\";\n        String topic = PUBLISH_TOPIC;\n        Integer qos = 2;\n        Boolean retained = false;\n        if ((!message.equals(\"\")) || (!topic.equals(\"\"))) {\n            // 最后的遗嘱\n            try {\n                mMqttConnectOptions.setWill(topic, message.getBytes(), qos.intValue(), retained.booleanValue());\n            } catch (Exception e) {\n                Log.i(TAG, \"Exception Occured\", e);\n                doConnect = false;\n                iMqttActionListener.onFailure(null, e);\n            }\n        }\n        if (doConnect) {\n            doClientConnection();\n        }\n    }\n\n    /**\n     * 连接MQTT服务器\n     */\n    private void doClientConnection() {\n        if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {\n            try {\n                mqttAndroidClient.connect(mMqttConnectOptions, null, iMqttActionListener);\n            } catch (MqttException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * 判断网络是否连接\n     */\n    private boolean isConnectIsNomarl() {\n        ConnectivityManager connectivityManager = (ConnectivityManager) this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);\n        NetworkInfo info = connectivityManager.getActiveNetworkInfo();\n        if (info != null && info.isAvailable()) {\n            String name = info.getTypeName();\n            Log.i(TAG, \"当前网络名称：\" + name);\n            return true;\n        } else {\n            Log.i(TAG, \"没有可用网络\");\n            /*没有可用网络的时候，延迟3秒再尝试重连*/\n            new Handler().postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    doClientConnection();\n                }\n            }, 3000);\n            return false;\n        }\n    }\n\n    //MQTT是否连接成功的监听\n    private IMqttActionListener iMqttActionListener = new IMqttActionListener() {\n\n        @Override\n        public void onSuccess(IMqttToken arg0) {\n            Log.i(TAG, \"连接成功 \");\n            try {\n                mqttAndroidClient.subscribe(PUBLISH_TOPIC, 2);//订阅主题，参数：主题、服务质量\n            } catch (MqttException e) {\n                e.printStackTrace();\n            }\n        }\n\n        @Override\n        public void onFailure(IMqttToken arg0, Throwable arg1) {\n            arg1.printStackTrace();\n            Log.i(TAG, \"连接失败 \");\n            doClientConnection();//连接失败，重连（可关闭服务器进行模拟）\n        }\n    };\n\n    //订阅主题的回调\n    private MqttCallback mqttCallback = new MqttCallback() {\n\n        @Override\n        public void messageArrived(String topic, MqttMessage message) throws Exception {\n            Log.i(TAG, \"收到消息： \" + new String(message.getPayload()));\n            //收到消息，这里弹出Toast表示。如果需要更新UI，可以使用广播或者EventBus进行发送\n            Toast.makeText(getApplicationContext(), \"messageArrived: \" + new String(message.getPayload()), Toast.LENGTH_LONG).show();\n            //收到其他客户端的消息后，响应给对方告知消息已到达或者消息有问题等\n            response(\"message arrived\");\n        }\n\n        @Override\n        public void deliveryComplete(IMqttDeliveryToken arg0) {\n\n        }\n\n        @Override\n        public void connectionLost(Throwable arg0) {\n            Log.i(TAG, \"连接断开 \");\n            doClientConnection();//连接断开，重连\n        }\n    };\n\n    @Override\n    public void onDestroy() {\n        try {\n            mqttAndroidClient.disconnect(); //断开连接\n        } catch (MqttException e) {\n            e.printStackTrace();\n        }\n        super.onDestroy();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wildma/mqttandroidclient/permission/DialogHelper.java",
    "content": "package com.wildma.mqttandroidclient.permission;\n\nimport android.app.Activity;\nimport android.content.DialogInterface;\nimport android.support.v7.app.AlertDialog;\n\nimport com.wildma.mqttandroidclient.R;\n\n\n/**\n * Desc\t        ${DialogHelper}\n */\npublic class DialogHelper {\n\n    public static void showRationaleDialog(final PermissionUtils.OnRationaleListener.ShouldRequest shouldRequest, Activity activity) {\n        if (activity == null)\n            return;\n        new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Light_Dialog_Alert)\n                .setTitle(android.R.string.dialog_alert_title)\n                .setMessage(R.string.permission_denied_hint)\n                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int which) {\n                        shouldRequest.again(true);\n                    }\n                })\n                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int which) {\n                        shouldRequest.again(false);\n                    }\n                })\n                .setCancelable(false)\n                .create()\n                .show();\n    }\n\n    public static void showOpenAppSettingDialog(Activity activity) {\n        if (activity == null)\n            return;\n        new AlertDialog.Builder(activity, R.style.Theme_AppCompat_Light_Dialog_Alert)\n                .setTitle(android.R.string.dialog_alert_title)\n                .setMessage(R.string.permission_denied_forever_hint)\n                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int which) {\n                        PermissionUtils.launchAppDetailsSettings();\n                    }\n                })\n                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int which) {\n\n                    }\n                })\n                .setCancelable(false)\n                .create()\n                .show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wildma/mqttandroidclient/permission/PermissionConstants.java",
    "content": "package com.wildma.mqttandroidclient.permission;\n\nimport android.Manifest;\nimport android.Manifest.permission;\nimport android.annotation.SuppressLint;\nimport android.support.annotation.StringDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n\n/**\n * Desc\t        ${权限相关常量}\n */\n@SuppressLint(\"InlinedApi\")\npublic final class PermissionConstants {\n\n    public static final String CALENDAR   = Manifest.permission_group.CALENDAR;\n    public static final String CAMERA     = Manifest.permission_group.CAMERA;\n    public static final String CONTACTS   = Manifest.permission_group.CONTACTS;\n    public static final String LOCATION   = Manifest.permission_group.LOCATION;\n    public static final String MICROPHONE = Manifest.permission_group.MICROPHONE;\n    public static final String PHONE      = Manifest.permission_group.PHONE;\n    public static final String SENSORS    = Manifest.permission_group.SENSORS;\n    public static final String SMS        = Manifest.permission_group.SMS;\n    public static final String STORAGE    = Manifest.permission_group.STORAGE;\n\n    private static final String[] GROUP_CALENDAR   = {\n            permission.READ_CALENDAR, permission.WRITE_CALENDAR\n    };\n    private static final String[] GROUP_CAMERA     = {\n            permission.CAMERA\n    };\n    private static final String[] GROUP_CONTACTS   = {\n            permission.READ_CONTACTS, permission.WRITE_CONTACTS, permission.GET_ACCOUNTS\n    };\n    private static final String[] GROUP_LOCATION   = {\n            permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION\n    };\n    private static final String[] GROUP_MICROPHONE = {\n            permission.RECORD_AUDIO\n    };\n    private static final String[] GROUP_PHONE      = {\n            permission.READ_PHONE_STATE, permission.READ_PHONE_NUMBERS, permission.CALL_PHONE,\n            permission.ANSWER_PHONE_CALLS, permission.READ_CALL_LOG, permission.WRITE_CALL_LOG,\n            permission.ADD_VOICEMAIL, permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS\n    };\n    private static final String[] GROUP_SENSORS    = {\n            permission.BODY_SENSORS\n    };\n    private static final String[] GROUP_SMS        = {\n            permission.SEND_SMS, permission.RECEIVE_SMS, permission.READ_SMS,\n            permission.RECEIVE_WAP_PUSH, permission.RECEIVE_MMS,\n    };\n    private static final String[] GROUP_STORAGE    = {\n            permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE\n    };\n\n    @StringDef({CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Permission {\n    }\n\n    public static String[] getPermissions(@Permission final String permission) {\n        switch (permission) {\n            case CALENDAR:\n                return GROUP_CALENDAR;\n            case CAMERA:\n                return GROUP_CAMERA;\n            case CONTACTS:\n                return GROUP_CONTACTS;\n            case LOCATION:\n                return GROUP_LOCATION;\n            case MICROPHONE:\n                return GROUP_MICROPHONE;\n            case PHONE:\n                return GROUP_PHONE;\n            case SENSORS:\n                return GROUP_SENSORS;\n            case SMS:\n                return GROUP_SMS;\n            case STORAGE:\n                return GROUP_STORAGE;\n        }\n        return new String[]{permission};\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/wildma/mqttandroidclient/permission/PermissionUtils.java",
    "content": "package com.wildma.mqttandroidclient.permission;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.support.annotation.Nullable;\nimport android.support.annotation.RequiresApi;\nimport android.support.v4.content.ContextCompat;\nimport android.util.Log;\nimport android.view.MotionEvent;\nimport android.view.WindowManager;\n\nimport com.wildma.mqttandroidclient.MyApplication;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Desc\t        ${权限工具类}\n * 注意：需要在清单文件注册PermissionActivity\n */\npublic final class PermissionUtils {\n\n    private static final List<String> PERMISSIONS = getPermissions();\n\n    private static PermissionUtils sInstance;\n\n    private OnRationaleListener mOnRationaleListener;\n    private SimpleCallback      mSimpleCallback;\n    private FullCallback        mFullCallback;\n    private ThemeCallback       mThemeCallback;\n    private Set<String>         mPermissions;\n    private List<String>        mPermissionsRequest;\n    private List<String>        mPermissionsGranted;\n    private List<String>        mPermissionsDenied;\n    private List<String>        mPermissionsDeniedForever;\n\n    /**\n     * 获取应用权限\n     *\n     * @return 清单文件中的权限列表\n     */\n    public static List<String> getPermissions() {\n        return getPermissions(MyApplication.getContext().getPackageName());\n    }\n\n    /**\n     * 获取应用权限\n     *\n     * @param packageName 包名\n     * @return 清单文件中的权限列表\n     */\n    public static List<String> getPermissions(final String packageName) {\n        PackageManager pm = MyApplication.getContext().getPackageManager();\n        try {\n            return Arrays.asList(\n                    pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS)\n                            .requestedPermissions\n            );\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return Collections.emptyList();\n        }\n    }\n\n    /**\n     * 判断权限是否已授权\n     *\n     * @param permissions\n     * @return\n     */\n    public static boolean isGranted(final String... permissions) {\n        for (String permission : permissions) {\n            if (!isGranted(permission)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private static boolean isGranted(final String permission) {\n        return Build.VERSION.SDK_INT < Build.VERSION_CODES.M\n                || PackageManager.PERMISSION_GRANTED\n                == ContextCompat.checkSelfPermission(MyApplication.getContext(), permission);\n    }\n\n    /**\n     * 打开应用详情设置界面\n     */\n    public static void launchAppDetailsSettings() {\n        Intent intent = new Intent(\"android.settings.APPLICATION_DETAILS_SETTINGS\");\n        intent.setData(Uri.parse(\"package:\" + MyApplication.getContext().getPackageName()));\n        MyApplication.getContext().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));\n    }\n\n    /**\n     * 设置请求权限\n     *\n     * @param permissions 要请求的权限\n     * @return {@link PermissionUtils}\n     */\n    public static PermissionUtils permission(@PermissionConstants.Permission final String... permissions) {\n        return new PermissionUtils(permissions);\n    }\n\n    private PermissionUtils(final String... permissions) {\n        mPermissions = new LinkedHashSet<>();\n        for (String permission : permissions) {\n            for (String aPermission : PermissionConstants.getPermissions(permission)) {\n                if (PERMISSIONS.contains(aPermission)) {\n                    mPermissions.add(aPermission);\n                }\n            }\n        }\n        sInstance = this;\n    }\n\n    /**\n     * Set rationale listener.\n     *\n     * @param listener The rationale listener.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils rationale(final OnRationaleListener listener) {\n        mOnRationaleListener = listener;\n        return this;\n    }\n\n    /**\n     * Set the simple call back.\n     *\n     * @param callback the simple call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final SimpleCallback callback) {\n        mSimpleCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the full call back.\n     *\n     * @param callback the full call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final FullCallback callback) {\n        mFullCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the theme callback.\n     *\n     * @param callback The theme callback.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils theme(final ThemeCallback callback) {\n        mThemeCallback = callback;\n        return this;\n    }\n\n    /**\n     * 开始请求\n     */\n    public void request() {\n        mPermissionsGranted = new ArrayList<>();\n        mPermissionsRequest = new ArrayList<>();\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            mPermissionsGranted.addAll(mPermissions);\n            requestCallback();\n        } else {\n            for (String permission : mPermissions) {\n                if (isGranted(permission)) {\n                    mPermissionsGranted.add(permission);\n                } else {\n                    mPermissionsRequest.add(permission);\n                }\n            }\n            if (mPermissionsRequest.isEmpty()) {\n                requestCallback();\n            } else {\n                startPermissionActivity();\n            }\n        }\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private void startPermissionActivity() {\n        mPermissionsDenied = new ArrayList<>();\n        mPermissionsDeniedForever = new ArrayList<>();\n        PermissionActivity.start(MyApplication.getContext());\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private boolean rationale(final Activity activity) {\n        boolean isRationale = false;\n        if (mOnRationaleListener != null) {\n            for (String permission : mPermissionsRequest) {\n                if (activity.shouldShowRequestPermissionRationale(permission)) {\n                    getPermissionsStatus(activity);\n                    mOnRationaleListener.rationale(new OnRationaleListener.ShouldRequest() {\n                        @Override\n                        public void again(boolean again) {\n                            if (again) {\n                                startPermissionActivity();\n                            } else {\n                                requestCallback();\n                            }\n                        }\n                    });\n                    isRationale = true;\n                    break;\n                }\n            }\n            mOnRationaleListener = null;\n        }\n        return isRationale;\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private void getPermissionsStatus(final Activity activity) {\n        for (String permission : mPermissionsRequest) {\n            if (isGranted(permission)) {\n                mPermissionsGranted.add(permission);\n            } else {\n                mPermissionsDenied.add(permission);\n                if (!activity.shouldShowRequestPermissionRationale(permission)) {\n                    mPermissionsDeniedForever.add(permission);\n                }\n            }\n        }\n    }\n\n    private void requestCallback() {\n        if (mSimpleCallback != null) {\n            if (mPermissionsRequest.size() == 0\n                    || mPermissions.size() == mPermissionsGranted.size()) {\n                mSimpleCallback.onGranted();\n            } else {\n                if (!mPermissionsDenied.isEmpty()) {\n                    mSimpleCallback.onDenied();\n                }\n            }\n            mSimpleCallback = null;\n        }\n        if (mFullCallback != null) {\n            if (mPermissionsRequest.size() == 0\n                    || mPermissions.size() == mPermissionsGranted.size()) {\n                mFullCallback.onGranted(mPermissionsGranted);\n            } else {\n                if (!mPermissionsDenied.isEmpty()) {\n                    mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied);\n                }\n            }\n            mFullCallback = null;\n        }\n        mOnRationaleListener = null;\n        mThemeCallback = null;\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private void onRequestPermissionsResult(final Activity activity) {\n        getPermissionsStatus(activity);\n        requestCallback();\n    }\n\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static class PermissionActivity extends Activity {\n\n        public static void start(final Context context) {\n            Intent starter = new Intent(context, PermissionActivity.class);\n            starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            context.startActivity(starter);\n        }\n\n        @Override\n        protected void onCreate(@Nullable Bundle savedInstanceState) {\n            getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE\n                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);\n            if (sInstance == null) {\n                super.onCreate(savedInstanceState);\n                Log.e(\"PermissionUtils\", \"request permissions failed\");\n                finish();\n                return;\n            }\n            if (sInstance.mThemeCallback != null) {\n                sInstance.mThemeCallback.onActivityCreate(this);\n            }\n            super.onCreate(savedInstanceState);\n\n            if (sInstance.rationale(this)) {\n                finish();\n                return;\n            }\n            if (sInstance.mPermissionsRequest != null) {\n                int size = sInstance.mPermissionsRequest.size();\n                if (size <= 0) {\n                    finish();\n                    return;\n                }\n                requestPermissions(sInstance.mPermissionsRequest.toArray(new String[size]), 1);\n            }\n        }\n\n        @Override\n        public void onRequestPermissionsResult(int requestCode,\n                                               @NonNull String[] permissions,\n                                               @NonNull int[] grantResults) {\n            sInstance.onRequestPermissionsResult(this);\n            finish();\n        }\n\n        @Override\n        public boolean dispatchTouchEvent(MotionEvent ev) {\n            finish();\n            return true;\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    /*设置拒绝权限后再次请求的回调接口*/\n    public interface OnRationaleListener {\n\n        void rationale(ShouldRequest shouldRequest);\n\n        interface ShouldRequest {\n            void again(boolean again);\n        }\n    }\n\n    /*是否授权回调接口*/\n    public interface SimpleCallback {\n        void onGranted();\n\n        void onDenied();\n    }\n\n    /*是否授权回调接口*/\n    public interface FullCallback {\n        void onGranted(List<String> permissionsGranted);\n\n        void onDenied(List<String> permissionsDeniedForever, List<String> permissionsDenied);\n    }\n\n    /*主题回调接口*/\n    public interface ThemeCallback {\n        void onActivityCreate(Activity activity);\n    }\n}"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center\">\n\n    <Button\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:onClick=\"publish\"\n        android:text=\"publish\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">MqttAndroidClient</string>\n    <string name=\"permission_denied_hint\">我们需要一些您已经拒绝的权限，请同意授权，否则功能无法正常使用！</string>\n    <string name=\"permission_denied_forever_hint\">我们需要一些您已经拒绝的权限，请到设置页面手动授权，否则功能无法正常使用！</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <!--透明activity样式-->\n    <style name=\"ActivityTransparent\">\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowAnimationStyle\">@android:style/Animation</item>\n        <!--android:statusBarColor requires API level 21-->\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "app/src/test/java/com/wildma/mqttandroidclient/ExampleUnitTest.java",
    "content": "package com.wildma.mqttandroidclient;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.3.3'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Thu Nov 08 18:05:42 CST 2018\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-3.3-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]