浏览代码

修改download-source

JonaNorman 1 年之前
父节点
当前提交
df3e0241df
共有 50 个文件被更改,包括 907 次插入1402 次删除
  1. 5 2
      app/build.gradle
  2. 3 5
      app/src/main/AndroidManifest.xml
  3. 0 165
      app/src/main/assets/aria_config.xml
  4. 14 15
      app/src/main/java/com/norman/webviewup/demo/MainActivity.java
  5. 0 3
      app/src/main/java/com/norman/webviewup/demo/MyApp.java
  6. 0 26
      aria-source/src/androidTest/java/com/norman/webviewup/lib/ExampleInstrumentedTest.java
  7. 0 4
      aria-source/src/main/AndroidManifest.xml
  8. 0 160
      aria-source/src/main/java/com/norman/webviewup/lib/source/aira/WebViewUrlSource.java
  9. 0 17
      aria-source/src/test/java/com/norman/webviewup/lib/ExampleUnitTest.java
  10. 3 3
      build.gradle
  11. 3 3
      core/build.gradle
  12. 1 1
      core/src/main/AndroidManifest.xml
  13. 0 2
      core/src/main/java/com/norman/webviewup/lib/UpgradeCallback.java
  14. 0 38
      core/src/main/java/com/norman/webviewup/lib/UpgradeDirectory.java
  15. 0 62
      core/src/main/java/com/norman/webviewup/lib/UpgradeOptions.java
  16. 148 0
      core/src/main/java/com/norman/webviewup/lib/WebViewReplace.java
  17. 15 0
      core/src/main/java/com/norman/webviewup/lib/WebViewReplaceException.java
  18. 68 367
      core/src/main/java/com/norman/webviewup/lib/WebViewUpgrade.java
  19. 0 40
      core/src/main/java/com/norman/webviewup/lib/download/DownloadAction.java
  20. 0 7
      core/src/main/java/com/norman/webviewup/lib/download/DownloadSink.java
  21. 39 21
      core/src/main/java/com/norman/webviewup/lib/hook/PackageManagerServiceHook.java
  22. 0 1
      core/src/main/java/com/norman/webviewup/lib/hook/WebViewUpdateServiceHook.java
  23. 96 0
      core/src/main/java/com/norman/webviewup/lib/source/UpgradeAssetSource.java
  24. 28 0
      core/src/main/java/com/norman/webviewup/lib/source/UpgradeFileSource.java
  25. 10 15
      core/src/main/java/com/norman/webviewup/lib/source/UpgradePackageSource.java
  26. 45 0
      core/src/main/java/com/norman/webviewup/lib/source/UpgradePathSource.java
  27. 139 0
      core/src/main/java/com/norman/webviewup/lib/source/UpgradeSource.java
  28. 0 117
      core/src/main/java/com/norman/webviewup/lib/util/ApkUtils.java
  29. 11 9
      core/src/main/java/com/norman/webviewup/lib/util/FileUtils.java
  30. 16 0
      core/src/main/java/com/norman/webviewup/lib/util/HandlerUtils.java
  31. 0 31
      core/src/main/java/com/norman/webviewup/lib/util/Md5Utils.java
  32. 0 0
      download-source/.gitignore
  33. 3 4
      download-source/build.gradle
  34. 0 0
      download-source/consumer-rules.pro
  35. 0 0
      download-source/proguard-rules.pro
  36. 8 0
      download-source/src/main/AndroidManifest.xml
  37. 246 0
      download-source/src/main/java/com/norman/webviewup/lib/source/download/UpgradeDownloadSource.java
  38. 4 1
      gradle.properties
  39. 1 1
      gradle/wrapper/gradle-wrapper.properties
  40. 0 1
      local-source/.gitignore
  41. 0 30
      local-source/build.gradle
  42. 0 0
      local-source/consumer-rules.pro
  43. 0 21
      local-source/proguard-rules.pro
  44. 0 4
      local-source/src/main/AndroidManifest.xml
  45. 0 91
      local-source/src/main/java/com/norman/webviewup/lib/source/WebViewAssetSource.java
  46. 0 33
      local-source/src/main/java/com/norman/webviewup/lib/source/WebViewFileSource.java
  47. 0 6
      local-source/src/main/java/com/norman/webviewup/lib/source/WebViewPathSource.java
  48. 0 77
      local-source/src/main/java/com/norman/webviewup/lib/source/WebViewSource.java
  49. 0 17
      local-source/src/test/java/com/norman/webviewup/lib/ExampleUnitTest.java
  50. 1 2
      settings.gradle

+ 5 - 2
app/build.gradle

