[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/UpdateAppUtils\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n        <option name=\"resolveModulePerSourceSet\" value=\"false\" />\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_7\" project-jdk-name=\"JDK\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/FastUpdate.iml\" filepath=\"$PROJECT_DIR$/FastUpdate.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/app/app.iml\" filepath=\"$PROJECT_DIR$/app/app.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/UpdateAppUtils/updateapputils.iml\" filepath=\"$PROJECT_DIR$/UpdateAppUtils/updateapputils.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "README.md",
    "content": "# UpdateAppUtils1.4\n### 一行代码，快速实现app在线下载更新<br>  A simple library for Android update app\n\n\n![](update.gif)\n\n\n\n## 集成\ncompile引入\n```\ndependencies {\n    compile 'com.teprinciple:updateapputils:1.4'\n}\n```\n\n## 使用\n更新检测一般放在MainActivity或者启动页上，\n在请求服务器版本检测接口获取到versionCode、versionName、最新apkPath后调用。\n\n\n\n\n\n\n#### 快速使用\n```\n UpdateAppUtils.from(this)\n                .serverVersionCode(2)  //服务器versionCode\n                .serverVersionName(\"2.0\") //服务器versionName\n                .apkPath(apkPath) //最新apk下载地址\n                .update();\n```\n\n#### Kotlin代码调用完全一样\n```\n   private fun update() {\n        val apkPath:String = \"http://issuecdn.baidupcs.com/issue/netdisk/apk/BaiduNetdisk_7.15.1.apk\"\n\n        UpdateAppUtils.from(this)\n                .serverVersionCode(2)\n                .serverVersionName(\"2.0\")\n                .apkPath(apkPath)\n                .update()\n    }\n\n```\n\n\n\n#### 更多配置使用\n```\nUpdateAppUtils.from(this)\n                .checkBy(UpdateAppUtils.CHECK_BY_VERSION_NAME) //更新检测方式，默认为VersionCode\n                .serverVersionCode(2)\n                .serverVersionName(\"2.0\")\n                .apkPath(apkPath)\n                .showNotification(false) //是否显示下载进度到通知栏，默认为true\n                .updateInfo(info)  //更新日志信息 String\n                .downloadBy(UpdateAppUtils.DOWNLOAD_BY_BROWSER) //下载方式：app下载、手机浏览器下载。默认app下载\n                .isForce(true) //是否强制更新，默认false 强制更新情况下用户不同意更新则不能使用app\n                .update();\n```\n\n#### 说明\n```\n    1、UpdateAppUtils提供两种更新判断方式\n\n    CHECK_BY_VERSION_CODE:通过versionCode判断，服务器上versionCode > 本地versionCode则执行更新\n\n    CHECK_BY_VERSION_NAME：通过versionName判断，服务器上versionName 与 本地versionName不同则更新\n\n    2、UpdateAppUtils提供两种下载apk方式\n\n    DOWNLOAD_BY_APP：通过App下载\n\n    DOWNLOAD_BY_BROWSER：通过手机浏览器下载\n\n```\n\n#### 关于适配Android6.0、Android7.0\n\n##### 适配Android7.0\n\n1、注册provider\n```\n    <provider\n            android:name=\"android.support.v4.content.FileProvider\"\n            android:authorities=\"${applicationId}.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/file_paths\" />\n    /provider>\n```\n2、新建file_paths.xml文件\n```\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <external-path path=\"Android/data/包名/\"    name=\"files_root\" />\n    <external-path path=\".\" name=\"external_storage_root\" />\n</paths>\n```\n可参见demo中的代码\n\n如果你的版本没有适配到Android7.0 为了不进行上述操作，可以直接这样设置：\n\n```\nUpdateAppUtils.needFitAndroidN(false)\n```\n\n\n##### 适配Android6.0\n关于6.0适配，请自行在调用API时申请WRITE_EXTERNAL_STORAGE权限，可以参考demo中的代码\n\n\n\n#### 更新日志\n1.4<br>\n使用[filedownloader](https://github.com/lingochamp/FileDownloader)替换DownloadManager，避免部分手机DownLoadManager无效，同时解决了重复下载的问题，且提高了下载速度<br>\n增加接口UpdateAppUtils.needFitAndroidN(false)，避免不需要适配7.0，也要设置FileProvider<br>\n<br>1.3.1<br>\n修复部分bug，在demo中加入kotlin调用代码<br>\n<br>1.3<br>\n增加接口方法 showNotification(false)//是否显示下载进度到通知栏；<br>updateInfo(info)//更新日志信息；下载前WiFi判断。<br>\n<br>1.2<br>\n适配Android7.0，并在demo中加入适配6.0和7.0的代码<br>\n<br>1.1<br>\n适配更多SdkVersion"
  },
  {
    "path": "UpdateAppUtils/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "UpdateAppUtils/build.gradle",
    "content": "apply plugin: 'com.android.library'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"25.0.3\"\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion 25\n        versionCode 1\n        versionName \"1.0\"\n\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n\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:25.3.1'\n    testCompile 'junit:junit:4.12'\n    compile 'com.liulishuo.filedownloader:library:1.6.8'\n}\n\n\n\n\napply plugin: 'com.github.dcendents.android-maven'\napply plugin: 'com.jfrog.bintray'\n// This is the library version used when deploying the artifact\nversion = \"1.4\"\n\ndef siteUrl = 'https://github.com/teprinciple/UpdateAppDemo'      // 项目的主页\ndef gitUrl = 'https://github.com/teprinciple/UpdateAppDemo.git'   // Git仓库的url\ngroup = \"com.teprinciple\"          // Maven Group ID for the artifact，一般填你唯一的包名\ninstall {\n    repositories.mavenInstaller {\n        // This generates POM.file_paths with proper parameters\n        pom {\n            project {\n                packaging 'aar'\n                // Add your description here\n                name 'A Simple library for Android update app'\n                url siteUrl\n                // Set your license\n                licenses {\n                    license {\n                        name 'The Apache Software License, Version 2.0'\n                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                    }\n                }\n                developers {\n                    developer {\n                        id 'teprinciple'        //填写的一些基本信息\n                        name 'teprinciple'\n                        email 'teprinciple@foxmail.com'\n                    }\n                }\n                scm {\n                    connection gitUrl\n                    developerConnection gitUrl\n                    url siteUrl\n                }\n            }\n        }\n    }\n}\ntask sourcesJar(type: Jar) {\n    from android.sourceSets.main.java.srcDirs\n    classifier = 'sources'\n}\ntask javadoc(type: Javadoc) {\n    source = android.sourceSets.main.java.srcDirs\n    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n}\ntask javadocJar(type: Jar, dependsOn: javadoc) {\n    classifier = 'javadoc'\n    from javadoc.destinationDir\n}\nartifacts {\n    archives javadocJar\n    archives sourcesJar\n}\nProperties properties = new Properties()\nproperties.load(project.rootProject.file('local.properties').newDataInputStream())\nbintray {\n    user = properties.getProperty(\"bintray.user\")\n    key = properties.getProperty(\"bintray.apikey\")\n    configurations = ['archives']\n    pkg {\n        repo = \"maven\" ////跟上面创建的Maven仓库名字保持一致\n        name = \"UpdateAppUtils\"    //发布到JCenter上的项目名字\n        websiteUrl = siteUrl\n        vcsUrl = gitUrl\n        licenses = [\"Apache-2.0\"]\n        publish = true\n    }\n}\njavadoc { //jav doc采用utf-8编码否则会报“GBK的不可映射字符”错误\n    options{\n        encoding \"UTF-8\"\n        charSet 'UTF-8'\n    }\n}\n\n"
  },
  {
    "path": "UpdateAppUtils/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 /Users/teprinciple/Library/Android/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": "UpdateAppUtils/src/androidTest/java/teprinciple/library/ExampleInstrumentedTest.java",
    "content": "package teprinciple.library;\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(\"teprinciple.library.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "UpdateAppUtils/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\n          package=\"teprinciple.updateapputils\"\n    >\n\n    <uses-permission android:name=\"android.permission.DOWNLOAD_WITHOUT_NOTIFICATION\"/>\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n\n    <application\n        android:allowBackup=\"true\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        >\n\n        <receiver\n            android:name=\"util.UpdateAppReceiver\"\n            android:enabled=\"true\"\n            android:exported=\"false\">\n            <intent-filter>\n                <action android:name=\"teprinciple.update\"/>\n            </intent-filter>\n        </receiver>\n\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "UpdateAppUtils/src/main/java/customview/ConfirmDialog.java",
    "content": "package customview;\n\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.TextView;\n\n\nimport feature.Callback;\nimport teprinciple.updateapputils.R;\n\n\n/**\n * Created by Teprinciple on 2016/10/13.\n */\npublic class ConfirmDialog extends Dialog {\n\n    Callback callback;\n    private TextView content;\n    private TextView sureBtn;\n    private TextView cancleBtn;\n\n    public ConfirmDialog(Context context, Callback callback) {\n        super(context, R.style.CustomDialog);\n        this.callback = callback;\n        setCustomDialog();\n    }\n\n    private void setCustomDialog() {\n        View mView = LayoutInflater.from(getContext()).inflate(R.layout.dialog_confirm, null);\n        sureBtn = (TextView)mView.findViewById(R.id.dialog_confirm_sure);\n        cancleBtn = (TextView)mView.findViewById(R.id.dialog_confirm_cancle);\n        content = (TextView) mView.findViewById(R.id.dialog_confirm_title);\n\n\n        sureBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                callback.callback(1);\n                ConfirmDialog.this.cancel();\n            }\n        });\n        cancleBtn.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                callback.callback(0);\n                ConfirmDialog.this.cancel();\n            }\n        });\n        super.setContentView(mView);\n    }\n\n\n    public ConfirmDialog setContent(String s){\n        content.setText(s);\n        return this;\n    }\n\n\n}\n"
  },
  {
    "path": "UpdateAppUtils/src/main/java/feature/Callback.java",
    "content": "package feature;\n\n/**\n * Created by sanmu on 2016/10/13 0013.\n */\npublic interface Callback {\n    public void callback(int position);\n}\n"
  },
  {
    "path": "UpdateAppUtils/src/main/java/util/DownloadAppUtils.java",
    "content": "package util;\n\nimport android.app.AlertDialog;\nimport android.app.DownloadManager;\nimport android.content.ActivityNotFoundException;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.Settings;\nimport android.support.v4.content.FileProvider;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.widget.Toast;\n\nimport com.liulishuo.filedownloader.BaseDownloadTask;\nimport com.liulishuo.filedownloader.FileDownloadLargeFileListener;\nimport com.liulishuo.filedownloader.FileDownloader;\n\nimport java.io.File;\n\n\n/**\n * Created by Teprinciple on 2016/12/13.\n */\nclass DownloadAppUtils {\n    private static final String TAG = DownloadAppUtils.class.getSimpleName();\n    public static long downloadUpdateApkId = -1;//下载更新Apk 下载任务对应的Id\n    public static String downloadUpdateApkFilePath;//下载更新Apk 文件路径\n\n\n    /**\n     * 通过浏览器下载APK包\n     * @param context\n     * @param url\n     */\n    public static void downloadForWebView(Context context, String url) {\n        Uri uri = Uri.parse(url);\n        Intent intent = new Intent(Intent.ACTION_VIEW, uri);\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        context.startActivity(intent);\n    }\n\n\n\n    public static void download(final Context context, String url,final String serverVersionName) {\n\n        String packageName = context.getPackageName();\n        String filePath = null;\n        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {//外部存储卡\n            filePath = Environment.getExternalStorageDirectory().getAbsolutePath();\n        } else {\n            Log.i(TAG, \"没有SD卡\");\n            return;\n        }\n\n        String apkLocalPath= filePath + File.separator + packageName + \"_\"+serverVersionName+\".apk\";\n\n        downloadUpdateApkFilePath = apkLocalPath;\n\n        FileDownloader.setup(context);\n\n        FileDownloader.getImpl().create(url)\n                .setPath(apkLocalPath)\n                .setListener(new FileDownloadLargeFileListener() {\n                    @Override\n                    protected void pending(BaseDownloadTask task, long soFarBytes, long totalBytes) {\n                    }\n\n                    @Override\n                    protected void progress(BaseDownloadTask task, long soFarBytes, long totalBytes) {\n                        send(context, (int) (soFarBytes*100.0/totalBytes),serverVersionName);\n                    }\n\n                    @Override\n                    protected void paused(BaseDownloadTask task, long soFarBytes, long totalBytes) {\n                    }\n\n                    @Override\n                    protected void completed(BaseDownloadTask task) {\n                        send(context,100,serverVersionName);\n                    }\n\n                    @Override\n                    protected void error(BaseDownloadTask task, Throwable e) {\n                        Toast.makeText(context, \"下载出错\", Toast.LENGTH_SHORT).show();\n                    }\n\n                    @Override\n                    protected void warn(BaseDownloadTask task) {\n                    }\n                }).start();\n    }\n\n\n\n\n    private static void send(Context context,int progress,String serverVersionName) {\n        Intent intent = new Intent(\"teprinciple.update\");\n        intent.putExtra(\"progress\",progress);\n        intent.putExtra(\"title\",serverVersionName);\n        context.sendBroadcast(intent);\n    }\n\n\n}\n"
  },
  {
    "path": "UpdateAppUtils/src/main/java/util/UpdateAppReceiver.java",
    "content": "package util;\n\nimport android.app.Notification;\nimport android.app.NotificationManager;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.support.v4.app.NotificationCompat;\nimport android.support.v4.content.FileProvider;\n\nimport java.io.File;\n\n\n\n/**\n * Created by Teprinciple on 2017/11/3.\n */\n\n public class UpdateAppReceiver extends BroadcastReceiver {\n\n\n    @Override\n    public void onReceive(Context context, Intent intent) {\n\n        int notifyId = 1;\n        int progress = intent.getIntExtra(\"progress\", 0);\n        String title = intent.getStringExtra(\"title\");\n\n        NotificationManager nm = null;\n        if (UpdateAppUtils.showNotification){\n            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);\n            builder.setContentTitle(\"正在下载 \"+title);\n            builder.setSmallIcon(android.R.mipmap.sym_def_app_icon);\n            builder.setProgress(100,progress,false);\n\n            Notification notification = builder.build();\n            nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);\n            nm.notify(notifyId,notification);\n        }\n\n\n        if (progress == 100){\n            if (nm!=null)nm.cancel(notifyId);\n\n            if (DownloadAppUtils.downloadUpdateApkFilePath != null) {\n                Intent i = new Intent(Intent.ACTION_VIEW);\n                File apkFile = new File(DownloadAppUtils.downloadUpdateApkFilePath);\n                if ( UpdateAppUtils.needFitAndroidN &&  Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                    i.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                    Uri contentUri = FileProvider.getUriForFile(\n                            context, context.getPackageName() + \".fileprovider\", apkFile);\n                    i.setDataAndType(contentUri, \"application/vnd.android.package-archive\");\n                } else {\n                    i.setDataAndType(Uri.fromFile(apkFile),\n                            \"application/vnd.android.package-archive\");\n                }\n                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                context.startActivity(i);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "UpdateAppUtils/src/main/java/util/UpdateAppUtils.java",
    "content": "package util;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport customview.ConfirmDialog;\nimport feature.Callback;\n\n\n/**\n * Created by Teprinciple on 2016/11/15.\n */\npublic class UpdateAppUtils {\n\n    private final String TAG = \"UpdateAppUtils\";\n    public static final int CHECK_BY_VERSION_NAME = 1001;\n    public static final int CHECK_BY_VERSION_CODE = 1002;\n    public static final int DOWNLOAD_BY_APP = 1003;\n    public static final int DOWNLOAD_BY_BROWSER = 1004;\n\n    private Activity activity;\n    private int checkBy = CHECK_BY_VERSION_CODE;\n    private int downloadBy = DOWNLOAD_BY_APP;\n    private int serverVersionCode = 0;\n    private String apkPath=\"\";\n    private String serverVersionName=\"\";\n    private boolean isForce = false; //是否强制更新\n    private int localVersionCode = 0;\n    private String localVersionName=\"\";\n    public static boolean needFitAndroidN = true; //提供给 整个工程不需要适配到7.0的项目 置为false\n    public static boolean showNotification = true;\n    private String updateInfo = \"\";\n\n\n\n\n    public UpdateAppUtils needFitAndroidN(boolean needFitAndroidN) {\n        UpdateAppUtils.needFitAndroidN = needFitAndroidN;\n        return this;\n    }\n\n    private UpdateAppUtils(Activity activity) {\n        this.activity = activity;\n        getAPPLocalVersion(activity);\n    }\n\n    public static UpdateAppUtils from(Activity activity){\n        return new UpdateAppUtils(activity);\n    }\n\n    public UpdateAppUtils checkBy(int checkBy){\n        this.checkBy = checkBy;\n        return this;\n    }\n\n    public UpdateAppUtils apkPath(String apkPath){\n        this.apkPath = apkPath;\n        return this;\n    }\n\n    public UpdateAppUtils downloadBy(int downloadBy){\n        this.downloadBy = downloadBy;\n        return this;\n    }\n\n    public UpdateAppUtils showNotification(boolean showNotification){\n        this.showNotification = showNotification;\n        return this;\n    }\n\n    public UpdateAppUtils updateInfo(String updateInfo){\n        this.updateInfo = updateInfo;\n        return this;\n    }\n\n\n\n    public UpdateAppUtils serverVersionCode(int serverVersionCode){\n        this.serverVersionCode = serverVersionCode;\n        return this;\n    }\n\n    public UpdateAppUtils serverVersionName(String  serverVersionName){\n        this.serverVersionName = serverVersionName;\n        return this;\n    }\n\n    public UpdateAppUtils isForce(boolean  isForce){\n        this.isForce = isForce;\n        return this;\n    }\n\n    //获取apk的版本号 currentVersionCode\n    private  void getAPPLocalVersion(Context ctx) {\n        PackageManager manager = ctx.getPackageManager();\n        try {\n            PackageInfo info = manager.getPackageInfo(ctx.getPackageName(), 0);\n            localVersionName = info.versionName; // 版本名\n            localVersionCode = info.versionCode; // 版本号\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public void update(){\n\n        switch (checkBy){\n            case CHECK_BY_VERSION_CODE:\n                if (serverVersionCode >localVersionCode){\n                    toUpdate();\n                }else {\n                    Log.i(TAG,\"当前版本是最新版本\"+serverVersionCode+\"/\"+serverVersionName);\n                }\n                break;\n\n            case CHECK_BY_VERSION_NAME:\n                if (!serverVersionName.equals(localVersionName)){\n                    toUpdate();\n                }else {\n                    Log.i(TAG,\"当前版本是最新版本\"+serverVersionCode+\"/\"+serverVersionName);\n                }\n                break;\n        }\n\n    }\n\n    private void toUpdate() {\n\n        realUpdate();\n\n    }\n\n    private void realUpdate() {\n        ConfirmDialog dialog = new ConfirmDialog(activity, new Callback() {\n            @Override\n            public void callback(int position) {\n                switch (position){\n                    case 0:  //cancle\n                        if (isForce)System.exit(0);\n                        break;\n\n                    case 1:  //sure\n                        if (downloadBy == DOWNLOAD_BY_APP) {\n                            if (isWifiConnected(activity)){\n//                                DownloadAppUtils.downloadForAutoInstall(activity, apkPath, \"demo.apk\", serverVersionName);\n                                DownloadAppUtils.download(activity, apkPath, serverVersionName);\n                            }else {\n                                new ConfirmDialog(activity, new Callback() {\n                                    @Override\n                                    public void callback(int position) {\n                                        if (position==1){\n                                            DownloadAppUtils.download(activity, apkPath, serverVersionName);\n                                            //DownloadAppUtils.downloadForAutoInstall(activity, apkPath, \"demo.apk\", serverVersionName);\n                                        }else {\n                                            if (isForce)activity.finish();\n                                        }\n                                    }\n                                }).setContent(\"目前手机不是WiFi状态\\n确认是否继续下载更新？\").show();\n                            }\n\n                        }else if (downloadBy == DOWNLOAD_BY_BROWSER){\n                            DownloadAppUtils.downloadForWebView(activity,apkPath);\n                        }\n                        break;\n                }\n            }\n        });\n\n        String content = \"发现新版本:\"+serverVersionName+\"\\n是否下载更新?\";\n        if (!TextUtils.isEmpty(updateInfo)){\n            content = \"发现新版本:\"+serverVersionName+\"是否下载更新?\\n\\n\"+updateInfo;\n        }\n        dialog .setContent(content);\n        dialog.setCancelable(false);\n        dialog.show();\n    }\n\n\n    /**\n     * 检测wifi是否连接\n     */\n    public static boolean isWifiConnected(Context context) {\n        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n        if (cm != null) {\n            NetworkInfo networkInfo = cm.getActiveNetworkInfo();\n            if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n\n}\n"
  },
  {
    "path": "UpdateAppUtils/src/main/res/drawable/bg_dialog_confirm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <size android:height=\"20dp\" android:width=\"40dp\"/>\n    <solid android:color=\"#EFEFEF\"/>\n    <corners android:radius=\"12dp\"/>\n\n</shape>"
  },
  {
    "path": "UpdateAppUtils/src/main/res/layout/dialog_confirm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\"\n    >\n\n    <LinearLayout\n        android:layout_width=\"270dp\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"vertical\"\n        android:background=\"@drawable/bg_dialog_confirm\">\n\n\n        <TextView\n            android:layout_marginTop=\"15dp\"\n            android:layout_marginLeft=\"15dp\"\n            android:layout_marginRight=\"15dp\"\n            android:gravity=\"center_horizontal\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:textSize=\"19sp\"\n            android:text=\"提示\"\n            android:textColor=\"@color/text_black\"\n            />\n\n        <TextView\n            android:id=\"@+id/dialog_confirm_title\"\n            android:layout_marginTop=\"10dp\"\n            android:layout_marginBottom=\"10dp\"\n            android:layout_marginLeft=\"15dp\"\n            android:layout_marginRight=\"15dp\"\n            android:gravity=\"center_horizontal\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:textSize=\"17sp\"\n            android:lineSpacingExtra=\"5dp\"\n            android:textColor=\"@color/text_black\"\n            />\n\n\n\n        <View\n            android:layout_marginTop=\"13dp\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"1dp\"\n            android:background=\"#DFDFDF\"/>\n\n\n        <LinearLayout\n            android:orientation=\"horizontal\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"44dp\">\n\n            <TextView\n                android:clickable=\"true\"\n                android:id=\"@+id/dialog_confirm_cancle\"\n                android:gravity=\"center\"\n                android:layout_width=\"0dp\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"match_parent\"\n                android:text=\"@string/cancel\"\n                android:textSize=\"17sp\"\n                android:textColor=\"@color/text_blue\"\n                />\n            <View\n                android:layout_width=\"1dp\"\n                android:layout_height=\"match_parent\"\n                android:background=\"#DFDFDF\"/>\n            <TextView\n                android:clickable=\"true\"\n                android:id=\"@+id/dialog_confirm_sure\"\n                android:gravity=\"center\"\n                android:layout_width=\"0dp\"\n                android:layout_weight=\"1\"\n                android:layout_height=\"match_parent\"\n                android:text=\"@string/sure\"\n                android:textSize=\"17sp\"\n                android:textColor=\"@color/text_blue\"\n                />\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n\n</LinearLayout>"
  },
  {
    "path": "UpdateAppUtils/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    <color name=\"text_blue\">#0076FF</color>\n    <color name=\"text_black\">#333333</color>\n</resources>\n"
  },
  {
    "path": "UpdateAppUtils/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Library</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"sure\">确认</string>\n</resources>\n"
  },
  {
    "path": "UpdateAppUtils/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"CustomDialog\" parent=\"@android:style/Theme.Dialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowIsTranslucent\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowBackground\">@drawable/bg_dialog_confirm</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n    </style>\n</resources>"
  },
  {
    "path": "UpdateAppUtils/src/test/java/teprinciple/library/ExampleUnitTest.java",
    "content": "package teprinciple.library;\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": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\n\nandroid {\n    compileSdkVersion 25\n    buildToolsVersion \"25.0.2\"\n    defaultConfig {\n        applicationId \"com.example.teprinciple.updateappdemo\"\n        minSdkVersion 19\n        targetSdkVersion 23\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    lintOptions {\n        abortOnError false\n    }\n}\n\ndependencies {\n    compile fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    compile project(':updateapputils')\n    compile 'com.android.support:appcompat-v7:25.1.0'\n    compile 'com.android.support.constraint:constraint-layout:1.0.2'\n    testCompile 'junit:junit:4.12'\n    compile \"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version\"\n}\nrepositories {\n    mavenCentral()\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 C:\\Users\\Teprinciple\\AppData\\Local\\Android\\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"
  },
  {
    "path": "app/src/androidTest/java/com/example/teprinciple/updateappdemo/ExampleInstrumentedTest.java",
    "content": "package com.example.teprinciple.updateappdemo;\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.example.teprinciple.updateappdemo\", appContext.getPackageName());\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=\"com.example.teprinciple.updateappdemo\">\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\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\n        <provider\n            android:name=\"android.support.v4.content.FileProvider\"\n            android:authorities=\"${applicationId}.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/file_paths\"/>\n        </provider>\n\n        <activity android:name=\".KotlinDemoActivity\">\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/com/example/teprinciple/updateappdemo/KotlinDemoActivity.kt",
    "content": "package com.example.teprinciple.updateappdemo\n\nimport android.support.v7.app.AppCompatActivity\nimport android.os.Bundle\nimport util.UpdateAppUtils\n\nclass KotlinDemoActivity : AppCompatActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_kotlin_demo)\n        findViewById(R.id.kotlin_btn).setOnClickListener {\n            update()\n        }\n    }\n\n    private fun update() {\n        val apkPath:String = \"http://issuecdn.baidupcs.com/issue/netdisk/apk/BaiduNetdisk_7.15.1.apk\"\n        UpdateAppUtils.from(this)\n                .serverVersionCode(2)\n                .serverVersionName(\"2.0\")\n                .apkPath(apkPath)\n                .update()\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/example/teprinciple/updateappdemo/MainActivity.java",
    "content": "package com.example.teprinciple.updateappdemo;\n\nimport android.Manifest;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.provider.Settings;\nimport android.support.annotation.NonNull;\nimport android.support.v4.app.ActivityCompat;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.app.AppCompatActivity;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport customview.ConfirmDialog;\nimport feature.Callback;\nimport util.UpdateAppUtils;\n\n\npublic class MainActivity extends AppCompatActivity {\n\n    //服务器apk path,这里放了百度云盘的apk 作为测试\n    String apkPath = \"http://issuecdn.baidupcs.com/issue/netdisk/apk/BaiduNetdisk_7.15.1.apk\";\n    private int code = 0;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n    }\n\n    public void updateApp(View view) {\n        checkAndUpdate(1);\n    }\n\n    public void downloadByWeb(View view) {\n        checkAndUpdate(2);\n    }\n\n\n    public void forceUpdate(View view) {\n        checkAndUpdate(3);\n    }\n\n\n    public void checkByName(View view) {\n        checkAndUpdate(4);\n    }\n\n    public void kotlin(View view) {\n        checkAndUpdate(5);\n    }\n\n    private void checkAndUpdate(int code) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            realUpdate(code);\n        } else {\n            if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)\n                    == PackageManager.PERMISSION_GRANTED) {\n                realUpdate(code);\n            } else {//申请权限\n                ActivityCompat.requestPermissions(this,\n                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);\n            }\n        }\n    }\n\n\n    private void realUpdate(int code) {\n        this.code = code;\n        switch (code) {\n            case 1:\n                updat1();\n                break;\n            case 2:\n                update2();\n                break;\n            case 3:\n                update3();\n                break;\n            case 4:\n                update4();\n                break;\n            case 5:\n                startActivity(new Intent(this,KotlinDemoActivity.class));\n                break;\n\n        }\n    }\n\n\n    //基本更新\n    private void updat1() {\n        UpdateAppUtils.from(this)\n                .serverVersionCode(2)\n                .serverVersionName(\"2.0\")\n                .apkPath(apkPath)\n                .updateInfo(\"1.修复若干bug\\n2.美化部分页面\\n3.增加微信支付方式\")\n//                .showNotification(false)\n//                .needFitAndroidN(false)\n                .update();\n    }\n\n    //通过浏览器下载\n    private void update2() {\n        UpdateAppUtils.from(this)\n                .serverVersionCode(2)\n                .serverVersionName(\"2.0\")\n                .apkPath(apkPath)\n                .downloadBy(UpdateAppUtils.DOWNLOAD_BY_BROWSER)\n                .update();\n    }\n\n    //强制更新\n    private void update3() {\n        UpdateAppUtils.from(this)\n                .serverVersionCode(2)\n                .serverVersionName(\"2.0\")\n                .apkPath(apkPath)\n                .isForce(true)\n                .update();\n    }\n\n    //根据versionName判断跟新\n    private void update4() {\n        UpdateAppUtils.from(this)\n                .checkBy(UpdateAppUtils.CHECK_BY_VERSION_NAME)\n                .serverVersionName(\"2.0\")\n                .serverVersionCode(2)\n                .apkPath(apkPath)\n                .downloadBy(UpdateAppUtils.DOWNLOAD_BY_BROWSER)\n                .isForce(true)\n                .update();\n    }\n\n\n    //权限请求结果\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n\n        switch (requestCode) {\n            case 1:\n                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n                    realUpdate(code);\n                } else {\n                    new ConfirmDialog(this, new Callback() {\n                        @Override\n                        public void callback(int position) {\n                            if (position==1){\n                                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);\n                                intent.setData(Uri.parse(\"package:\" + getPackageName())); // 根据包名打开对应的设置界面\n                                startActivity(intent);\n                            }\n                        }\n                    }).setContent(\"暂无读写SD卡权限\\n是否前往设置？\").show();\n                }\n                break;\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/bg_dialog_confirm.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n\n    <size android:height=\"20dp\" android:width=\"40dp\"/>\n    <solid android:color=\"#EFEFEF\"/>\n    <corners android:radius=\"12dp\"/>\n\n</shape>"
  },
  {
    "path": "app/src/main/res/layout/activity_kotlin_demo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.example.teprinciple.updateappdemo.KotlinDemoActivity\">\n\n\n    <Button\n        android:id=\"@+id/kotlin_btn\"\n        android:text=\"kotlin使用\"\n        android:layout_marginTop=\"50dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\"/>\n\n</RelativeLayout>\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    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.example.teprinciple.updateappdemo.MainActivity\">\n\n    <TextView\n        android:layout_marginTop=\"10dp\"\n        android:gravity=\"center\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"这里是借助百度云盘apk下载地址作为测试\"/>\n\n    <Button\n        android:layout_marginTop=\"50dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"最基本使用\"\n        android:onClick=\"updateApp\"/>\n\n    <Button\n        android:layout_marginTop=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"浏览器下载apk\"\n        android:onClick=\"downloadByWeb\"/>\n\n    <Button\n        android:layout_marginTop=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"强制更新，不更新退出app\"\n        android:onClick=\"forceUpdate\"/>\n\n    <Button\n        android:layout_marginTop=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"通过Version_Name判断更新，不同则更新\\n（当前Version_Name1.0）\"\n        android:onClick=\"checkByName\"/>\n\n    <Button\n        android:layout_marginTop=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Kotlin\"\n        android:onClick=\"kotlin\"/>\n\n\n\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_start.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/activity_start\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    tools:context=\"com.example.teprinciple.updateappdemo.StartActivity\">\n\n\n    <Button\n        android:onClick=\"go\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"50dp\" />\n\n\n</RelativeLayout>\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    <color name=\"text_blue\">#0076FF</color>\n    <color name=\"text_black\">#333333</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">UpdateAppDemo</string>\n    <string name=\"cancel\">取消</string>\n    <string name=\"sure\">确认</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    <style name=\"CustomDialog\" parent=\"@android:style/Theme.Dialog\">\n        <item name=\"android:windowFrame\">@null</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowIsTranslucent\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowBackground\">@drawable/bg_dialog_confirm</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n    </style>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.file_paths\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/file_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <external-path path=\"Android/data/com.example.teprinciple.updateappdemo/\"    name=\"files_root\" />\n    <external-path path=\".\" name=\"external_storage_root\" />\n</paths>\n"
  },
  {
    "path": "app/src/test/java/com/example/teprinciple/updateappdemo/ExampleUnitTest.java",
    "content": "package com.example.teprinciple.updateappdemo;\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    ext.kotlin_version = '1.1.4-3'\n\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:2.2.2'\n\n        classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'\n        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.6'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    tasks.withType(Javadoc).all { enabled = false }\n    repositories {\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n\n\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon Dec 28 10:00:20 PST 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.14.1-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\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', ':updateapputils'\n"
  }
]