[
  {
    "path": ".gitignore",
    "content": "\n.gradle/\n.idea/\n*.iml\nbuild\nimport-summary.txt\nlocal.properties\n*.apk\n"
  },
  {
    "path": "README.md",
    "content": "# NoExclamation\n叹号杀手-去除安卓手机在国内时网络图标上的感叹号\n\nhttps://www.noisyfox.cn/android-captive-portal.html\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 28\n\n    defaultConfig {\n        applicationId \"org.foxteam.noisyfox.noexclamation\"\n        minSdkVersion 21\n        targetSdkVersion 28\n        resConfigs \"zh\", \"en\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"org.foxteam.noisyfox.noexclamation\"\n          android:versionCode=\"8\"\n          android:versionName=\"@string/app_version_name\">\n\n    <application\n        android:label=\"@string/app_name\"\n        android:name=\"App\"\n        android:icon=\"@drawable/ic_launcher\"\n        android:theme=\"@android:style/Theme.Material.Light.DarkActionBar\"\n        android:allowBackup=\"true\">\n        <activity\n            android:name=\".NewActivity\"\n            android:label=\"@string/app_name\"\n            android:configChanges=\"orientation|screenSize\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/App.java",
    "content": "package org.foxteam.noisyfox.noexclamation;\n\nimport android.app.Application;\n\n//This class allows global method for StringRes for translation\n\npublic class App extends Application {\n\n    private static App app; //ApplicationContext reference (safe, no context leak)\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        app = this;\n    }\n\n    public static String getStr(int resId) {\n        return app.getString(resId);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/NewActivity.java",
    "content": "package org.foxteam.noisyfox.noexclamation;\n\nimport android.app.AlertDialog;\nimport android.app.ProgressDialog;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.preference.Preference;\nimport android.preference.PreferenceActivity;\nimport android.preference.PreferenceCategory;\nimport android.widget.Toast;\n\nimport org.foxteam.noisyfox.noexclamation.adapter.ConfigAdapterFactory;\nimport org.foxteam.noisyfox.noexclamation.adapter.IConfigProvider;\nimport org.foxteam.noisyfox.noexclamation.adapter.ITaskExecutor;\n\nimport java.util.concurrent.locks.ReentrantLock;\n\nimport static org.foxteam.noisyfox.noexclamation.App.getStr;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class NewActivity extends PreferenceActivity implements ITaskExecutor {\n\n    private IConfigProvider provider;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        addPreferencesFromResource(R.xml.main_pref);\n\n        PreferenceCategory category = (PreferenceCategory) findPreference(\"cat_detail_settings\");\n\n        provider = ConfigAdapterFactory.createProvider(this, this);\n        provider.buildSettingsSection(category);\n\n        findPreference(\"refresh\").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {\n            @Override\n            public boolean onPreferenceClick(Preference preference) {\n                provider.refreshStatus();\n                return true;\n            }\n        });\n        findPreference(\"set_noisyfox\").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {\n            @Override\n            public boolean onPreferenceClick(Preference preference) {\n                askForConfirm(getStr(R.string.setting_toNoisyFox), new Runnable() {\n                    @Override\n                    public void run() {\n                        provider.setToNoisyfox();\n                    }\n                });\n                return true;\n            }\n        });\n        findPreference(\"set_google\").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {\n            @Override\n            public boolean onPreferenceClick(Preference preference) {\n                askForConfirm(getStr(R.string.setting_toGoogle), new Runnable() {\n                    @Override\n                    public void run() {\n                        provider.resetToGoogle();\n                    }\n                });\n                return true;\n            }\n        });\n\n        runTask(getString(R.string.runTask_getRoot), new TaskRunnable() {\n            @Override\n            public Bundle run() {\n                String result = Utils.cmdExecSu(\"id\");\n                if (result == null || !result.contains(\"uid=0\")) {\n                    return new Bundle();\n                }\n                return null;\n            }\n        }, new AfterTaskRunnable() {\n            @Override\n            public void run(Bundle result) {\n                if (result != null) {\n                    Toast.makeText(NewActivity.this, getStr(R.string.toast_needsRootMsg), Toast.LENGTH_LONG).show();\n                }\n                provider.refreshStatus();\n            }\n        });\n    }\n\n    private final ReentrantLock mSyncRoot = new ReentrantLock();\n    private int mTaskCount = 0;\n    private ProgressDialog mTaskDialog = null;\n\n    public void runTask(String msg, final TaskRunnable task, final AfterTaskRunnable after) {\n        mSyncRoot.lock();\n        try {\n            mTaskCount++;\n            if (mTaskDialog == null) {\n                mTaskDialog = new ProgressDialog(this);\n                mTaskDialog.setCancelable(false);\n                mTaskDialog.setIndeterminate(true);\n            }\n            mTaskDialog.setMessage(msg);\n            mTaskDialog.show();\n        } finally {\n            mSyncRoot.unlock();\n        }\n\n        Thread t = new Thread() {\n            @Override\n            public void run() {\n                Bundle result = null;\n                try {\n                    result = task.run();\n                } finally {\n                    final Bundle b = result;\n                    runOnUiThread(new Runnable() {\n                        @Override\n                        public void run() {\n                            after.run(b);\n                            mSyncRoot.lock();\n                            try {\n                                mTaskCount--;\n                                if (mTaskCount == 0) {\n                                    mTaskDialog.dismiss();\n                                }\n                            } finally {\n                                mSyncRoot.unlock();\n                            }\n                        }\n                    });\n                }\n            }\n        };\n        t.start();\n    }\n\n    private void askForConfirm(String msg, final Runnable doStuff) {\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setMessage(msg);\n        builder.setPositiveButton(getStr(R.string.dialogBtn_Confirm), new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                doStuff.run();\n            }\n        });\n        builder.setNegativeButton(getStr(R.string.dialogBtn_Cancel), new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n            }\n        });\n        builder.show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/Utils.java",
    "content": "package org.foxteam.noisyfox.noexclamation;\n\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStreamWriter;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class Utils {\n\n    public static String cmdExecSu(String cmd) {\n        Process p = null;\n        BufferedReader reader = null;\n        BufferedWriter writer = null;\n        try {\n            p = new ProcessBuilder(\"su\").start();\n            writer = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));\n            InputStream is = p.getInputStream();\n            reader = new BufferedReader(new InputStreamReader(is));\n            writer.write(cmd);\n            writer.write(\"\\n\");\n            writer.write(\"exit\\n\");\n            writer.flush();\n            StringBuilder sb = new StringBuilder();\n            String line;\n            while ((line = reader.readLine()) != null) {\n                sb.append(line);\n            }\n            p.waitFor();\n            return sb.toString();\n        } catch (IOException e) {\n            e.printStackTrace();\n        } catch (InterruptedException e) {\n            e.printStackTrace();\n        } finally {\n            if (reader != null) {\n                try {\n                    reader.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n            if (writer != null) {\n                try {\n                    writer.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n            if (p != null) {\n                p.destroy();\n            }\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/CommonConfigProvider.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.preference.PreferenceCategory;\n\nimport org.foxteam.noisyfox.noexclamation.R;\nimport org.foxteam.noisyfox.noexclamation.adapter.settings.ISettingsItem;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport static org.foxteam.noisyfox.noexclamation.App.getStr;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class CommonConfigProvider implements IConfigProvider, ISettingsItem.OnSettingsChangedListener {\n\n    private final List<ISettingsItem> mAllSettings = new ArrayList<>();\n    private final Context mContext;\n    private final ITaskExecutor mTaskExecutor;\n\n    public CommonConfigProvider(Context context,\n                                ITaskExecutor taskExecutor) {\n        mContext = context;\n        mTaskExecutor = taskExecutor;\n    }\n\n    public void addSettings(ISettingsItem settingsItem) {\n        settingsItem.injectTaskExecutor(mTaskExecutor);\n        settingsItem.setOnSettingsChangedListener(this);\n        mAllSettings.add(settingsItem);\n    }\n\n    @Override\n    public void buildSettingsSection(PreferenceCategory category) {\n        for (ISettingsItem s : mAllSettings) {\n            category.addPreference(s.onCreatePreference(mContext));\n        }\n    }\n\n    @Override\n    public void refreshStatus() {\n        mTaskExecutor.runTask(getStr(R.string.status_refresh), new ITaskExecutor.TaskRunnable() {\n            @Override\n            public Bundle run() {\n                Bundle result = new Bundle();\n                for (ISettingsItem s : mAllSettings) {\n                    s.reloadValue(result);\n                }\n                return result;\n            }\n        }, new ITaskExecutor.AfterTaskRunnable() {\n            @Override\n            public void run(Bundle result) {\n                for (ISettingsItem s : mAllSettings) {\n                    s.refreshPreference(result);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void resetToGoogle() {\n        mTaskExecutor.runTask(getStr(R.string.status_reset), new ITaskExecutor.TaskRunnable() {\n            @Override\n            public Bundle run() {\n                for (ISettingsItem s : mAllSettings) {\n                    s.resetToGoogle();\n                }\n                return null;\n            }\n        }, new ITaskExecutor.AfterTaskRunnable() {\n            @Override\n            public void run(Bundle result) {\n                refreshStatus();\n            }\n        });\n    }\n\n    @Override\n    public void setToNoisyfox() {\n        mTaskExecutor.runTask(getStr(R.string.status_setNoisyFox), new ITaskExecutor.TaskRunnable() {\n            @Override\n            public Bundle run() {\n                for (ISettingsItem s : mAllSettings) {\n                    s.setToNoisyfox();\n                }\n                return null;\n            }\n        }, new ITaskExecutor.AfterTaskRunnable() {\n            @Override\n            public void run(Bundle result) {\n                refreshStatus();\n            }\n        });\n    }\n\n    @Override\n    public void onSettingsChanged(ISettingsItem settingsItem) {\n        refreshStatus();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/ConfigAdapterFactory.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter;\n\nimport android.content.Context;\nimport android.provider.Settings;\n\nimport org.foxteam.noisyfox.noexclamation.R;\nimport org.foxteam.noisyfox.noexclamation.adapter.settings.ListSettings;\nimport org.foxteam.noisyfox.noexclamation.adapter.settings.TextSettings;\nimport org.foxteam.noisyfox.noexclamation.adapter.settings.ToggleSettings;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\n\nimport static org.foxteam.noisyfox.noexclamation.App.getStr;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class ConfigAdapterFactory {\n    public static IConfigProvider createProvider(Context context,\n                                                 ITaskExecutor taskExecutor) {\n        CommonConfigProvider provider = new CommonConfigProvider(context, taskExecutor);\n\n        // Check for android 7.1.2 which use 'captive_portal_mode' instead of 'captive_portal_detection_enabled'\n        if (isSettingsPresent(\"captive_portal_mode\")) {\n            provider.addSettings(new ListSettings(getStr(R.string.portalModeTitle),\n                    \"captive_portal_mode\",\n                    new String[]{\"0\", \"1\", \"2\"},\n                    new String[]{getStr(R.string.portalMode_disable), getStr(R.string.portalMode_promptOnly), getStr(R.string.portalMode_ignoreLoginPrompts)},\n                    \"1\"));\n        } else {\n            provider.addSettings(new ToggleSettings(getStr(R.string.portalDetectionTitle),\n                    \"captive_portal_detection_enabled\", \"1\", \"0\", ToggleSettings.State.Positive, \"On\", \"Off\", getStr(R.string.progress_open), getStr(R.string.progress_close)));\n        }\n\n        // For android 7.0+\n        if (isSettingsPresent(\"captive_portal_use_https\")) {\n            provider.addSettings(new ToggleSettings(getStr(R.string.setting_useHttps),\n                    \"captive_portal_use_https\", \"1\", \"0\", ToggleSettings.State.Positive, \"On\", \"Off\", getStr(R.string.progress_open), getStr(R.string.progress_close)));\n        }\n\n        boolean a711 = false;\n        if (isSettingsPresent(\"captive_portal_http_url\")) {\n            a711 = true;\n            provider.addSettings(new TextSettings(getStr(R.string.protocol_http), \"captive_portal_http_url\", \"http://www.noisyfox.io/generate_204\"));\n        }\n        if (isSettingsPresent(\"captive_portal_https_url\")) {\n            a711 = true;\n            provider.addSettings(new TextSettings(getStr(R.string.protocol_https), \"captive_portal_https_url\", \"https://www.noisyfox.io/generate_204\"));\n        }\n        if (!a711) {\n            provider.addSettings(new TextSettings(getStr(R.string.protocol_default), \"captive_portal_server\", \"noisyfox.io\"));\n        }\n\n        return provider;\n    }\n\n    private static boolean isSettingsPresent(String key) {\n        Class clazz = Settings.Global.class;\n\n        for (Field field : clazz.getDeclaredFields()) {\n            if (field.getType().equals(String.class) && Modifier.isStatic(field.getModifiers())) {\n                try {\n                    String v = (String) field.get(null);\n                    if (key.equals(v)) {\n                        return true;\n                    }\n                } catch (IllegalAccessException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/IConfigProvider.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter;\n\nimport android.preference.PreferenceCategory;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic interface IConfigProvider {\n\n    void buildSettingsSection(PreferenceCategory category);\n\n    void refreshStatus();\n\n    void resetToGoogle();\n\n    void setToNoisyfox();\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/ITaskExecutor.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter;\n\nimport android.os.Bundle;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic interface ITaskExecutor {\n    void runTask(String msg, TaskRunnable task, AfterTaskRunnable after);\n\n    interface AfterTaskRunnable {\n        void run(Bundle result);\n    }\n\n    interface TaskRunnable {\n        Bundle run();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/settings/BaseSettingsItem.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter.settings;\n\nimport org.foxteam.noisyfox.noexclamation.adapter.ITaskExecutor;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic abstract class BaseSettingsItem implements ISettingsItem {\n    private OnSettingsChangedListener listener;\n    protected ITaskExecutor executor;\n\n    @Override\n    public void setOnSettingsChangedListener(OnSettingsChangedListener listener) {\n        this.listener = listener;\n    }\n\n    @Override\n    public void setToNoisyfox() {\n    }\n\n    @Override\n    public void resetToGoogle() {\n    }\n\n    @Override\n    public void injectTaskExecutor(ITaskExecutor executor) {\n        this.executor = executor;\n    }\n\n    protected void notifySettingsChanged() {\n        if (listener != null) {\n            listener.onSettingsChanged(this);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/settings/GlobalKeyedSettings.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter.settings;\n\nimport android.os.Bundle;\n\nimport org.foxteam.noisyfox.noexclamation.Utils;\nimport org.foxteam.noisyfox.noexclamation.adapter.ITaskExecutor;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic abstract class GlobalKeyedSettings extends BaseSettingsItem {\n    protected final String mKey;\n\n    protected GlobalKeyedSettings(String key) {\n        mKey = key;\n    }\n\n    @Override\n    public void reloadValue(Bundle valueOut) {\n        valueOut.putString(mKey, Utils.cmdExecSu(\"settings get global \" + mKey));\n    }\n\n    protected String getValue(Bundle value) {\n        String v = null;\n        if (value != null) {\n            v = value.getString(mKey);\n        }\n\n        return v;\n    }\n\n    protected void setValue(String value) {\n        Utils.cmdExecSu(String.format(\"settings put global %s %s\", mKey, value));\n    }\n\n    protected void removeValue() {\n        Utils.cmdExecSu(\"settings delete global \" + mKey);\n    }\n\n    protected void setValueAsync(String msg, final String value) {\n        executor.runTask(msg, new ITaskExecutor.TaskRunnable() {\n            @Override\n            public Bundle run() {\n                setValue(value);\n                return null;\n            }\n        }, new ITaskExecutor.AfterTaskRunnable() {\n            @Override\n            public void run(Bundle result) {\n                notifySettingsChanged();\n            }\n        });\n    }\n\n    protected void removeValueAsync(String msg) {\n        executor.runTask(msg, new ITaskExecutor.TaskRunnable() {\n            @Override\n            public Bundle run() {\n                removeValue();\n                return null;\n            }\n        }, new ITaskExecutor.AfterTaskRunnable() {\n            @Override\n            public void run(Bundle result) {\n                notifySettingsChanged();\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/settings/ISettingsItem.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter.settings;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.preference.Preference;\n\nimport org.foxteam.noisyfox.noexclamation.adapter.ITaskExecutor;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic interface ISettingsItem {\n    Preference onCreatePreference(Context context);\n\n    void reloadValue(Bundle valueOut);\n\n    void refreshPreference(Bundle value);\n\n    void setToNoisyfox();\n\n    void resetToGoogle();\n\n    void setOnSettingsChangedListener(OnSettingsChangedListener listener);\n\n    void injectTaskExecutor(ITaskExecutor executor);\n\n    interface OnSettingsChangedListener {\n        void onSettingsChanged(ISettingsItem settingsItem);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/settings/ListSettings.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter.settings;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.preference.ListPreference;\nimport android.preference.Preference;\n\nimport org.foxteam.noisyfox.noexclamation.R;\n\nimport static org.foxteam.noisyfox.noexclamation.App.getStr;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class ListSettings extends GlobalKeyedSettings implements Preference.OnPreferenceChangeListener {\n    private ListPreference mPreference;\n    private final String mTitle;\n    private final String[] mValues;\n    private final String[] mDisplayValues;\n    private final int defaultValueIndex;\n\n    public ListSettings(String title, String key, String values[], String displayValues[], String defaultValue) {\n        super(key);\n        mTitle = title;\n        mValues = values;\n        mDisplayValues = displayValues;\n\n        int d = -1;\n        for (int i = 0; i < values.length; i++) {\n            if (defaultValue.equals(values[i])) {\n                d = i;\n                break;\n            }\n        }\n        if (d == -1) {\n            throw new RuntimeException(\"default value not in valid values!\");\n        }\n        defaultValueIndex = d;\n    }\n\n    @Override\n    public Preference onCreatePreference(Context context) {\n        mPreference = new ListPreference(context);\n        mPreference.setEntryValues(mValues);\n        mPreference.setEntries(mDisplayValues);\n        mPreference.setTitle(mTitle);\n\n        refreshPreference(null);\n\n        return mPreference;\n    }\n\n    @Override\n    public void refreshPreference(Bundle value) {\n        mPreference.setOnPreferenceChangeListener(null);\n\n        String v = getValue(value);\n        if (v == null || \"null\".equals(v)) {\n            mPreference.setValueIndex(defaultValueIndex);\n            mPreference.setSummary(v == null ? getStr(R.string.status_unknown) : mDisplayValues[defaultValueIndex]);\n        } else {\n            mPreference.setValue(v);\n            mPreference.setSummary(mPreference.getEntry());\n        }\n\n        mPreference.setOnPreferenceChangeListener(this);\n    }\n\n    @Override\n    public boolean onPreferenceChange(Preference preference, Object newValue) {\n        setValueAsync(\"设置中\", (String) newValue);\n        return false;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/settings/TextSettings.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter.settings;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.preference.EditTextPreference;\nimport android.preference.Preference;\n\nimport org.foxteam.noisyfox.noexclamation.R;\n\nimport static org.foxteam.noisyfox.noexclamation.App.getStr;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class TextSettings extends GlobalKeyedSettings implements Preference.OnPreferenceChangeListener {\n    private EditTextPreference mPreference;\n    private final String mTitle;\n    private final String mDefaultValue;\n\n    public TextSettings(String title, String key, String defaultValue) {\n        super(key);\n        mTitle = title;\n        mDefaultValue = defaultValue;\n    }\n\n    public String getDefaultValue() {\n        return mDefaultValue;\n    }\n\n    @Override\n    public Preference onCreatePreference(Context context) {\n        mPreference = new EditTextPreference(context);\n        mPreference.setTitle(mTitle);\n        mPreference.setDialogTitle(mTitle);\n\n        refreshPreference(null);\n\n        return mPreference;\n    }\n\n    @Override\n    public void refreshPreference(Bundle value) {\n        mPreference.setOnPreferenceChangeListener(null);\n\n        String v = getValue(value);\n        if (v == null || \"null\".equals(v)) {\n            mPreference.setSummary(getStr(R.string.value_isDefault));\n            mPreference.setText(mDefaultValue);\n        } else {\n            mPreference.setSummary(v);\n            mPreference.setText(v);\n        }\n\n        mPreference.setOnPreferenceChangeListener(this);\n    }\n\n    @Override\n    public boolean onPreferenceChange(Preference preference, Object newValue) {\n        setValueAsync(\"设置中\", (String) newValue);\n        return false;\n    }\n\n    @Override\n    public void setToNoisyfox() {\n        setValue(getDefaultValue());\n    }\n\n    @Override\n    public void resetToGoogle() {\n        removeValue();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/foxteam/noisyfox/noexclamation/adapter/settings/ToggleSettings.java",
    "content": "package org.foxteam.noisyfox.noexclamation.adapter.settings;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.preference.Preference;\nimport android.preference.SwitchPreference;\n\nimport org.foxteam.noisyfox.noexclamation.R;\n\nimport static org.foxteam.noisyfox.noexclamation.App.getStr;\n\n/**\n * Created by Noisyfox on 2017/7/1.\n */\n\npublic class ToggleSettings extends GlobalKeyedSettings implements Preference.OnPreferenceChangeListener {\n\n    private SwitchPreference mPreference;\n    private final String mTitle;\n    private final String mPositiveValue;\n    private final String mNegativeValue;\n    private final State mStateIfNotSet;\n    private final String mPositiveSummary;\n    private final String mNegativeSummary;\n    private final String mPositiveProgress;\n    private final String mNegativeProgress;\n\n    public ToggleSettings(String title, String key, String valuePositive, String valueNegative, State stateIfNotSet,\n                          String summaryPositive, String summaryNegative,\n                          String progressPositive, String progressNegative) {\n        super(key);\n        mTitle = title;\n        mPositiveValue = valuePositive;\n        mNegativeValue = valueNegative;\n        mStateIfNotSet = stateIfNotSet;\n        mPositiveSummary = summaryPositive;\n        mNegativeSummary = summaryNegative;\n        mPositiveProgress = progressPositive;\n        mNegativeProgress = progressNegative;\n    }\n\n    @Override\n    public Preference onCreatePreference(Context context) {\n        mPreference = new SwitchPreference(context);\n\n        mPreference.setTitle(mTitle);\n\n        refreshPreference(null);\n\n        return mPreference;\n    }\n\n    public State getState(String currentValue) {\n        if (currentValue != null) {\n            if (currentValue.equals(\"null\")) {\n                return mStateIfNotSet;\n            } else if (currentValue.equals(mNegativeValue)) {\n                return State.Negative;\n            } else if (currentValue.equals(mPositiveValue)) {\n                return State.Positive;\n            }\n        }\n        return State.Unknown;\n    }\n\n    @Override\n    public boolean onPreferenceChange(Preference preference, Object newValue) {\n        boolean checked = (boolean) newValue;\n        setValueAsync(checked ? mPositiveProgress : mNegativeProgress, checked ? mPositiveValue : mNegativeValue);\n        return false;\n    }\n\n    public enum State {\n        Positive, Negative, Unknown\n    }\n\n    @Override\n    public void refreshPreference(Bundle value) {\n        mPreference.setOnPreferenceChangeListener(null);\n        String v = getValue(value);\n\n        State s = getState(v);\n        switch (s) {\n            case Positive:\n                mPreference.setSummary(mPositiveSummary);\n                mPreference.setChecked(true);\n                break;\n            case Negative:\n                mPreference.setSummary(mNegativeSummary);\n                mPreference.setChecked(false);\n                break;\n            case Unknown:\n                mPreference.setSummary(getStr(R.string.status_unknown));\n                mPreference.setChecked(false);\n                break;\n        }\n        mPreference.setOnPreferenceChangeListener(this);\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">叹号杀手</string>\n    <string name=\"app_version_name\" translatable=\"false\">3.0</string>\n\n    <string name=\"setting_toNoisyFox\">即将把服务器地址设置为 noisyfox.io，是否继续？</string>\n    <string name=\"setting_toGoogle\">即将把服务器地址重置为默认值，是否继续？</string>\n    <string name=\"runTask_getRoot\">获取root权限</string>\n\n    <string name=\"toast_needsRootMsg\">无法获得root权限！程序将可能无法正常使用！</string>\n    <string name=\"dialogBtn_Confirm\">继续</string>\n    <string name=\"dialogBtn_Cancel\">取消</string>\n\n    <string name=\"portalModeTitle\">Captive Portal 检测模式</string>\n    <string name=\"portalMode_disable\">完全禁用检测</string>\n    <string name=\"portalMode_promptOnly\">检测到需要登录则弹框提醒</string>\n    <string name=\"portalMode_ignoreLoginPrompts\">检测到需要登录则自动断开此网络并不再自动连接</string>\n\n    <string name=\"portalDetectionTitle\">Captive Portal 检测</string>\n    <string name=\"progress_open\">开启中</string>\n    <string name=\"progress_close\">关闭中</string>\n\n    <string name=\"protocol_http\">服务地址(http)</string>\n    <string name=\"protocol_https\">服务地址(https)</string>\n    <string name=\"protocol_default\">服务地址</string>\n\n    <string name=\"status_refresh\">刷新当前状态</string>\n    <string name=\"status_reset\">重置为默认值</string>\n    <string name=\"status_setNoisyFox\">设置为 noisyfox.io</string>\n\n    <string name=\"version_prefix\">版本</string>\n\n    <string name=\"prefSummary_visitBlog\">欢迎访问我的博客来支持我的作品！ 猛戳这里 一noisyfox.io</string>\n    <string name=\"prefTitle_visitBlog\">求关注！求打赏！</string>\n    <string name=\"prefCategory_about\">关于</string>\n\n    <string name=\"prefSummary_visitGithub\">猛戳这里 一github.com/Noisyfox/NoExclamation</string>\n    <string name=\"prefTitle_openSrcGithub\">开源啦！</string>\n    <string name=\"prefCategory_detailedSettings\">详细设置</string>\n\n    <string name=\"prefTitle_resetToDefault\">重置为默认值</string>\n    <string name=\"prefSummary_resetToDefault\">将 portal server 重置为系统默认设置。</string>\n    <string name=\"prefTitle_setToNoisyfox\">设为 noisyfox.io</string>\n    <string name=\"prefSummary_setToNoisyfox\">将 portal server 设置为 noisyfox.io。</string>\n\n    <string name=\"prefCategory_portalSetup\">Portal Server 一键设置</string>\n    <string name=\"prefTitle_refreshStatus\">刷新当前状态</string>\n\n    <string name=\"prefCategory_Instructions\">使用说明</string>\n    <string name=\"prefSummary_Instructions\">最简单的去掉网络连接的感叹号的方式： 戳 设为 noisyfox.io 选项，然后开飞行模式，然后再关飞行模式，搞定！</string>\n    <string name=\"status_unknown\">未知</string>\n    <string name=\"value_isDefault\">默认</string>\n    <string name=\"setting_useHttps\">启用 https</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-en/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!--\n    English Translation\n    by Bryan Walsh\n    (https://github.com/bwalsh0)\n    -->\n\n    <string name=\"app_name\">NoExclamation</string>\n\n    <string name=\"setting_toGoogle\">Reset to system default?</string>\n    <string name=\"runTask_getRoot\">Grant root permission</string>\n\n    <string name=\"toast_needsRootMsg\">Unable to access root priveliges! This application may not work as intended.</string>\n    <string name=\"dialogBtn_Confirm\">Confirm</string>\n    <string name=\"dialogBtn_Cancel\">Cancel</string>\n\n    <string name=\"portalModeTitle\">Captive Portal Detection Mode</string>\n    <string name=\"portalMode_disable\">Disable</string>\n    <string name=\"portalMode_promptOnly\">Prompt for login</string>\n\n    <string name=\"portalDetectionTitle\">Captive Portal Detection</string>\n    <string name=\"progress_open\">Opening</string>\n    <string name=\"progress_close\">Closing</string>\n\n    <string name=\"protocol_http\">HTTP URL</string>\n    <string name=\"protocol_https\">HTTPS URL</string>\n    <string name=\"protocol_default\">Default Protocol</string>\n\n    <string name=\"status_refresh\">Refreshing status</string>\n    <string name=\"status_reset\">Resetting to default</string>\n    <string name=\"status_setNoisyFox\">Set to noisyfox.io</string>\n\n    <string name=\"version_prefix\">Version</string>\n\n    <string name=\"prefSummary_visitBlog\">Visit my blog to support my work! Visit 一noisyfox.io</string>\n    <string name=\"prefTitle_visitBlog\">Check out the blog!</string>\n    <string name=\"prefCategory_about\">About</string>\n\n    <string name=\"prefSummary_visitGithub\">Visit 一github.com/Noisyfox/NoExclamation</string>\n    <string name=\"prefTitle_openSrcGithub\">Open Source!</string>\n    <string name=\"prefCategory_detailedSettings\">Advanced Settings</string>\n\n    <string name=\"prefTitle_resetToDefault\">Reset to default</string>\n    <string name=\"prefSummary_resetToDefault\">Reset portal server to system default</string>\n    <string name=\"prefTitle_setToNoisyfox\">Set to noisyfox.io</string>\n    <string name=\"prefSummary_setToNoisyfox\">Set portal server to noisyfox.io</string>\n\n    <string name=\"prefCategory_portalSetup\">One-Click Setup</string>\n    <string name=\"prefTitle_refreshStatus\">Refresh Status</string>\n\n    <string name=\"prefCategory_Instructions\">Instructions</string>\n    <string name=\"prefSummary_Instructions\">This is the easiest way to get rid of the exclamation mark (error mode) of a Wi-Fi connection. Tap \\\"Set to noisyfox.io\\\", turn on Airplane mode, turn it back off, and it will be fixed!</string>\n    <string name=\"portalMode_ignoreLoginPrompts\">Ignore networks with login prompts</string>\n    <string name=\"setting_toNoisyFox\">Set server to noisyfox.io?</string>\n    <string name=\"status_unknown\">Unknown</string>\n    <string name=\"setting_useHttps\">Use HTTPS</string>\n    <string name=\"value_isDefault\">Default</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-zh/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">叹号杀手</string>\n\n    <string name=\"setting_toNoisyFox\">即将把服务器地址设置为 noisyfox.io，是否继续？</string>\n    <string name=\"setting_toGoogle\">即将把服务器地址重置为默认值，是否继续？</string>\n    <string name=\"runTask_getRoot\">获取root权限</string>\n\n    <string name=\"toast_needsRootMsg\">无法获得root权限！程序将可能无法正常使用！</string>\n    <string name=\"dialogBtn_Confirm\">继续</string>\n    <string name=\"dialogBtn_Cancel\">取消</string>\n\n    <string name=\"portalModeTitle\">Captive Portal 检测模式</string>\n    <string name=\"portalMode_disable\">完全禁用检测</string>\n    <string name=\"portalMode_promptOnly\">检测到需要登录则弹框提醒</string>\n    <string name=\"portalMode_ignoreLoginPrompts\">检测到需要登录则自动断开此网络并不再自动连接</string>\n\n    <string name=\"portalDetectionTitle\">Captive Portal 检测</string>\n    <string name=\"progress_open\">开启中</string>\n    <string name=\"progress_close\">关闭中</string>\n\n    <string name=\"protocol_http\">服务地址(http)</string>\n    <string name=\"protocol_https\">服务地址(https)</string>\n    <string name=\"protocol_default\">服务地址</string>\n\n    <string name=\"status_refresh\">刷新当前状态</string>\n    <string name=\"status_reset\">重置为默认值</string>\n    <string name=\"status_setNoisyFox\">设置为 noisyfox.io</string>\n\n    <string name=\"version_prefix\">版本</string>\n\n    <string name=\"prefSummary_visitBlog\">欢迎访问我的博客来支持我的作品！ 猛戳这里 一noisyfox.io</string>\n    <string name=\"prefTitle_visitBlog\">求关注！求打赏！</string>\n    <string name=\"prefCategory_about\">关于</string>\n\n    <string name=\"prefSummary_visitGithub\">猛戳这里 一github.com/Noisyfox/NoExclamation</string>\n    <string name=\"prefTitle_openSrcGithub\">开源啦！</string>\n    <string name=\"prefCategory_detailedSettings\">详细设置</string>\n\n    <string name=\"prefTitle_resetToDefault\">重置为默认值</string>\n    <string name=\"prefSummary_resetToDefault\">将 portal server 重置为系统默认设置。</string>\n    <string name=\"prefTitle_setToNoisyfox\">设为 noisyfox.io</string>\n    <string name=\"prefSummary_setToNoisyfox\">将 portal server 设置为 noisyfox.io。</string>\n\n    <string name=\"prefCategory_portalSetup\">Portal Server 一键设置</string>\n    <string name=\"prefTitle_refreshStatus\">刷新当前状态</string>\n\n    <string name=\"prefCategory_Instructions\">使用说明</string>\n    <string name=\"prefSummary_Instructions\">最简单的去掉网络连接的感叹号的方式： 戳 设为 noisyfox.io 选项，然后开飞行模式，然后再关飞行模式，搞定！</string>\n    <string name=\"status_unknown\">未知</string>\n    <string name=\"setting_useHttps\">启用 https</string>\n    <string name=\"value_isDefault\">默认</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/main_pref.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<PreferenceScreen xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <PreferenceCategory android:title=\"@string/prefCategory_Instructions\">\n        <Preference android:summary=\"@string/prefSummary_Instructions\" />\n    </PreferenceCategory>\n\n    <Preference\n        android:key=\"refresh\"\n        android:title=\"@string/prefTitle_refreshStatus\" />\n\n    <PreferenceCategory android:title=\"@string/prefCategory_portalSetup\">\n        <Preference\n            android:key=\"set_noisyfox\"\n            android:summary=\"@string/prefSummary_setToNoisyfox\"\n            android:title=\"@string/prefTitle_setToNoisyfox\" />\n        <Preference\n            android:key=\"set_google\"\n            android:summary=\"@string/prefSummary_resetToDefault\"\n            android:title=\"@string/prefTitle_resetToDefault\" />\n    </PreferenceCategory>\n\n    <PreferenceCategory\n        android:key=\"cat_detail_settings\"\n        android:title=\"@string/prefCategory_detailedSettings\">\n\n    </PreferenceCategory>\n\n    <PreferenceCategory android:title=\"@string/prefCategory_about\">\n        <Preference\n            android:summary=\"@string/prefSummary_visitGithub\"\n            android:title=\"@string/prefTitle_openSrcGithub\">\n            <intent\n                android:action=\"android.intent.action.VIEW\"\n                android:data=\"https://github.com/Noisyfox/NoExclamation\" />\n        </Preference>\n        <Preference\n            android:summary=\"@string/prefSummary_visitBlog\"\n            android:title=\"@string/prefTitle_visitBlog\">\n            <intent\n                android:action=\"android.intent.action.VIEW\"\n                android:data=\"https://www.noisyfox.io\" />\n        </Preference>\n        <Preference\n            android:summary=\"@string/app_version_name\"\n            android:title=\"@string/version_prefix\" />\n    </PreferenceCategory>\n</PreferenceScreen>"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    repositories {\n        jcenter()\n        google()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:3.3.2'\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        google()\n    }\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Mar 18 10:14:12 AEDT 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.1-all.zip\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"
  }
]