@@ -3,12 +3,12 @@ plugins {
 }
 
 android {
-    compileSdkVersion 30
 
     defaultConfig {
+        compileSdk 33
         applicationId "com.norman.webviewup.demo"
         minSdkVersion 21
-        targetSdkVersion 30
+        targetSdkVersion 33
         versionCode 1
         versionName "1.0"
 
@@ -39,9 +39,12 @@ android {
         sourceCompatibility JavaVersion.VERSION_1_8
         targetCompatibility JavaVersion.VERSION_1_8
     }
+    namespace 'com.norman.webviewup.demo'
 }
 
 dependencies {
     implementation 'androidx.appcompat:appcompat:1.3.0'
     implementation project(":core")
+    implementation project(':download-source')
+
 }

+ 3 - 5
app/src/main/AndroidManifest.xml

@@ -1,10 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.norman.webviewup.demo">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
 
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     <application
         android:name=".MyApp"
@@ -14,6 +11,7 @@
         android:roundIcon="@mipmap/ic_launcher_round"
         android:theme="@style/AppTheme">
         <activity
+            android:exported="true"
             android:name=".MainActivity"
             android:configChanges="orientation|screenSize">
             <intent-filter>

+ 0 - 165
app/src/main/assets/aria_config.xml

@@ -1,165 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<aria>
-  <!--注意,修改该配置文件中的属性会覆盖代码中所设置的属性-->
-
-  <!--Aria框架配置-->
-  <app>
-    <!--是否使用AriaCrashHandler来捕获异常,异常日志保存在:/mnt/sdcard/Android/data/{package_name}/files/log/-->
-    <useAriaCrashHandler value="false"/>
-    <!--设置Aria的日志级别,{@link ALog#LOG_LEVEL_VERBOSE}-->
-    <logLevel value="2"/>
-    <!-- 是否检查网络 true: 检查网络,false: 不检查网络-->
-    <netCheck value="true"/>
-    <!--除非无法使用注解,否则不建议使用广播来接受任务状态,true:使用广播接收任务状态,false:不适用广播接收状态 -->
-    <!-- http://aria.laoyuyu.me/aria_doc/api/use_broadcast.html -->
-    <useBroadcast value="false"/>
-    <!--断网的时候是否重试,true:断网也重试;false:断网不重试,直接走失败的回调-->
-    <notNetRetry value="true"/>
-  </app>
-
-
-  <!--普通下载任务-->
-  <download>
-    <!--设置http下载获取文件大小是否使用Head请求。true:使用head请求,false:使用默认的get请求。默认为false-->
-    <!--只适用于3.8.11以上的版本-->
-    <useHeadRequest value="false"/>
-
-    <!--设置任务最大下载速度,0表示不限速,单位为:kb-->
-    <maxSpeed value="0"/>
-
-    <!--
-      多线程下载是否使用块下载模式,{@code true}使用,{@code false}不使用
-      注意:
-        1、使用分块模式,在读写性能底下的手机上,合并文件需要的时间会更加长;
-        2、优点是使用多线程的块下载,初始化时,文件初始化时将不会预占用对应长度的空间;
-        3、只对新的多线程下载任务有效
-        4、只对多线程的任务有效
-    -->
-    <useBlock value="true"/>
-
-    <!--设置下载线程数,下载线程数不能小于1
-      注意:
-      1、线程下载数改变后,新的下载任务才会生效;
-      2、如果任务大小小于1m,该设置不会生效;
-      3、从3.4.1开始,如果线程数为1,文件初始化时将不再预占用对应长度的空间,下载多少byte,则占多大的空间;
-         对于采用多线程的任务或旧任务,依然采用原来的文件空间占用方式;
-    -->
-    <threadNum value="5"/>
-
-    <!--设置下载队列最大任务数, 默认为2-->
-    <maxTaskNum value="1"/>
-
-    <!--设置下载失败,重试次数,默认为10-->
-    <reTryNum value="5"/>
-
-    <!--设置重试间隔,单位为毫秒,默认2000毫秒-->
-    <reTryInterval value="5000"/>
-
-    <!--设置url连接超时时间,单位为毫秒,默认5000毫秒-->
-    <connectTimeOut value="5000"/>
-
-    <!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
-    <iOTimeOut value="10000"/>
-
-    <!--设置写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢-->
-    <buffSize value="8192"/>
-
-    <!--设置https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名-->
-    <ca name="" path=""/>
-
-    <!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
-    <convertSpeed value="true"/>
-
-    <!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
-    <queueMod value="now"/>
-
-    <!--进度更新更新间隔,默认1000毫秒-->
-    <updateInterval value="1000"/>
-
-  </download>
-
-  <!--普通上传任务-->
-  <upload>
-    <!--设置任务最大上传速度,0表示不限速,单位为:kb-->
-    <maxSpeed value="0"/>
-
-    <!--设置IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
-    <iOTimeOut value="10000"/>
-
-    <!--设置写文件buff大小,该数值大小不能小于2048,数值变小,速度会变慢-->
-    <buffSize value="8192"/>
-
-    <!--是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
-    <convertSpeed value="true"/>
-
-    <!--设置上传队列最大任务数, 默认为2-->
-    <maxTaskNum value="2"/>
-
-    <!--设置上传失败,重试次数,默认为10-->
-    <reTryNum value="3"/>
-
-    <!--设置重试间隔,单位为毫秒-->
-    <reTryInterval value="2000"/>
-
-    <!--设置url连接超时时间,单位为毫秒,默认5000毫秒-->
-    <connectTimeOut value="5000"/>
-
-    <!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
-    <queueMod value="wait"/>
-
-    <!--进度更新更新间隔,默认1000毫秒-->
-    <updateInterval value="1000"/>
-
-  </upload>
-
-  <!-- 下载类组合任务 -->
-  <dGroup>
-
-    <!--组合任务下载队列最大任务数, 默认为2-->
-    <maxTaskNum value="3"/>
-
-    <!--设置下载失败,重试次数,默认为10-->
-    <reTryNum value="5"/>
-
-    <!--设置重试间隔,单位为毫秒,默认2000毫秒-->
-    <reTryInterval value="5000"/>
-
-    <!--执行队列类型,见com.arialyy.aria.core.QueueMod,默认类型为wait-->
-    <queueMod value="wait"/>
-
-    <!--进度更新更新间隔,默认1000毫秒-->
-    <updateInterval value="1000"/>
-    <!--子任务失败时组任务回调stop,默认true false将回调fail-->
-    <subFailAsStop value="true"/>
-
-    <!-- =============================以下为子任务的配置====================================-->
-
-    <!--能同时下载的子任务最大任务数,默认3-->
-    <subMaxTaskNum value="5"/>
-
-    <!--子任务下载失败时的重试次数,默认为5-->
-    <subReTryNum value="5"/>
-
-    <!--子任务下载失败时的重试间隔,单位为毫秒,默认2000毫秒-->
-    <subReTryInterval value="5000"/>
-
-    <!--子任务url连接超时时间,单位为毫秒,默认5000毫秒-->
-    <connectTimeOut value="5000"/>
-
-    <!--子任务IO流读取时间,单位为毫秒,默认20000毫秒,该时间不能少于10000毫秒-->
-    <iOTimeOut value="10000"/>
-
-    <!--子任务写文件buff大小,该数值大小不能小于2048,数值变小,下载速度会变慢-->
-    <buffSize value="8192"/>
-
-    <!--子任务 https ca 证书信息;path 为assets目录下的CA证书完整路径,name 为CA证书名-->
-    <ca name="" path=""/>
-
-    <!--子任务是否需要转换速度单位,转换完成后为:1b/s、1kb/s、1mb/s、1gb/s、1tb/s,如果不需要将返回byte长度-->
-    <convertSpeed value="true"/>
-
-    <!--子任务的最大下载速度,0表示不限速,单位为:kb; -->
-    <maxSpeed value="0"/>
-
-  </dGroup>
-</aria>

+ 14 - 15
app/src/main/java/com/norman/webviewup/demo/MainActivity.java

@@ -3,6 +3,7 @@ package com.norman.webviewup.demo;
 import android.app.Activity;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -15,10 +16,11 @@ import android.widget.Toast;
 import androidx.appcompat.app.AlertDialog;
 
 import com.norman.webviewup.lib.UpgradeCallback;
-import com.norman.webviewup.lib.UpgradeOptions;
 import com.norman.webviewup.lib.WebViewUpgrade;
+import com.norman.webviewup.lib.source.download.UpgradeDownloadSource;
 import com.norman.webviewup.lib.util.ProcessUtils;
 
+import java.io.File;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -28,9 +30,10 @@ import java.util.Map;
 public class MainActivity extends Activity implements UpgradeCallback {
 
 
-    private static final Map<String,List<UpgradeInfo>> UPGRADE_PACKAGE_MAP = new HashMap<>();
+    private static final Map<String, List<UpgradeInfo>> UPGRADE_PACKAGE_MAP = new HashMap<>();
+
     static {
-        UPGRADE_PACKAGE_MAP.put("arm",Arrays.asList(
+        UPGRADE_PACKAGE_MAP.put("arm", Arrays.asList(
                 new UpgradeInfo(
                         "com.google.android.webview",
                         "122.0.6261.64",
@@ -55,14 +58,14 @@ public class MainActivity extends Activity implements UpgradeCallback {
 
         ));
 
-        UPGRADE_PACKAGE_MAP.put("arm64",Arrays.asList(
+        UPGRADE_PACKAGE_MAP.put("arm64", Arrays.asList(
                 new UpgradeInfo(
                         "com.huawei.webview",
                         "14.0.0.331",
                         "https://mirror.ghproxy.com/https://raw.githubusercontent.com/JonaNorman/ShareFile/main/com.huawei.webview_14.0.0.331_arm64-v8a_armeabi-v7a.zip")
         ));
 
-        UPGRADE_PACKAGE_MAP.put("x86",Arrays.asList(
+        UPGRADE_PACKAGE_MAP.put("x86", Arrays.asList(
                 new UpgradeInfo(
                         "com.google.android.webview",
                         "122.0.6261.64",
@@ -137,7 +140,7 @@ public class MainActivity extends Activity implements UpgradeCallback {
     @Override
     public void onUpgradeError(Throwable throwable) {
         Toast.makeText(getApplicationContext(), "webView upgrade fail", Toast.LENGTH_SHORT).show();
-        Log.v("MainActivity","message:" + throwable.getMessage() + "\nstackTrace:" + Log.getStackTraceString(throwable));
+        Log.v("MainActivity", "message:" + throwable.getMessage() + "\nstackTrace:" + Log.getStackTraceString(throwable));
         updateUpgradeWebViewStatus();
     }
 
@@ -161,14 +164,12 @@ public class MainActivity extends Activity implements UpgradeCallback {
                 } else {
                     UpgradeInfo upgradeInfo = upgradeInfoList.get(which);
                     selectUpgradeInfo = upgradeInfo;
-                    UpgradeOptions upgradeOptions = new UpgradeOptions
-                            .Builder(getApplicationContext(),
-                            upgradeInfo.packageName,
+                    UpgradeDownloadSource upgradeDownloadSource = new UpgradeDownloadSource(
+                            getApplicationContext(),
                             upgradeInfo.url,
-                            upgradeInfo.versionName,
-                            new DownloadSinkImpl())
-                            .build();
-                    WebViewUpgrade.upgrade(upgradeOptions);
+                            new File(getApplicationContext().getFilesDir(), upgradeInfo.packageName + "/" + upgradeInfo.versionName + ".apk")
+                    );
+                    WebViewUpgrade.upgrade(upgradeDownloadSource);
                     updateUpgradeWebViewPackageInfo();
                     updateUpgradeWebViewStatus();
                 }
@@ -215,8 +216,6 @@ public class MainActivity extends Activity implements UpgradeCallback {
             upgradeStatusTextView.setText("fail");
         } else if (WebViewUpgrade.isCompleted()) {
             upgradeStatusTextView.setText("complete");
-        } else if (WebViewUpgrade.isInited()) {
-            upgradeStatusTextView.setText("init");
         } else {
             upgradeStatusTextView.setText("");
         }

+ 0 - 3
app/src/main/java/com/norman/webviewup/demo/MyApp.java

@@ -2,13 +2,10 @@ package com.norman.webviewup.demo;
 
 import android.app.Application;
 
-import com.arialyy.aria.core.Aria;
-
 public class MyApp extends Application {
 
     @Override
     public void onCreate() {
         super.onCreate();
-        Aria.init(this);
     }
 }

+ 0 - 26
aria-source/src/androidTest/java/com/norman/webviewup/lib/ExampleInstrumentedTest.java

@@ -1,26 +0,0 @@
-package com.norman.webviewup.lib;
-
-import android.content.Context;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
-    @Test
-    public void useAppContext() {
-        // Context of the app under test.
-        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-        assertEquals("com.norman.webviewup.lib.test", appContext.getPackageName());
-    }
-}

+ 0 - 4
aria-source/src/main/AndroidManifest.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest package="com.norman.webviewup.lib.source">
-
-</manifest>

+ 0 - 160
aria-source/src/main/java/com/norman/webviewup/lib/source/aira/WebViewUrlSource.java

@@ -1,160 +0,0 @@
-package com.norman.webviewup.lib.source.aira;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import com.arialyy.aria.core.Aria;
-import com.arialyy.aria.core.download.DownloadEntity;
-import com.arialyy.aria.core.download.DownloadReceiver;
-import com.arialyy.aria.core.download.DownloadTaskListener;
-import com.arialyy.aria.core.inf.IEntity;
-import com.arialyy.aria.core.task.DownloadTask;
-import com.norman.webviewup.lib.UpgradeDirectory;
-import com.norman.webviewup.lib.source.WebViewPathSource;
-import com.norman.webviewup.lib.util.FileUtils;
-import com.norman.webviewup.lib.util.Md5Utils;
-
-import java.io.File;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-
-public class WebViewUrlSource extends WebViewPathSource implements DownloadTaskListener {
-
-    private final HashSet<DownloadProcessCallback> downloadProcessCallbackHashSet = new HashSet<>();
-
-    private final String url;
-
-    private final String path;
-
-    private final DownloadReceiver downloadReceiver;
-
-    private long taskId;
-
-    private DownloadEntity downloadEntity;
-
-    public WebViewUrlSource(Context context,String url) {
-        this(context,url,null);
-    }
-
-    public WebViewUrlSource(Context context,String url, String path) {
-        this.url = url;
-        if (TextUtils.isEmpty(path)  && !TextUtils.isEmpty(url)) {
-            File apkFile = UpgradeDirectory.getApkFile(context, Md5Utils.getMd5(url));
-            this.path = apkFile.getPath();
-        } else {
-            this.path = path;
-        }
-        this.downloadReceiver = Aria.download(this);
-        this.downloadReceiver.register();
-
-        List<DownloadEntity> downloadEntityList = this.downloadReceiver.getDownloadEntity(url);
-        if (downloadEntityList != null) {
-            for (DownloadEntity entity : downloadEntityList) {
-                if (Objects.equals(entity.getFilePath(), path)) {
-                    downloadEntity = entity;
-                    break;
-                }
-            }
-        }
-        if (downloadEntity != null) {
-            taskId = downloadEntity.getId();
-        }
-    }
-
-
-    @Override
-    protected void onPrepare() {
-        if (!FileUtils.isNotEmptyFile(path) ||downloadEntity == null ||
-                downloadEntity.getState() == IEntity.STATE_CANCEL) {
-            FileUtils.makeDirectory(path);
-            taskId = downloadReceiver
-                    .load(url)
-                    .setFilePath(path)
-                    .ignoreCheckPermissions()
-                    .ignoreFilePathOccupy()
-                    .create();
-            downloadEntity = downloadReceiver.getDownloadEntity(taskId);
-        } else if (downloadEntity.getState() == IEntity.STATE_WAIT
-                || downloadEntity.getState() == IEntity.STATE_OTHER
-                || downloadEntity.getState() == IEntity.STATE_FAIL
-                || downloadEntity.getState() == IEntity.STATE_STOP) {
-            downloadReceiver
-                    .load(taskId)
-                    .ignoreCheckPermissions()
-                    .resume();
-        } else if (downloadEntity.getState() == IEntity.STATE_COMPLETE) {
-            success();
-        } //running
-
-    }
-    
-
-    @Override
-    public void onWait(DownloadTask task) {
-
-    }
-
-    @Override
-    public void onPre(DownloadTask task) {
-        downloadEntity = task.getDownloadEntity();
-
-    }
-
-    @Override
-    public void onTaskPre(DownloadTask task) {
-
-    }
-
-    @Override
-    public void onTaskResume(DownloadTask task) {
-
-    }
-
-    @Override
-    public void onTaskStart(DownloadTask task) {
-
-    }
-
-    @Override
-    public void onTaskStop(DownloadTask task) {
-        downloadEntity = task.getDownloadEntity();
-    }
-
-    @Override
-    public void onTaskCancel(DownloadTask task) {
-        downloadEntity = task.getDownloadEntity();
-    }
-
-    @Override
-    public void onTaskFail(DownloadTask task, Exception e) {
-        error(e);
-    }
-
-    @Override
-    public void onTaskComplete(DownloadTask task) {
-       success();
-    }
-
-    @Override
-    public void onTaskRunning(DownloadTask task) {
-        float percent = task.getPercent() / 100.0f;
-        for (DownloadProcessCallback downloadProcessCallback : downloadProcessCallbackHashSet) {
-            downloadProcessCallback.onProcess(percent);
-        }
-    }
-
-    @Override
-    public void onNoSupportBreakPoint(DownloadTask task) {
-
-    }
-
-    @Override
-    public synchronized String getPath() {
-        return path;
-    }
-
-    public interface DownloadProcessCallback{
-        void onProcess(float percent);
-    }
-}

+ 0 - 17
aria-source/src/test/java/com/norman/webviewup/lib/ExampleUnitTest.java

@@ -1,17 +0,0 @@
-package com.norman.webviewup.lib;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
-    @Test
-    public void addition_isCorrect() {
-        assertEquals(4, 2 + 2);
-    }
-}

+ 3 - 3
build.gradle

@@ -1,17 +1,17 @@
 buildscript {
     repositories {
-        google()
         mavenCentral()
+        google()
     }
     dependencies {
-        classpath "com.android.tools.build:gradle:4.1.3"
+        classpath 'com.android.tools.build:gradle:8.3.1'
     }
 }
 
 allprojects {
     repositories {
-        google()
         mavenCentral()
+        google()
     }
 }
 

+ 3 - 3
core/build.gradle

@@ -3,11 +3,12 @@ plugins {
 }
 
 android {
-    compileSdkVersion 30
+    namespace 'com.norman.webviewup.lib'
 
     defaultConfig {
+        compileSdk 33
         minSdkVersion 21
-        targetSdkVersion 30
+        targetSdkVersion 33
         consumerProguardFiles "consumer-rules.pro"
     }
 
@@ -25,5 +26,4 @@ android {
 
 dependencies {
     implementation 'androidx.appcompat:appcompat:1.6.1'
-    implementation project(':local-source')
 }

+ 1 - 1
core/src/main/AndroidManifest.xml

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
-<manifest package="com.norman.webviewup.lib">
+<manifest>
 
 </manifest>

+ 0 - 2
core/src/main/java/com/norman/webviewup/lib/UpgradeCallback.java

@@ -1,9 +1,7 @@
 package com.norman.webviewup.lib;
 
 public interface UpgradeCallback {
-
     void onUpgradeProcess(float percent);
-
     void onUpgradeComplete();
 
     void onUpgradeError(Throwable throwable);

+ 0 - 38
core/src/main/java/com/norman/webviewup/lib/UpgradeDirectory.java

@@ -1,38 +0,0 @@
-package com.norman.webviewup.lib;
-
-import android.content.Context;
-
-import com.norman.webviewup.lib.util.FileUtils;
-import com.norman.webviewup.lib.util.Md5Utils;
-
-import java.io.File;
-
-public class UpgradeDirectory {
-
-    private static final String UPGRADE_DIRECTORY_NAME = "WebViewUpgrade";
-
-    private static final String APK_DIRECTORY_NAME = "apk";
-
-    public static File getRoot(Context context) {
-        return new File(context.getFilesDir(),
-                UPGRADE_DIRECTORY_NAME);
-    }
-
-
-    public static File createDirectory(Context context, String child) {
-        File childDir = new File(getRoot(context), child);
-        FileUtils.makeDirectory(childDir);
-        return childDir;
-    }
-
-    public static File getApkDirectory(Context context) {
-        return createDirectory(context, APK_DIRECTORY_NAME);
-    }
-
-    public static File getApkFile(Context context, String name) {
-        File apkFile = new File(getApkDirectory(context), name);
-        FileUtils.createFile(apkFile);
-        return apkFile;
-    }
-
-}

+ 0 - 62
core/src/main/java/com/norman/webviewup/lib/UpgradeOptions.java

@@ -1,62 +0,0 @@
-package com.norman.webviewup.lib;
-
-import android.app.Application;
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import com.norman.webviewup.lib.download.DownloadSink;
-
-public class UpgradeOptions {
-
-    public final DownloadSink downloaderSink;
-    public final Application context;
-    public final String url;
-
-
-    private UpgradeOptions(UpgradeOptions.Builder builder) {
-        this.context = builder.context;
-        this.url = builder.url;
-    }
-
-    public static class Builder {
-        Application context;
-        String url;
-
-
-        public Builder(@NonNull Context context, @NonNull String packageName, @NonNull String url, @NonNull String version, @NonNull DownloadSink downloaderSink) {
-            this.downloaderSink = downloaderSink;
-            this.context = (Application) context.getApplicationContext();
-            this.url = url;
-        }
-
-        private Builder(UpgradeOptions options) {
-            downloaderSink = options.downloaderSink;
-            context = options.context;
-            url = options.url;
-        }
-
-
-        public Builder setDownloaderSink(DownloadSink downloaderSink) {
-            this.downloaderSink = downloaderSink;
-            return this;
-        }
-
-        public Builder setContext(Context context) {
-            this.context = (Application) context.getApplicationContext();
-            return this;
-        }
-
-
-        public Builder setUrl(String url) {
-            this.url = url;
-            return this;
-        }
-
-        public UpgradeOptions build() {
-            return new UpgradeOptions(this);
-        }
-    }
-
-
-}

+ 148 - 0
core/src/main/java/com/norman/webviewup/lib/WebViewReplace.java

@@ -0,0 +1,148 @@
+package com.norman.webviewup.lib;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.webkit.WebView;
+
+import com.norman.webviewup.lib.hook.PackageManagerServiceHook;
+import com.norman.webviewup.lib.hook.WebViewUpdateServiceHook;
+import com.norman.webviewup.lib.reflect.RuntimeAccess;
+import com.norman.webviewup.lib.service.interfaces.IServiceManager;
+import com.norman.webviewup.lib.service.interfaces.IWebViewFactory;
+import com.norman.webviewup.lib.service.interfaces.IWebViewUpdateService;
+import com.norman.webviewup.lib.util.FileUtils;
+
+
+public class WebViewReplace {
+
+    private static PackageInfo SYSTEM_WEB_VIEW_PACKAGE_INFO;
+
+    private static PackageInfo REPLACE_WEB_VIEW_PACKAGE_INFO;
+
+    public synchronized static void replace(Context context, String apkPath) throws WebViewReplaceException {
+        PackageManagerServiceHook managerHook = null;
+        WebViewUpdateServiceHook updateServiceHook = null;
+        try {
+            if (context == null) {
+                throw new WebViewReplaceException("context is null");
+            }
+            if (!FileUtils.existFile(apkPath)) {
+                throw new WebViewReplaceException("apkFile is not exist");
+            }
+
+            if (Looper.myLooper() != Looper.getMainLooper()) {
+                throw new WebViewReplaceException("replace webView only in main thread");
+            }
+            PackageInfo packageInfo = context.getPackageManager()
+                    .getPackageArchiveInfo(apkPath, 0);
+
+            if (packageInfo == null) {
+                throw new WebViewReplaceException(apkPath + " is not apk");
+            }
+
+            int sdkVersion = Build.VERSION.SDK_INT;
+            ApplicationInfo applicationInfo = packageInfo.applicationInfo;
+
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                if (sdkVersion < applicationInfo.minSdkVersion) {
+                    throw new WebViewReplaceException("current system version " + sdkVersion + " is smaller than the minimum version " + applicationInfo.minSdkVersion + " required by the apk  " + apkPath);
+                }
+            }
+            managerHook = new PackageManagerServiceHook(context, packageInfo.packageName, apkPath);
+            updateServiceHook = new WebViewUpdateServiceHook(context, packageInfo.packageName);
+            managerHook.hook();
+            updateServiceHook.hook();
+            if (SYSTEM_WEB_VIEW_PACKAGE_INFO == null) {
+                SYSTEM_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
+            }
+            checkWebView(context);
+            REPLACE_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
+        } catch (Throwable throwable) {
+            if (throwable instanceof WebViewReplaceException) {
+                throw throwable;
+            } else {
+                String message = throwable.getMessage();
+                if (TextUtils.isEmpty(message)) {
+                    message = "";
+                }
+                throw new WebViewReplaceException(message, throwable);
+            }
+        } finally {
+            try {
+                if (managerHook != null) {
+                    managerHook.restore();
+                }
+                if (updateServiceHook != null) {
+                    updateServiceHook.restore();
+                }
+            } catch (Throwable throwable) {
+                String message = throwable.getMessage();
+                if (TextUtils.isEmpty(message)) {
+                    message = "";
+                }
+                throw new WebViewReplaceException(message, throwable);
+            }
+        }
+    }
+
+    public synchronized static String getSystemWebViewPackageName() {
+        if (SYSTEM_WEB_VIEW_PACKAGE_INFO == null) {
+            SYSTEM_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
+        }
+        return SYSTEM_WEB_VIEW_PACKAGE_INFO != null ? SYSTEM_WEB_VIEW_PACKAGE_INFO.packageName : null;
+    }
+
+    public synchronized static String getSystemWebViewPackageVersion() {
+        if (SYSTEM_WEB_VIEW_PACKAGE_INFO == null) {
+            SYSTEM_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
+        }
+        return SYSTEM_WEB_VIEW_PACKAGE_INFO != null ? SYSTEM_WEB_VIEW_PACKAGE_INFO.versionName : null;
+    }
+
+    public synchronized static String getReplaceWebViewPackageName() {
+        return REPLACE_WEB_VIEW_PACKAGE_INFO != null ? REPLACE_WEB_VIEW_PACKAGE_INFO.packageName : null;
+    }
+
+    public synchronized static String getReplaceWebViewVersion() {
+        return REPLACE_WEB_VIEW_PACKAGE_INFO != null ? REPLACE_WEB_VIEW_PACKAGE_INFO.versionName : null;
+    }
+
+
+    private static void checkWebView(Context context) throws WebViewReplaceException {
+        IWebViewFactory webViewFactory = RuntimeAccess.staticAccess(IWebViewFactory.class);
+        Object providerInstance = webViewFactory.getProviderInstance();
+        if (providerInstance != null) {
+            throw new WebViewReplaceException("WebView only can replace before System WebView init");
+        }
+        new WebView(context);
+    }
+
+
+    private static PackageInfo loadCurrentWebViewPackageInfo() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            try {
+                return WebView.getCurrentWebViewPackage();
+            } catch (Throwable ignore) {
+
+            }
+        }
+        try {
+            IServiceManager serviceManager = RuntimeAccess.staticAccess(IServiceManager.class);
+            IBinder binder = serviceManager.getService(IWebViewUpdateService.SERVICE);
+            IWebViewUpdateService service = RuntimeAccess.staticAccess(IWebViewUpdateService.class);
+            IInterface iInterface = service.asInterface(binder);
+            service = RuntimeAccess.objectAccess(IWebViewUpdateService.class, iInterface);
+            return service.getCurrentWebViewPackage();
+        } catch (Throwable ignore) {
+        }
+        return null;
+    }
+
+
+}

+ 15 - 0
core/src/main/java/com/norman/webviewup/lib/WebViewReplaceException.java

@@ -0,0 +1,15 @@
+package com.norman.webviewup.lib;
+
+public class WebViewReplaceException extends Exception {
+
+    public WebViewReplaceException(String message) {
+        super(message);
+    }
+
+    public WebViewReplaceException(String message, Throwable cause) {
+        super(message, cause);
+        setStackTrace(cause.getStackTrace());
+    }
+
+
+}

+ 68 - 367
core/src/main/java/com/norman/webviewup/lib/WebViewUpgrade.java

@@ -1,45 +1,18 @@
 package com.norman.webviewup.lib;
 
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Looper;
-import android.text.TextUtils;
-import android.webkit.WebView;
-
-import com.norman.webviewup.lib.download.DownloadAction;
-import com.norman.webviewup.lib.download.DownloadSink;
-import com.norman.webviewup.lib.hook.PackageManagerHook;
-import com.norman.webviewup.lib.hook.WebViewUpdateServiceHook;
-import com.norman.webviewup.lib.reflect.RuntimeAccess;
-import com.norman.webviewup.lib.service.interfaces.IServiceManager;
-import com.norman.webviewup.lib.service.interfaces.IWebViewFactory;
-import com.norman.webviewup.lib.service.interfaces.IWebViewUpdateService;
-import com.norman.webviewup.lib.source.WebViewFileSource;
-import com.norman.webviewup.lib.util.ApkUtils;
-import com.norman.webviewup.lib.util.FileUtils;
-import com.norman.webviewup.lib.util.Md5Utils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
+import com.norman.webviewup.lib.source.UpgradePathSource;
+import com.norman.webviewup.lib.source.UpgradeSource;
+import com.norman.webviewup.lib.util.HandlerUtils;
 
-public class WebViewUpgrade {
+import java.util.HashSet;
+import java.util.Set;
 
-    private static final List<UpgradeCallback> UPGRADE_CALLBACK_LIST = new ArrayList<>();
-    private static final String UPGRADE_DIRECTORY = "WebViewUpgrade";
+public class WebViewUpgrade {
 
+    private static final Set<UpgradeCallback> UPGRADE_CALLBACK_SET = new HashSet<>();
 
-    private static final int STATUS_UNINIT = 0;
+    private static final int STATUS_NEW = 0;
 
     private static final int STATUS_RUNNING = 1;
 
@@ -47,35 +20,13 @@ public class WebViewUpgrade {
 
     private static final int STATUS_COMPLETE = 3;
 
-    private static UpgradeOptions UPGRADE_OPTIONS;
-
-    private static int UPGRADE_STATUS = STATUS_UNINIT;
-
+    private static int UPGRADE_STATUS = STATUS_NEW;
 
     private static float UPGRADE_PROCESS;
 
-    private static PackageInfo SYSTEM_WEB_VIEW_PACKAGE_INFO;
-
-    private static PackageInfo UPGRADE_WEB_VIEW_PACKAGE_INFO;
 
     private static Throwable UPGRADE_THROWABLE;
 
-    private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
-
-
-    public synchronized static void addUpgradeCallback(UpgradeCallback upgradeCallback) {
-        if (upgradeCallback == null) return;
-        if (UPGRADE_CALLBACK_LIST.contains(upgradeCallback)) return;
-        UPGRADE_CALLBACK_LIST.add(upgradeCallback);
-    }
-
-    public synchronized static void removeUpgradeCallback(UpgradeCallback upgradeCallback) {
-        if (upgradeCallback == null) return;
-        if (!UPGRADE_CALLBACK_LIST.contains(upgradeCallback)) return;
-        UPGRADE_CALLBACK_LIST.remove(upgradeCallback);
-    }
-
-
     public synchronized static boolean isProcessing() {
         return UPGRADE_STATUS == STATUS_RUNNING;
     }
@@ -88,10 +39,6 @@ public class WebViewUpgrade {
         return UPGRADE_STATUS == STATUS_FAIL;
     }
 
-    public synchronized static boolean isInited() {
-        return UPGRADE_STATUS != STATUS_UNINIT;
-    }
-
     public synchronized static Throwable getUpgradeError() {
         return UPGRADE_THROWABLE;
     }
@@ -100,251 +47,71 @@ public class WebViewUpgrade {
         return UPGRADE_PROCESS;
     }
 
-    public synchronized static void upgrade(UpgradeOptions options) {
-        try {
-            if (UPGRADE_STATUS == STATUS_RUNNING || UPGRADE_STATUS == STATUS_COMPLETE) {
-                return;
-            }
-            UPGRADE_OPTIONS = options;
-            UPGRADE_STATUS = STATUS_RUNNING;
-            UPGRADE_PROCESS = 0;
-            UPGRADE_THROWABLE = null;
-            HandlerThread upgradeThread = new HandlerThread("WebViewUpgrade");
-            upgradeThread.start();
-            Handler upgradeHandler = new Handler(upgradeThread.getLooper());
-            upgradeHandler.post(new UPGRADE_ACTION(upgradeHandler));
-        } catch (Throwable throwable) {
-            callErrorCallback(throwable);
-        }
+    public synchronized static void addUpgradeCallback(UpgradeCallback upgradeCallback) {
+        UPGRADE_CALLBACK_SET.add(upgradeCallback);
     }
 
-    static class UPGRADE_ACTION implements Runnable {
-
-        private final Handler handler;
-        private Context context;
-
-        private String apkPathKey;
-
-        private String libsPathKey;
-
-        private String apkPath;
-        private String libsDir;
-
-
-        private SharedPreferences sharedPreferences;
-
-        public UPGRADE_ACTION(Handler handler) {
-            this.handler = handler;
-        }
+    public synchronized static void removeUpgradeCallback(UpgradeCallback upgradeCallback) {
+        UPGRADE_CALLBACK_SET.remove(upgradeCallback);
+    }
 
-        @Override
-        public void run() {
-            try {
-                UpgradeOptions options = UPGRADE_OPTIONS;
-                DownloadSink downloaderSink = options.downloaderSink;
-                context = options.context;
-                String apkUrl = options.url;
-                String md5 = Md5Utils.getMd5(apkUrl);
-                if (TextUtils.isEmpty(md5)) {
-                    throw new RuntimeException("get url md5 is empty, url is " + apkUrl);
-                }
-                apkPathKey = md5 + "_apk";
-                libsPathKey = md5 + "_libs";
-                sharedPreferences = context
-                        .getSharedPreferences(
-                                UPGRADE_DIRECTORY,
-                                Context.MODE_PRIVATE);
-                apkPath = sharedPreferences
-                        .getString(apkPathKey, null);
-                libsDir = sharedPreferences
-                        .getString(libsPathKey, null);
-
-                if (!TextUtils.isEmpty(apkPath)
-                        && new File(apkPath).exists() &&
-                        !TextUtils.isEmpty(libsDir)
-                        && new File(libsDir).exists()) {
-                    upgradeWebView();
-                } else {
-                    String downloadPath = new File(context.getFilesDir(),
-                            UPGRADE_DIRECTORY
-                                    + "/tmp/" + md5 + ".download").getAbsolutePath();
-                    DownloadAction downloadAction = downloaderSink.createDownload(apkUrl, downloadPath);
-                    if (downloadAction.isCompleted()) {
-                        installApk(downloadPath);
-                        downloadAction.delete();
-                        upgradeWebView();
-                    } else {
-                        downloadAction.addCallback(new DownloadAction.Callback() {
-
-                            @Override
-                            public void onComplete(String path) {
-                                handler.post(() -> {
-                                    try {
-                                        installApk(path);
-                                        downloadAction.delete();
-                                        upgradeWebView();
-                                    } catch (Throwable throwable) {
-                                        callErrorCallback(throwable);
-                                        handler.getLooper().quit();
-                                    }
-                                });
-                            }
-
-                            @Override
-                            public void onFail(Throwable throwable) {
-                                callErrorCallback(throwable);
-                                handler.getLooper().quit();
-                            }
-
-                            @Override
-                            public void onProcess(float percent) {
-                                callProcessCallback(percent * 0.90f);
-                            }
-                        });
-                    }
-                    downloadAction.start();
-                }
-            } catch (Throwable throwable) {
-                callErrorCallback(throwable);
-                handler.getLooper().quit();
-            }
-        }
+    public static String getSystemWebViewPackageName() {
+        return WebViewReplace.getSystemWebViewPackageName();
+    }
 
+    public static String getSystemWebViewPackageVersion() {
+        return WebViewReplace.getSystemWebViewPackageVersion();
+    }
 
-        private void upgradeWebView() {
-            callProcessCallback(0.95f);
-            replaceWebViewProvider(context,
-                    apkPath,
-                    libsDir);
-            handler.getLooper().quit();
-        }
+    public static String getUpgradeWebViewPackageName() {
+        return WebViewReplace.getReplaceWebViewPackageName();
+    }
 
-        private void installApk(String downloadFilePath) {
-            File downloadFile = new File(downloadFilePath);
-            File downloadDirectory = downloadFile.getParentFile();
-            String downloadName = downloadFile.getName();
-            int dotIndex = downloadName.indexOf(".");
-            String tempApkPath;
-            if (dotIndex >= 0) {
-                tempApkPath = new File(downloadDirectory, downloadName.substring(0, dotIndex) + ".apk").getAbsolutePath();
-            } else {
-                tempApkPath = new File(downloadDirectory, downloadName + ".apk").getAbsolutePath();
-            }
-            PackageInfo packageInfo = ApkUtils.extractApk(context, downloadFilePath, tempApkPath);
-            callProcessCallback(0.92f);
-
-
-
-            apkPath = new File(context.getFilesDir(),
-                    UPGRADE_DIRECTORY
-                            + "/" + packageInfo.packageName + "/" + packageInfo.versionName + ".apk").getAbsolutePath();
-            libsDir = new File(context.getFilesDir(),
-                    UPGRADE_DIRECTORY
-                            + "/" + packageInfo.packageName + "/" + packageInfo.versionName + "/libs").getAbsolutePath();
-
-            FileUtils.moveFile(new File(tempApkPath),new File(apkPath));
-            ApkUtils.extractNativeLibrary(apkPath, libsDir);
-            callProcessCallback(0.94f);
-            sharedPreferences
-                    .edit()
-                    .putString(apkPathKey, apkPath)
-                    .commit();
-            sharedPreferences
-                    .edit()
-                    .putString(libsPathKey, libsDir)
-                    .commit();
-        }
+    public static String getUpgradeWebViewVersion() {
+        return WebViewReplace.getReplaceWebViewVersion();
     }
 
 
-    private static void replaceWebViewProvider(Context context,
-                                               String apkPath,
-                                               String soLibDir) {
-        PackageManagerHook managerHook = null;
-        WebViewUpdateServiceHook updateServiceHook = null;
+    public synchronized static void upgrade(UpgradeSource webViewSource) {
         try {
-            callProcessCallback(0.96f);
-            PackageInfo packageInfo = context.getPackageManager()
-                    .getPackageArchiveInfo(apkPath, 0);
-
-            if (packageInfo == null) {
-                throw new NullPointerException("path: " + apkPath + " is not apk");
+            if (UPGRADE_STATUS == STATUS_RUNNING || UPGRADE_STATUS == STATUS_COMPLETE) {
+                return;
             }
+            UPGRADE_STATUS = STATUS_RUNNING;
+            UPGRADE_THROWABLE = null;
+            UPGRADE_PROCESS = 0;
 
-            int sdkVersion = Build.VERSION.SDK_INT;
-            ApplicationInfo applicationInfo = packageInfo.applicationInfo;
-
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-                if (sdkVersion < applicationInfo.minSdkVersion) {
-                    throw new RuntimeException("The current system version " + sdkVersion + " is smaller than the minimum version " + applicationInfo.minSdkVersion + "required by the apk  " + apkPath);
-                }
+            if (!(webViewSource instanceof UpgradePathSource)) {
+                return;
             }
-            managerHook = new PackageManagerHook(context, packageInfo.packageName, apkPath, soLibDir);
-            updateServiceHook = new WebViewUpdateServiceHook(context, packageInfo.packageName);
-            managerHook.hook();
-            callProcessCallback(0.97f);
-            updateServiceHook.hook();
-            callProcessCallback(0.98f);
-
-            Object lock = new Object();
-            AtomicBoolean loadOver = new AtomicBoolean();
-            AtomicReference<Throwable> throwableReference = new AtomicReference<>(null);
-            MAIN_HANDLER.post(() -> {
-                try {
-                    synchronized (WebViewUpgrade.class) {
-                        if (SYSTEM_WEB_VIEW_PACKAGE_INFO == null) {
-                            SYSTEM_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
+            UpgradePathSource webViewPathSource = (UpgradePathSource) webViewSource;
+            webViewPathSource.prepare(new UpgradeSource.OnPrepareCallback() {
+                @Override
+                public void onPrepareSuccess(UpgradeSource webViewSource) {
+                    HandlerUtils.runInMainThread(() -> {
+                        try {
+                            WebViewReplace.replace(webViewSource.getContext(), webViewPathSource.getPath());
+                            callProcessCallback(1.0f);
+                            callCompleteCallback();
+                        } catch (WebViewReplaceException e) {
+                            callErrorCallback(e);
                         }
-                    }
-                    checkWebView();
-                    new WebView(context);
-                    synchronized (WebViewUpgrade.class) {
-                        UPGRADE_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
-                    }
-                } catch (Throwable throwable) {
-                    throwableReference.set(throwable);
-                } finally {
-                    synchronized (lock) {
-                        loadOver.set(true);
-                        lock.notifyAll();
-                    }
+                    });
                 }
-            });
-            synchronized (lock) {
-                long startTime = System.currentTimeMillis();
-                while (!loadOver.get()) {
-                    try {
-                        lock.wait(100);
-                    } catch (InterruptedException ignore) {
-                    }
-                    if ((System.currentTimeMillis() - startTime) > 5000) {
-                        throwableReference.set(new RuntimeException("webView load timeOut"));
-                        break;
-                    }
+
+                @Override
+                public void onPrepareProcess(UpgradeSource webViewSource, float percent) {
+                    callProcessCallback(percent * 0.99f);
                 }
-            }
-            Throwable throwable = throwableReference.get();
-            if (throwable != null) {
-                throw new RuntimeException(throwable);
-            }
-            callProcessCallback(1.0f);
-            callCompleteCallback();
-        } finally {
-            if (managerHook != null) {
-                managerHook.restore();
-            }
-            if (updateServiceHook != null) {
-                updateServiceHook.restore();
-            }
 
-        }
-    }
+                @Override
+                public void onPrepareError(UpgradeSource webViewSource, Throwable throwable) {
+                    callErrorCallback(throwable);
+                }
+            });
 
-    private static void checkWebView() {
-        IWebViewFactory webViewFactory = RuntimeAccess.staticAccess(IWebViewFactory.class);
-        Object providerInstance = webViewFactory.getProviderInstance();
-        if (providerInstance != null) {
-            throw new IllegalStateException("WebViewProvider has been created, and the upgrade function can only be used before the webview is instantiated");
+        } catch (Throwable throwable) {
+            callErrorCallback(throwable);
         }
     }
 
@@ -353,98 +120,32 @@ public class WebViewUpgrade {
         synchronized (WebViewUpgrade.class) {
             UPGRADE_STATUS = STATUS_FAIL;
             UPGRADE_THROWABLE = throwable;
-        }
-        runInMainThread(() -> {
-            synchronized (WebViewUpgrade.class) {
-                for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_LIST) {
-                    upgradeCallback.onUpgradeError(throwable);
-                }
+            for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_SET) {
+                if (upgradeCallback == null) continue;
+                HandlerUtils.runInMainThread(() -> upgradeCallback.onUpgradeError(throwable));
             }
-        });
-    }
-
-    private static void callProcessCallback(float percent) {
-        synchronized (WebViewUpgrade.class) {
-            UPGRADE_PROCESS = percent;
         }
-        runInMainThread(() -> {
-            synchronized (WebViewUpgrade.class) {
-                for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_LIST) {
-                    upgradeCallback.onUpgradeProcess(percent);
-                }
-            }
-        });
     }
 
     private static void callCompleteCallback() {
         synchronized (WebViewUpgrade.class) {
             UPGRADE_STATUS = STATUS_COMPLETE;
-        }
-        runInMainThread(() -> {
-            synchronized (WebViewUpgrade.class) {
-                for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_LIST) {
-                    upgradeCallback.onUpgradeComplete();
-                }
+            for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_SET) {
+                if (upgradeCallback == null) continue;
+                HandlerUtils.runInMainThread(upgradeCallback::onUpgradeComplete);
             }
-        });
-    }
-
-
-    private static void runInMainThread(Runnable runnable) {
-        if (Looper.myLooper() == Looper.getMainLooper()) {
-            runnable.run();
-        } else {
-            MAIN_HANDLER.post(runnable);
-        }
-    }
-
-    public synchronized static String getSystemWebViewPackageName() {
-        if (SYSTEM_WEB_VIEW_PACKAGE_INFO == null) {
-            SYSTEM_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
-        }
-        return SYSTEM_WEB_VIEW_PACKAGE_INFO != null ? SYSTEM_WEB_VIEW_PACKAGE_INFO.packageName : null;
-    }
-
-    public synchronized static String getSystemWebViewPackageVersion() {
-        if (SYSTEM_WEB_VIEW_PACKAGE_INFO == null) {
-            SYSTEM_WEB_VIEW_PACKAGE_INFO = loadCurrentWebViewPackageInfo();
         }
-        return SYSTEM_WEB_VIEW_PACKAGE_INFO != null ? SYSTEM_WEB_VIEW_PACKAGE_INFO.versionName : null;
     }
 
-    public synchronized static String getUpgradeWebViewPackageName() {
-        return UPGRADE_WEB_VIEW_PACKAGE_INFO != null ? UPGRADE_WEB_VIEW_PACKAGE_INFO.packageName : null;
-    }
-
-    public synchronized static String getUpgradeWebViewVersion() {
-        return UPGRADE_WEB_VIEW_PACKAGE_INFO != null ? UPGRADE_WEB_VIEW_PACKAGE_INFO.versionName : null;
-    }
-
-    private static PackageInfo loadCurrentWebViewPackageInfo() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-            try {
-                return WebView.getCurrentWebViewPackage();
-            } catch (Throwable ignore) {
-
+    private static void callProcessCallback(float percent) {
+        synchronized (WebViewUpgrade.class) {
+            UPGRADE_PROCESS = percent;
+            for (UpgradeCallback upgradeCallback : UPGRADE_CALLBACK_SET) {
+                if (upgradeCallback == null) continue;
+                HandlerUtils.runInMainThread(() -> upgradeCallback.onUpgradeProcess(percent));
             }
         }
-        try {
-            IServiceManager serviceManager = RuntimeAccess.staticAccess(IServiceManager.class);
-            IBinder binder = serviceManager.getService(IWebViewUpdateService.SERVICE);
-            IWebViewUpdateService service = RuntimeAccess.staticAccess(IWebViewUpdateService.class);
-            IInterface iInterface = service.asInterface(binder);
-            service = RuntimeAccess.objectAccess(IWebViewUpdateService.class, iInterface);
-            PackageInfo packageInfo = service.getCurrentWebViewPackage();
-            return packageInfo;
-        } catch (Throwable ignore) {
-        }
-        return null;
     }
 
-    public  class FileCache {
 
-        public  static File getRootDirectory(Context context){
-            return context.getFilesDir()+""
-        }
-    }
 }

+ 0 - 40
core/src/main/java/com/norman/webviewup/lib/download/DownloadAction.java

@@ -1,40 +0,0 @@
-package com.norman.webviewup.lib.download;
-
-public interface DownloadAction {
-
-    String getUrl();
-
-    void start();
-
-    void stop();
-
-    void delete();
-
-    boolean isCompleted();
-
-    boolean isProcessing();
-
-
-    void addCallback(Callback callback);
-
-    void removeCallback(Callback callback);
-
-    interface Callback {
-
-        default void onStart() {
-
-        }
-
-        default void onProcess(float percent) {
-
-        }
-
-        default void onComplete(String path) {
-
-        }
-
-        default void onFail(Throwable throwable) {
-
-        }
-    }
-}

+ 0 - 7
core/src/main/java/com/norman/webviewup/lib/download/DownloadSink.java

@@ -1,7 +0,0 @@
-package com.norman.webviewup.lib.download;
-
-public interface DownloadSink {
-
-    DownloadAction createDownload(String url,
-                                  String path);
-}

+ 39 - 21
core/src/main/java/com/norman/webviewup/lib/hook/PackageManagerHook.java → core/src/main/java/com/norman/webviewup/lib/hook/PackageManagerServiceHook.java

@@ -27,9 +27,12 @@ import com.norman.webviewup.lib.util.ProcessUtils;
 import java.io.File;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.Map;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
 
-public class PackageManagerHook extends BinderHook {
+public class PackageManagerServiceHook extends BinderHook {
 
     private final Context context;
 
@@ -37,19 +40,15 @@ public class PackageManagerHook extends BinderHook {
 
     private final String apkPath;
 
-    private final String soPath;
-
     private Map<String, IBinder> binderCacheMap;
 
 
-    public PackageManagerHook(@NonNull Context context,
-                              @NonNull String packageName,
-                              @NonNull String apkPath,
-                              @NonNull String soPath) {
+    public PackageManagerServiceHook(@NonNull Context context,
+                                     @NonNull String packageName,
+                                     @NonNull String apkPath) {
         this.context = context;
         this.webViewPackageName = packageName;
         this.apkPath = apkPath;
-        this.soPath = soPath;
     }
 
     private final PackageManagerProxy proxy = new PackageManagerProxy() {
@@ -74,35 +73,54 @@ public class PackageManagerHook extends BinderHook {
             if (packageName.equals(webViewPackageName)) {
                 PackageInfo packageInfo = context.getPackageManager().getPackageArchiveInfo(apkPath, flags);
                 if (packageInfo == null) {
-                    flags &=~ PackageManager.GET_SIGNATURES;
+                    flags &= ~PackageManager.GET_SIGNATURES;
                     packageInfo = context.getPackageManager().getPackageArchiveInfo(apkPath, flags);
                 }
                 boolean is64Bit = ProcessUtils.is64Bit();
                 String[] supportBitAbis = is64Bit ? Build.SUPPORTED_64_BIT_ABIS : Build.SUPPORTED_32_BIT_ABIS;
                 Arrays.sort(supportBitAbis, Collections.reverseOrder());
-                File nativeLibraryDir = null;
-                String cpuAbi= null;
-                for (String supportBitAbi : supportBitAbis) {
-                    File file = new File(soPath + "/" + supportBitAbi);
-                    File[] childFile = file.listFiles();
-                    if (childFile != null && childFile.length > 0) {
-                        nativeLibraryDir = file;
-                        cpuAbi = supportBitAbi;
-                        break;
+                String nativeLibraryDir = null;
+                String cpuAbi = null;
+                ZipFile zipFile;
+                try {
+                    zipFile = new ZipFile(new File(apkPath));
+                    Enumeration<? extends ZipEntry> entries = zipFile.entries();
+                    while (entries.hasMoreElements()) {
+                        ZipEntry entry = entries.nextElement();
+                        String entryName = entry.getName();
+                        if (entryName.contains("../") || entry.isDirectory()) {
+                            continue;
+                        }
+                        if (!entryName.startsWith("lib/") && !entryName.endsWith(".so")) {
+                            continue;
+                        }
+                        String[] split = entry.getName().split("/");
+                        if (split.length < 2) {
+                            continue;
+                        }
+                        String abi = split[1];
+                        if (Arrays.binarySearch(supportBitAbis, abi) >= 0) {
+                            nativeLibraryDir = apkPath + "!/lib/" + abi;
+                            cpuAbi = abi;
+                            break;
+                        }
                     }
+                } catch (Throwable ignore) {
+
                 }
+
                 if (nativeLibraryDir == null) {
                     throw new NullPointerException("unable to find supported abis "
                             + Arrays.toString(supportBitAbis)
                             + " in apk " + apkPath);
                 }
                 try {
-                    IApplicationInfo iApplicationInfo = RuntimeAccess.objectAccess(IApplicationInfo.class,packageInfo.applicationInfo);
+                    IApplicationInfo iApplicationInfo = RuntimeAccess.objectAccess(IApplicationInfo.class, packageInfo.applicationInfo);
                     iApplicationInfo.setPrimaryCpuAbi(cpuAbi);
-                }catch (Throwable ignore){
+                } catch (Throwable ignore) {
 
                 }
-                packageInfo.applicationInfo.nativeLibraryDir = nativeLibraryDir.getAbsolutePath();
+                packageInfo.applicationInfo.nativeLibraryDir = nativeLibraryDir;
                 if (TextUtils.isEmpty(packageInfo.applicationInfo.sourceDir)) {
                     packageInfo.applicationInfo.sourceDir = apkPath;
                 }

+ 0 - 1
core/src/main/java/com/norman/webviewup/lib/hook/WebViewUpdateServiceHook.java

@@ -21,7 +21,6 @@ public class WebViewUpdateServiceHook extends BinderHook {
 
 
     private final Context context;
-
     private final String webViewPackageName;
 
     public WebViewUpdateServiceHook(Context context, String packageName) {

+ 96 - 0
core/src/main/java/com/norman/webviewup/lib/source/UpgradeAssetSource.java

@@ -0,0 +1,96 @@
+package com.norman.webviewup.lib.source;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+
+import androidx.annotation.NonNull;
+
+import com.norman.webviewup.lib.util.FileUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+public class UpgradeAssetSource extends UpgradePathSource {
+
+    private final String assetName;
+    private final String path;
+
+
+    private final boolean mainThread;
+
+    public UpgradeAssetSource(Context context, @NonNull String assetName, @NonNull File file) {
+        this(context, assetName, file, false);
+    }
+
+    public UpgradeAssetSource(Context context, @NonNull String assetName, @NonNull File file, boolean mainThread) {
+        super(context);
+        this.assetName = assetName;
+        this.path = file.getPath();
+        this.mainThread = mainThread;
+    }
+
+    @Override
+    public String getPath() {
+        return path;
+    }
+
+    private final Runnable copyAssetRunnable = new Runnable() {
+        @Override
+        public void run() {
+            FileOutputStream outputStream = null;
+            FileInputStream inputStream = null;
+            try {
+                FileUtils.createFile(path);
+                outputStream = new FileOutputStream(path);
+                FileChannel dstChannel = outputStream.getChannel();
+                AssetManager assetManager = getContext().getAssets();
+                AssetFileDescriptor assetFileDescriptor = assetManager.openFd(assetName);
+                inputStream = assetFileDescriptor.createInputStream();
+                FileChannel fileChannel = inputStream.getChannel();
+                long startOffset = assetFileDescriptor.getStartOffset();
+                long declaredLength = assetFileDescriptor.getDeclaredLength();
+                int size = 100;
+                long partSize = (long) Math.ceil(declaredLength * 1.0 / size);
+                long position = startOffset;
+                for (int i = 0; i < size; i++) {
+                    long count = i != size - 1 ? partSize : declaredLength - i * partSize;
+                    fileChannel.transferTo(position, count, dstChannel);
+                    process(i * 1.0f / size);
+                    position = position + count;
+                }
+                success();
+            } catch (Throwable e) {
+                FileUtils.delete(path);
+                error(e);
+            } finally {
+                if (outputStream != null) {
+                    try {
+                        outputStream.close();
+                    } catch (IOException ignore) {
+
+                    }
+                }
+                if (inputStream != null) {
+                    try {
+                        inputStream.close();
+                    } catch (IOException ignore) {
+
+                    }
+                }
+            }
+        }
+    };
+
+    @Override
+    protected void onPrepare(Object params) {
+        if (mainThread) {
+            copyAssetRunnable.run();
+            return;
+        }
+        new Thread(copyAssetRunnable).start();
+    }
+}

+ 28 - 0
core/src/main/java/com/norman/webviewup/lib/source/UpgradeFileSource.java

@@ -0,0 +1,28 @@
+package com.norman.webviewup.lib.source;
+
+
+import android.content.Context;
+
+import com.norman.webviewup.lib.util.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+public class UpgradeFileSource extends UpgradePathSource {
+    private final String path;
+
+    public UpgradeFileSource(Context context, File file) {
+        super(context);
+        this.path = file != null ? file.getPath() : null;
+    }
+
+    @Override
+    public synchronized String getPath() {
+        return path;
+    }
+
+    @Override
+    protected void onPrepare(Object params) {
+        error(new IOException("file not exist, path is " + path));
+    }
+}

+ 10 - 15
local-source/src/main/java/com/norman/webviewup/lib/source/WebViewPackageSource.java → core/src/main/java/com/norman/webviewup/lib/source/UpgradePackageSource.java

@@ -4,34 +4,29 @@ import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 
-public class WebViewPackageSource extends WebViewSource {
+public class UpgradePackageSource extends UpgradeSource {
 
     private final String packageName;
-    private final Context context;
-
-    private int flags;
-
     private PackageInfo packageInfo;
 
-    public WebViewPackageSource(Context context, String packageName) {
-        this.context = context == null ? null : context.getApplicationContext();
+    public UpgradePackageSource(Context context, String packageName) {
+        super(context);
         this.packageName = packageName;
     }
 
-
-    public synchronized void setFlags(int flags) {
-        this.flags = flags;
-    }
-
-
     public synchronized PackageInfo getPackageInfo() {
         return packageInfo;
     }
 
+    public String getPackageName() {
+        return packageName;
+    }
+
     @Override
-    protected void onPrepare() {
+    protected void onPrepare(Object params) {
         try {
-            packageInfo = context.getPackageManager().getPackageInfo(packageName, flags);
+            int flags = params instanceof Integer? (int) params :0;
+            packageInfo = getContext().getPackageManager().getPackageInfo(packageName, flags);
         } catch (PackageManager.NameNotFoundException e) {
             throw new RuntimeException(e);
         }

+ 45 - 0
core/src/main/java/com/norman/webviewup/lib/source/UpgradePathSource.java

@@ -0,0 +1,45 @@
+package com.norman.webviewup.lib.source;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.annotation.NonNull;
+
+import com.norman.webviewup.lib.util.FileUtils;
+
+public abstract class UpgradePathSource extends UpgradeSource {
+    private static final String PREFERENCE_NAME = "UPGRADE_PATH_SOURCE";
+
+
+    private final SharedPreferences sharedPreferences;
+
+    public UpgradePathSource(@NonNull Context context) {
+        super(context);
+        this.sharedPreferences = context
+                .getSharedPreferences(PREFERENCE_NAME,
+                        Context.MODE_PRIVATE);
+    }
+
+    public abstract String getPath();
+
+    @Override
+    protected void onSuccess() {
+        super.onSuccess();
+        sharedPreferences.edit().putBoolean(getPath(), true).commit();
+    }
+
+    @Override
+    public synchronized boolean isSuccess() {
+        if (super.isSuccess()) {
+            return true;
+        }
+        if (sharedPreferences.getBoolean(getPath(), false)) {
+            if (FileUtils.isNotEmpty(getPath())) {
+                success();
+                return true;
+            }
+            sharedPreferences.edit().putBoolean(getPath(), false).commit();
+        }
+        return false;
+    }
+}

+ 139 - 0
core/src/main/java/com/norman/webviewup/lib/source/UpgradeSource.java

@@ -0,0 +1,139 @@
+package com.norman.webviewup.lib.source;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.annotation.NonNull;
+
+import com.norman.webviewup.lib.util.FileUtils;
+import com.norman.webviewup.lib.util.HandlerUtils;
+
+import java.util.HashSet;
+
+public abstract class UpgradeSource {
+
+    private final HashSet<OnPrepareCallback> prepareCallbackSet = new HashSet<>();
+
+    private final Context context;
+
+    private boolean success;
+
+    private boolean running;
+
+    private Throwable errorThrowable;
+
+
+    public UpgradeSource(@NonNull Context context) {
+        this.context = context.getApplicationContext();
+
+    }
+
+    public synchronized boolean isSuccess() {
+        return success;
+    }
+
+    public synchronized boolean isProcess() {
+        return running;
+    }
+
+    public synchronized final Throwable getError() {
+        return errorThrowable;
+    }
+
+    public synchronized void prepare(OnPrepareCallback prepareCallback) {
+        prepare(prepareCallback, null);
+    }
+
+
+    public synchronized void prepare(OnPrepareCallback prepareCallback, Object params) {
+        if (isSuccess()) {
+            HandlerUtils.runInMainThread(() -> {
+                prepareCallback.onPrepareSuccess(this);
+            });
+        } else if (errorThrowable != null) {
+            HandlerUtils.runInMainThread(() -> {
+                prepareCallback.onPrepareError(this, errorThrowable);
+            });
+        } else {
+            prepareCallbackSet.add(prepareCallback);
+            if (!running) {
+                running = true;
+                try {
+                    onPrepare(params);
+                } catch (Throwable throwable) {
+                    error(throwable);
+                }
+            }
+        }
+    }
+
+    protected abstract void onPrepare(Object params);
+
+    protected synchronized final void success() {
+        if (success || errorThrowable != null) {
+            return;
+        }
+        success = true;
+        running = false;
+        for (OnPrepareCallback prepareCallback : prepareCallbackSet) {
+            HandlerUtils.runInMainThread(() -> {
+                prepareCallback.onPrepareSuccess(this);
+            });
+        }
+        prepareCallbackSet.clear();
+        onSuccess();
+    }
+
+    protected synchronized final void error(@NonNull Throwable throwable) {
+        if (isSuccess() || errorThrowable != null) {
+            return;
+        }
+        errorThrowable = throwable;
+        running = false;
+        for (OnPrepareCallback prepareCallback : prepareCallbackSet) {
+            HandlerUtils.runInMainThread(() -> {
+                prepareCallback.onPrepareError(this, throwable);
+            });
+        }
+        prepareCallbackSet.clear();
+        onError(throwable);
+    }
+
+    protected synchronized final void process(float percent) {
+        if (isSuccess() || errorThrowable != null) {
+            return;
+        }
+        for (OnPrepareCallback prepareCallback : prepareCallbackSet) {
+            HandlerUtils.runInMainThread(() -> {
+                prepareCallback.onPrepareProcess(this, percent);
+            });
+        }
+        onProcess(percent);
+    }
+
+    protected void onSuccess() {
+
+    }
+
+    protected void onError(Throwable throwable) {
+
+    }
+
+    protected void onProcess(float percent) {
+
+    }
+
+
+    public Context getContext() {
+        return context;
+    }
+
+    public interface OnPrepareCallback {
+
+        void onPrepareSuccess(UpgradeSource webViewSource);
+
+        void onPrepareProcess(UpgradeSource webViewSource, float percent);
+
+        void onPrepareError(UpgradeSource webViewSource, Throwable throwable);
+    }
+}

+ 0 - 117
core/src/main/java/com/norman/webviewup/lib/util/ApkUtils.java

@@ -1,117 +0,0 @@
-package com.norman.webviewup.lib.util;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.os.Build;
-import android.text.TextUtils;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-public class ApkUtils {
-
-
-    public static void extractNativeLibrary(String apkPath, String soDir) {
-        ZipFile zipFile = null;
-        try {
-            if (TextUtils.isEmpty(apkPath)) {
-                throw new NullPointerException("apkPath is empty");
-            }
-            if (TextUtils.isEmpty(soDir)) {
-                throw new NullPointerException("apkPath is empty");
-            }
-            zipFile = new ZipFile(new File(apkPath));
-            Enumeration<? extends ZipEntry> entries = zipFile.entries();
-            Map<String, List<ZipEntry>> soLibEntryMap = new HashMap<>();
-            while (entries.hasMoreElements()) {
-                ZipEntry entry = entries.nextElement();
-                String entryName = entry.getName();
-                if (entryName.contains("../") || entry.isDirectory()) {
-                    continue;
-                }
-                if (!entryName.startsWith("lib/") && !entryName.endsWith(".so")) {
-                    continue;
-                }
-                String[] split = entry.getName().split("/");
-                if (split.length >= 3) {
-                    String abi = split[1];
-                    List<ZipEntry> list = soLibEntryMap.get(abi);
-                    if (list == null) {
-                        list = new ArrayList<>();
-                        soLibEntryMap.put(abi, list);
-                    }
-                    list.add(entry);
-                }
-            }
-            String[] supportedAbis = Build.SUPPORTED_ABIS;
-            for (String abi : supportedAbis) {
-                List<ZipEntry> entryList = soLibEntryMap.get(abi);
-                if (entryList == null) continue;
-                for (ZipEntry entry : entryList) {
-                    String[] split = entry.getName().split("/");
-                    File targetFile = new File(soDir, abi + "/" + split[split.length - 1]);
-                    FileUtils.copyFile(zipFile.getInputStream(entry), targetFile, true);
-                }
-            }
-
-        } catch (IOException ioException) {
-            FileUtils.cleanDirectory(soDir);
-            throw new RuntimeException(ioException);
-        } finally {
-            if (zipFile != null) {
-                try {
-                    zipFile.close();
-                } catch (IOException ignore) {
-
-                }
-            }
-        }
-    }
-
-    public static PackageInfo extractApk(Context context,
-                                  String filePath,
-                                  String apkPath) {
-        PackageInfo packageInfo = null;
-        try {
-            packageInfo = context.getPackageManager().getPackageArchiveInfo(filePath, 0);
-        } catch (Throwable ignore) {
-
-        }
-        if (packageInfo == null) {
-            try (ZipFile zipFile = new ZipFile(filePath)) {
-                Enumeration<? extends ZipEntry> entries = zipFile.entries();
-                while (entries.hasMoreElements()) {
-                    ZipEntry entry = entries.nextElement();
-                    String entryName = entry.getName();
-                    if (entryName.endsWith(".apk")) {
-                        FileUtils.copyFile(zipFile.getInputStream(entry), new File(apkPath), true);
-                        packageInfo = context.getPackageManager().getPackageArchiveInfo(apkPath, 0);
-                        break;
-                    }
-                }
-
-            } catch (IOException throwable) {
-                throw new RuntimeException(throwable);
-            }
-        }else {
-            FileUtils.copyFile(filePath, apkPath);
-        }
-        if (packageInfo == null) {
-            throw new RuntimeException("path:" + filePath + " not exist apk");
-        } else {
-            return packageInfo;
-        }
-    }
-}

+ 11 - 9
core/src/main/java/com/norman/webviewup/lib/util/FileUtils.java

@@ -60,9 +60,8 @@ public class FileUtils {
     }
 
     public static void makeDirectory(File file) {
-        File directory = file.isDirectory() ? file : file.getParentFile();
-        if (!directory.exists()) {
-            directory.mkdirs();
+        if (file != null && !file.exists()) {
+            file.mkdirs();
         }
     }
 
@@ -107,8 +106,7 @@ public class FileUtils {
 
     public static void copyFile(InputStream inputStream,
                                 File outputFile, boolean close) {
-
-        FileUtils.makeDirectory(outputFile);
+        FileUtils.makeDirectory(outputFile.getParentFile());
         if (!outputFile.exists()) {
             try {
                 outputFile.createNewFile();
@@ -183,7 +181,7 @@ public class FileUtils {
         return file.isFile() && file.exists();
     }
 
-    public static boolean isNotEmptyFile(String path) {
+    public static boolean isNotEmpty(String path) {
         if (TextUtils.isEmpty(path)) {
             return false;
         }
@@ -191,14 +189,18 @@ public class FileUtils {
         return file.isFile() && file.exists() && file.length() != 0;
     }
 
-    public static boolean createFile(File file){
-        if (!file.exists()){
+    public static boolean createFile(File file) {
+        if (file != null && !file.exists()) {
+            makeDirectory(file.getParentFile());
             try {
-               return file.createNewFile();
+                return file.createNewFile();
             } catch (IOException ignore) {
                 return false;
             }
         }
         return true;
     }
+    public static boolean createFile(String path) {
+        return createFile(new File(path));
+    }
 }

+ 16 - 0
core/src/main/java/com/norman/webviewup/lib/util/HandlerUtils.java

@@ -0,0 +1,16 @@
+package com.norman.webviewup.lib.util;
+
+import android.os.Handler;
+import android.os.Looper;
+
+public class HandlerUtils {
+    private static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
+
+    public static void runInMainThread(Runnable runnable) {
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            runnable.run();
+        } else {
+            MAIN_HANDLER.post(runnable);
+        }
+    }
+}

+ 0 - 31
core/src/main/java/com/norman/webviewup/lib/util/Md5Utils.java

@@ -1,31 +0,0 @@
-package com.norman.webviewup.lib.util;
-
-import android.text.TextUtils;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class Md5Utils {
-    public static String getMd5(String string) {
-        if (TextUtils.isEmpty(string)) return "";
-
-        try {
-            MessageDigest md5 = MessageDigest.getInstance("MD5");
-            byte[] bytes = md5.digest((string).getBytes());
-            StringBuilder result = new StringBuilder();
-            for (byte b : bytes) {
-                String temp = Integer.toHexString(b & 0xff);
-                if (temp.length() == 1) {
-                    temp = "0" + temp;
-                }
-                result.append(temp);
-            }
-            return result.toString();
-        } catch (NoSuchAlgorithmException e) {
-            e.printStackTrace();
-        }
-        return "";
-    }
-
-
-}

+ 0 - 0
aria-source/.gitignore → download-source/.gitignore


+ 3 - 4
aria-source/build.gradle → download-source/build.gradle

@@ -3,11 +3,11 @@ plugins {
 }
 
 android {
-    compileSdkVersion 30
-
+    namespace 'com.norman.webviewup.lib.source.download'
     defaultConfig {
+        compileSdk 33
         minSdkVersion 21
-        targetSdkVersion 30
+        targetSdkVersion 33
         consumerProguardFiles "consumer-rules.pro"
     }
 
@@ -24,7 +24,6 @@ android {
 }
 
 dependencies {
-    implementation project(":local-source")
     compileOnly project(":core")
     implementation 'me.laoyuyu.aria:core:3.8.16'
 }

+ 0 - 0
aria-source/consumer-rules.pro → download-source/consumer-rules.pro


+ 0 - 0
aria-source/proguard-rules.pro → download-source/proguard-rules.pro


+ 8 - 0
download-source/src/main/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+</manifest>

+ 246 - 0
download-source/src/main/java/com/norman/webviewup/lib/source/download/UpgradeDownloadSource.java

@@ -0,0 +1,246 @@
+package com.norman.webviewup.lib.source.download;
+
+import android.content.Context;
+
+import com.arialyy.aria.core.Aria;
+import com.arialyy.aria.core.AriaManager;
+import com.arialyy.aria.core.config.DownloadConfig;
+import com.arialyy.aria.core.download.DownloadEntity;
+import com.arialyy.aria.core.download.DownloadReceiver;
+import com.arialyy.aria.core.download.DownloadTaskListener;
+import com.arialyy.aria.core.inf.IEntity;
+import com.arialyy.aria.core.task.DownloadTask;
+import com.norman.webviewup.lib.source.UpgradePathSource;
+import com.norman.webviewup.lib.util.FileUtils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Objects;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class UpgradeDownloadSource extends UpgradePathSource implements DownloadTaskListener {
+
+    public static final int MAX_DOWNLOAD_THREAD_NUM = 20;
+    private final String url;
+
+    private final String path;
+    private final int threadNum;
+
+    private DownloadReceiver downloadReceiver;
+
+    private String tempPath;
+
+    private DownloadEntity downloadEntity;
+
+
+    public UpgradeDownloadSource(Context context, String url, File file, int threadNum) {
+        super(context);
+        this.url = url;
+        this.path = file.getPath();
+        this.threadNum = threadNum;
+    }
+
+    public UpgradeDownloadSource(Context context, String url, File file) {
+        this(context, url, file, MAX_DOWNLOAD_THREAD_NUM);
+    }
+
+
+    @Override
+    protected void onPrepare(Object params) {
+        AriaManager ariaManager = Aria.init(getContext());
+        DownloadConfig downloadConfig = ariaManager.getDownloadConfig();
+        downloadConfig.setThreadNum(threadNum);
+        downloadReceiver = Aria.download(this);
+        downloadEntity = downloadReceiver.getFirstDownloadEntity(url);
+        tempPath = path + ".tmp";
+        this.downloadReceiver.register();
+        if (downloadEntity == null ||
+                downloadEntity.getState() == IEntity.STATE_CANCEL) {
+            FileUtils.createFile(tempPath);
+            long taskId = downloadReceiver
+                    .load(url)
+                    .setFilePath(tempPath)
+                    .ignoreCheckPermissions()
+                    .ignoreFilePathOccupy()
+                    .create();
+            downloadEntity = downloadReceiver.getDownloadEntity(taskId);
+        } else if (downloadEntity.getState() == IEntity.STATE_WAIT
+                || downloadEntity.getState() == IEntity.STATE_OTHER
+                || downloadEntity.getState() == IEntity.STATE_FAIL
+                || downloadEntity.getState() == IEntity.STATE_STOP) {
+            downloadReceiver
+                    .load(downloadEntity.getId())
+                    .ignoreCheckPermissions()
+                    .resume();
+        } else if (downloadEntity.getState() == IEntity.STATE_COMPLETE) {
+            copyApk();
+        }
+    }
+
+    private void copyApk() {
+        new Thread(() -> {
+            BufferedInputStream bufferedInput = null;
+            BufferedOutputStream bufferedOutput = null;
+            ZipFile zipFile = null;
+            try {
+                if (isValidApk(tempPath)) {
+                    bufferedInput = new BufferedInputStream(new FileInputStream(tempPath));
+                } else {
+                    zipFile = new ZipFile(tempPath);
+                    bufferedInput = findStreamInZip(zipFile);
+                }
+                Objects.requireNonNull(bufferedInput);
+                FileUtils.createFile(path);
+                bufferedOutput = new BufferedOutputStream(new FileOutputStream(path));
+                copyBufferStream(bufferedInput, bufferedOutput);
+                success();
+                deleteDownload();
+            } catch (Throwable e) {
+                FileUtils.delete(path);
+                error(e);
+            } finally {
+                try {
+                    if (bufferedInput != null) {
+                        bufferedInput.close();
+                    }
+                } catch (IOException ignore) {
+
+                }
+                try {
+                    if (bufferedOutput != null) {
+                        bufferedOutput.close();
+                    }
+                } catch (IOException ignore) {
+
+                }
+
+                try {
+                    if (zipFile != null) {
+                        zipFile.close();
+                    }
+                } catch (IOException ignore) {
+
+                }
+            }
+
+        }).start();
+    }
+
+
+    private BufferedInputStream findStreamInZip(ZipFile zipFile) throws IOException {
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = entries.nextElement();
+            String entryName = entry.getName();
+            if (entryName.endsWith(".apk")) {
+                return new BufferedInputStream(zipFile.getInputStream(entry));
+            }
+        }
+        return null;
+    }
+
+    private void copyBufferStream(BufferedInputStream bufferedInput,
+                                  BufferedOutputStream bufferedOutput) throws IOException {
+        int count;
+        int readCount = 0;
+        int availableByteCount = bufferedInput.available();
+        byte[] buffer = new byte[8192];
+        while ((count = bufferedInput.read(buffer)) > 0) {
+            bufferedOutput.write(buffer, 0, count);
+            readCount = readCount + count;
+            process(0.05f * readCount / availableByteCount + 0.95f);
+        }
+        bufferedOutput.flush();
+    }
+
+    private void deleteDownload() {
+        try {
+            downloadReceiver
+                    .load(downloadEntity.getId())
+                    .ignoreCheckPermissions()
+                    .cancel(true);
+        } catch (Throwable ignore) {
+
+        }
+    }
+
+
+    @Override
+    public void onWait(DownloadTask task) {
+
+    }
+
+    @Override
+    public void onPre(DownloadTask task) {
+        downloadEntity = task.getDownloadEntity();
+    }
+
+    @Override
+    public void onTaskPre(DownloadTask task) {
+
+    }
+
+    @Override
+    public void onTaskResume(DownloadTask task) {
+
+    }
+
+    @Override
+    public void onTaskStart(DownloadTask task) {
+
+    }
+
+    @Override
+    public void onTaskStop(DownloadTask task) {
+        downloadEntity = task.getDownloadEntity();
+    }
+
+    @Override
+    public void onTaskCancel(DownloadTask task) {
+        downloadEntity = task.getDownloadEntity();
+    }
+
+    @Override
+    public void onTaskFail(DownloadTask task, Exception e) {
+        error(e);
+    }
+
+    @Override
+    public void onTaskComplete(DownloadTask task) {
+        copyApk();
+    }
+
+    @Override
+    public void onTaskRunning(DownloadTask task) {
+        float percent = task.getPercent() / 100.0f * 0.95f;
+        process(percent);
+    }
+
+
+    @Override
+    public void onNoSupportBreakPoint(DownloadTask task) {
+
+    }
+
+    @Override
+    public synchronized String getPath() {
+        return path;
+    }
+
+
+    private boolean isValidApk(String path) {
+        try {
+            return getContext().getPackageManager().getPackageArchiveInfo(path, 0) != null;
+        } catch (Throwable ignore) {
+
+        }
+        return false;
+    }
+
+}

+ 4 - 1
gradle.properties

@@ -14,4 +14,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
 # AndroidX package structure to make it clearer which packages are bundled with the
 # Android operating system, and which are packaged with your app"s APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
+android.useAndroidX=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false

+ 1 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
 #Sat May 22 18:51:13 IST 2021
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
+distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.4-bin.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME

+ 0 - 1
local-source/.gitignore

@@ -1 +0,0 @@
-/build

+ 0 - 30
local-source/build.gradle

@@ -1,30 +0,0 @@
-plugins {
-    id 'com.android.library'
-}
-
-android {
-    compileSdkVersion 30
-
-    defaultConfig {
-        minSdkVersion 21
-        targetSdkVersion 30
-        consumerProguardFiles "consumer-rules.pro"
-    }
-
-    buildTypes {
-        release {
-            minifyEnabled false
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-        }
-    }
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_8
-        targetCompatibility JavaVersion.VERSION_1_8
-    }
-}
-
-dependencies {
-
-    implementation 'androidx.appcompat:appcompat:1.6.1'
-    compileOnly project(":core")
-}

+ 0 - 0
local-source/consumer-rules.pro


+ 0 - 21
local-source/proguard-rules.pro

@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-#   http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-#   public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile

+ 0 - 4
local-source/src/main/AndroidManifest.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest package="com.norman.webviewup.lib.source">
-
-</manifest>

+ 0 - 91
local-source/src/main/java/com/norman/webviewup/lib/source/WebViewAssetSource.java

@@ -1,91 +0,0 @@
-package com.norman.webviewup.lib.source;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.content.res.AssetManager;
-import android.text.TextUtils;
-
-import com.norman.webviewup.lib.UpgradeDirectory;
-import com.norman.webviewup.lib.util.FileUtils;
-import com.norman.webviewup.lib.util.Md5Utils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.channels.FileChannel;
-
-public class WebViewAssetSource extends WebViewPathSource {
-
-    private final String assetName;
-
-    private final Context context;
-
-    private final String path;
-
-    public WebViewAssetSource(Context context, String assetName, String path) {
-        this.assetName = assetName;
-        this.context = context == null ? null : context.getApplicationContext();
-        if (TextUtils.isEmpty(path) && context != null && !TextUtils.isEmpty(assetName)) {
-            File apkFile = UpgradeDirectory.getApkFile(context, Md5Utils.getMd5(assetName));
-            this.path = apkFile.getPath();
-        } else {
-            this.path = path;
-        }
-    }
-
-    public WebViewAssetSource(Context context, String assetName) {
-        this(context, assetName, null);
-    }
-
-    @Override
-    public String getPath() {
-        return path;
-    }
-
-    @Override
-    protected void onPrepare() {
-        if (FileUtils.isNotEmptyFile(path)) {
-            success();
-        } else {
-            new Thread(new Runnable() {
-                @Override
-                public void run() {
-                    FileOutputStream outputStream = null;
-                    FileInputStream inputStream = null;
-                    try {
-                        outputStream = new FileOutputStream(path);
-                        FileChannel dstChannel = outputStream.getChannel();
-                        AssetManager assetManager = context.getAssets();
-                        AssetFileDescriptor assetFileDescriptor = assetManager.openFd(assetName);
-                        inputStream = assetFileDescriptor.createInputStream();
-                        FileChannel fileChannel = inputStream.getChannel();
-                        long startOffset = assetFileDescriptor.getStartOffset();
-                        long declaredLength = assetFileDescriptor.getDeclaredLength();
-                        fileChannel.transferTo(startOffset, declaredLength, dstChannel);
-
-                    } catch (Throwable e) {
-                        FileUtils.delete(path);
-                        throw new RuntimeException(e);
-                    } finally {
-                        if (outputStream != null) {
-                            try {
-                                outputStream.close();
-                            } catch (IOException ignore) {
-
-                            }
-                        }
-                        if (inputStream != null) {
-                            try {
-                                inputStream.close();
-                            } catch (IOException ignore) {
-
-                            }
-                        }
-                    }
-
-                }
-            }).start();
-        }
-    }
-}

+ 0 - 33
local-source/src/main/java/com/norman/webviewup/lib/source/WebViewFileSource.java

@@ -1,33 +0,0 @@
-package com.norman.webviewup.lib.source;
-
-
-import com.norman.webviewup.lib.util.FileUtils;
-
-import java.io.File;
-import java.io.IOException;
-
-public class WebViewFileSource extends WebViewPathSource {
-    private final String path;
-
-    public WebViewFileSource(String path) {
-        this.path = path;
-    }
-
-    public WebViewFileSource(File file) {
-        this.path = file != null ? file.getPath() : null;
-    }
-
-    @Override
-    public synchronized String getPath() {
-        return path;
-    }
-
-    @Override
-    protected void onPrepare() {
-        if (FileUtils.isNotEmptyFile(path)) {
-            success();
-        } else {
-            error(new IOException("file not exist, path is " + path));
-        }
-    }
-}

+ 0 - 6
local-source/src/main/java/com/norman/webviewup/lib/source/WebViewPathSource.java

@@ -1,6 +0,0 @@
-package com.norman.webviewup.lib.source;
-
-public abstract class WebViewPathSource extends WebViewSource {
-
-    public abstract String getPath();
-}

+ 0 - 77
local-source/src/main/java/com/norman/webviewup/lib/source/WebViewSource.java

@@ -1,77 +0,0 @@
-package com.norman.webviewup.lib.source;
-
-import androidx.annotation.NonNull;
-
-import java.util.HashSet;
-
-public abstract class WebViewSource {
-
-    private final HashSet<OnPrepareCallback> prepareCallbackSet = new HashSet<>();
-
-    private boolean success;
-
-    private boolean running;
-
-    private Throwable errorThrowable;
-
-
-
-    public synchronized final boolean isSuccess() {
-        return success;
-    }
-
-    public synchronized final Throwable getError() {
-        return errorThrowable;
-    }
-
-
-    public synchronized void prepare(OnPrepareCallback prepareCallback) {
-        if (success) {
-            prepareCallback.onPrepareSuccess(this);
-        } else if (errorThrowable != null) {
-            prepareCallback.onPrepareError(this, errorThrowable);
-        } else {
-            prepareCallbackSet.add(prepareCallback);
-            if (!running) {
-                running = true;
-                try {
-                    onPrepare();
-                } catch (Throwable throwable) {
-                    error(throwable);
-                }
-            }
-        }
-    }
-
-    protected abstract void onPrepare();
-
-    protected synchronized final void success() {
-        if (success || errorThrowable != null) {
-            return;
-        }
-        success = true;
-        for (OnPrepareCallback prepareCallback : prepareCallbackSet) {
-            prepareCallback.onPrepareSuccess(this);
-        }
-        prepareCallbackSet.clear();
-    }
-
-    protected synchronized final void error(@NonNull Throwable throwable) {
-        if (success || errorThrowable != null) {
-            return;
-        }
-        errorThrowable = throwable;
-        for (OnPrepareCallback prepareCallback : prepareCallbackSet) {
-            prepareCallback.onPrepareError(this, throwable);
-        }
-        prepareCallbackSet.clear();
-    }
-
-
-    public interface OnPrepareCallback {
-
-        void onPrepareSuccess(WebViewSource webViewSource);
-
-        void onPrepareError(WebViewSource webViewSource, Throwable throwable);
-    }
-}

+ 0 - 17
local-source/src/test/java/com/norman/webviewup/lib/ExampleUnitTest.java

@@ -1,17 +0,0 @@
-package com.norman.webviewup.lib;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
- */
-public class ExampleUnitTest {
-    @Test
-    public void addition_isCorrect() {
-        assertEquals(4, 2 + 2);
-    }
-}

+ 1 - 2
settings.gradle

@@ -1,4 +1,3 @@
 include ':app'
 include ':core'
-include ':local-source'
-include ':aria-source'
+include ':download-source'