diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7bb2ab8..7e7c2ec 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -44,7 +44,7 @@
-
diff --git a/app/src/main/java/com/example/mir4updater/activity/InstallActivity.java b/app/src/main/java/com/example/mir4updater/activity/InstallActivity.java
deleted file mode 100644
index 235a642..0000000
--- a/app/src/main/java/com/example/mir4updater/activity/InstallActivity.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.example.mir4updater.activity;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import android.content.Intent;
-import android.content.pm.PackageInstaller;
-import android.os.Bundle;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.example.mir4updater.R;
-import com.example.mir4updater.utils.XapkUtils;
-
-public class InstallActivity extends AppCompatActivity {
-
- private static final String TAG = "InstallActivity";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_install);
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- Log.e(TAG, "onNewIntent: ");
- Bundle extras = intent.getExtras();
- if (XapkUtils.PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) {
- int status = -100;
- String message = "";
- if (extras != null) {
- status = extras.getInt(PackageInstaller.EXTRA_STATUS);
- message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
- }
- switch (status) {
- case PackageInstaller.STATUS_PENDING_USER_ACTION:
- // This test app isn't privileged, so the user has to confirm the install.
- Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
- startActivity(confirmIntent);
- break;
- case PackageInstaller.STATUS_SUCCESS:
- Toast.makeText(this, "安装成功!", Toast.LENGTH_SHORT).show();
- finish();
- break;
- case PackageInstaller.STATUS_FAILURE:
- case PackageInstaller.STATUS_FAILURE_ABORTED:
- case PackageInstaller.STATUS_FAILURE_BLOCKED:
- case PackageInstaller.STATUS_FAILURE_CONFLICT:
- case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
- case PackageInstaller.STATUS_FAILURE_INVALID:
- case PackageInstaller.STATUS_FAILURE_STORAGE:
- Toast.makeText(this, "安装失败,请重试",
- Toast.LENGTH_SHORT).show();
- finish();
- Log.d(TAG, "Install failed! " + status + ", " + message);
- break;
- default:
- Toast.makeText(this, "安装失败,解压文件可能已丢失或损坏,请重试",
- Toast.LENGTH_SHORT).show();
- finish();
- Log.d(TAG, "Unrecognized status received from installer: " + status);
- }
- }
- }
-
-
-}
diff --git a/app/src/main/java/com/example/mir4updater/activity/install/InstallActivity.java b/app/src/main/java/com/example/mir4updater/activity/install/InstallActivity.java
new file mode 100644
index 0000000..8313daf
--- /dev/null
+++ b/app/src/main/java/com/example/mir4updater/activity/install/InstallActivity.java
@@ -0,0 +1,279 @@
+package com.example.mir4updater.activity.install;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.blankj.utilcode.util.RomUtils;
+import com.example.mir4updater.R;
+import com.example.mir4updater.base.mvvm.BaseMvvmActivity;
+import com.example.mir4updater.databinding.ActivityInstallBinding;
+import com.example.mir4updater.utils.FileUtils;
+import com.hjq.toast.Toaster;
+
+import org.zeroturnaround.zip.ZipUtil;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+public class InstallActivity extends BaseMvvmActivity {
+
+ private static final String TAG = "InstallActivity";
+
+ private static final String PACKAGE_INSTALLED_ACTION = "TT_SESSION_API_PACKAGE_INSTALLED";
+
+ public static final String KEY_XAPK_PATH = "xapk_path";
+ public static final String KEY_APK_PATHS = "apk_paths";
+
+ private String mXapkPath;
+
+ private ExecutorService installXapkExectuor;
+
+ private PackageInstaller.Session mSession;
+
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_install;
+ }
+
+ @Override
+ protected void initDataBinding() {
+ mViewModel.setCtx(this);
+ mViewModel.setLifecycle(getLifecycleSubject());
+ mViewModel.setVDBinding(mViewDataBinding);
+ mViewDataBinding.setClick(new BtnClick());
+ }
+
+ @Override
+ protected void initView() {
+
+ }
+
+ public void initData() {
+// apkPaths = getIntent().getStringArrayListExtra(KEY_APK_PATHS);
+ Intent intent = getIntent();
+ mXapkPath = intent.getStringExtra(KEY_XAPK_PATH);
+ Log.e(TAG, "initData: " + mXapkPath);
+ if (TextUtils.isEmpty(mXapkPath) || !new File(mXapkPath).exists()) {
+ Toaster.show("文件不存在");
+ finish();
+ return;
+ }
+ Observable.create(new ObservableOnSubscribe>() {
+ @Override
+ public void subscribe(ObservableEmitter> emitter) throws Exception {
+ long startTime = System.currentTimeMillis();
+ File mApkFile = new File(mXapkPath);
+ String dirName = FileUtils.getFileNoExName(mApkFile.getAbsolutePath());
+ String unpackPath = getExternalCacheDir() + File.separator + "xapk_unzip" + File.separator + dirName;
+ File unpackDir = new File(unpackPath);
+ ZipUtil.unpack(mApkFile, unpackDir);
+ Log.e(TAG, "subscribe: time = " + (System.currentTimeMillis() - startTime) + "ms");
+ List files = new ArrayList<>(Arrays.asList(unpackDir.listFiles()));
+ List paths = files.stream().map(new Function() {
+ @Override
+ public String apply(File file) {
+ return file.getAbsolutePath();
+ }
+ }).collect(Collectors.toList());
+ List apkPaths = paths.stream().filter(new Predicate() {
+ @Override
+ public boolean test(String s) {
+ return s.endsWith(".apk");
+ }
+ }).collect(Collectors.toList());
+ emitter.onNext(apkPaths);
+ emitter.onComplete();
+ }
+ }).subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Observer>() {
+ @Override
+ public void onSubscribe(Disposable d) {
+
+ }
+
+ @Override
+ public void onNext(List fileList) {
+ Log.e(TAG, "installXAPK: " + fileList);
+ installXapk(fileList);
+
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ Log.e(TAG, "onError: " + e.getMessage());
+ }
+
+ @Override
+ public void onComplete() {
+ Log.e(TAG, "onComplete: ");
+ }
+ });
+
+ }
+
+ private void installXapk(List mApkPaths) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ Toast.makeText(this, "暂时不支持安装,请更新到Android 5.0及以上版本", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ if (mApkPaths == null || mApkPaths.isEmpty()) {
+ Toast.makeText(this, "解析apk出错或已取消", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ if (RomUtils.isMeizu() || RomUtils.isVivo()) {
+ Toast.makeText(this, "魅族或VIVO系统用户如遇安装被中止或者安装失败的情况,请尝试联系手机平台客服,或者更换系统内置包安装器再重试",
+ Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ installXapkExectuor = Executors.newSingleThreadExecutor();
+ installXapkExectuor.execute(() -> {
+ try {
+ mSession = initSession();
+
+ for (String apkPath : mApkPaths) {
+ addApkToInstallSession(apkPath, mSession);
+ }
+
+ commitSession(mSession);
+ } catch (IOException e) {
+ e.printStackTrace();
+ abandonSession();
+ }
+ });
+ }
+
+ private PackageInstaller.Session initSession() throws IOException {
+ PackageInstaller.Session session;
+ PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+
+ int sessionId;
+ sessionId = packageInstaller.createSession(params);
+ session = packageInstaller.openSession(sessionId);
+
+ return session;
+ }
+
+ private void addApkToInstallSession(String filePath, PackageInstaller.Session session)
+ throws IOException {
+ // It's recommended to pass the file size to openWrite(). Otherwise installation may fail
+ // if the disk is almost full.
+ try (OutputStream packageInSession = session.openWrite(FileUtils.getFileName(filePath), 0, new File(filePath).length());
+ InputStream is = new BufferedInputStream(new FileInputStream(filePath))) {
+ byte[] buffer = new byte[16384];
+ int n;
+ while ((n = is.read(buffer)) >= 0) {
+ packageInSession.write(buffer, 0, n);
+ }
+ }
+ }
+
+ private void commitSession(PackageInstaller.Session session) {
+ // Create an install status receiver.
+ Intent intent = new Intent(this, InstallActivity.class);
+ intent.setAction(PACKAGE_INSTALLED_ACTION);
+ PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
+ IntentSender intentSender = pendingIntent.getIntentSender();
+
+ // Commit the session (this will start the installation workflow).
+ session.commit(intentSender);
+ }
+
+ private void abandonSession() {
+ if (mSession != null) {
+ mSession.abandon();
+ mSession.close();
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Bundle extras = intent.getExtras();
+ if (PACKAGE_INSTALLED_ACTION.equals(intent.getAction())) {
+ int status = -100;
+ String message = "";
+ if (extras != null) {
+ status = extras.getInt(PackageInstaller.EXTRA_STATUS);
+ message = extras.getString(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Log.e(TAG, "onNewIntent: status = " + status);
+ Log.e(TAG, "onNewIntent: message = " + message);
+ }
+ switch (status) {
+ case PackageInstaller.STATUS_PENDING_USER_ACTION:
+ // This test app isn't privileged, so the user has to confirm the install.
+ Intent confirmIntent = (Intent) extras.get(Intent.EXTRA_INTENT);
+ startActivity(confirmIntent);
+ break;
+ case PackageInstaller.STATUS_SUCCESS:
+ Toast.makeText(this, "安装成功!", Toast.LENGTH_SHORT).show();
+ finish();
+ break;
+ case PackageInstaller.STATUS_FAILURE:
+ case PackageInstaller.STATUS_FAILURE_ABORTED:
+ case PackageInstaller.STATUS_FAILURE_BLOCKED:
+ case PackageInstaller.STATUS_FAILURE_CONFLICT:
+ case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
+ case PackageInstaller.STATUS_FAILURE_INVALID:
+ case PackageInstaller.STATUS_FAILURE_STORAGE:
+ Toast.makeText(this, "安装失败,请重试",
+ Toast.LENGTH_SHORT).show();
+ finish();
+ Log.d(TAG, "Install failed! " + status + ", " + message);
+ break;
+ default:
+ Toast.makeText(this, "安装失败,解压文件可能已丢失或损坏,请重试",
+ Toast.LENGTH_SHORT).show();
+ finish();
+ Log.d(TAG, "Unrecognized status received from installer: " + status);
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (installXapkExectuor != null && !installXapkExectuor.isShutdown()) {
+ installXapkExectuor.shutdown();
+ }
+ abandonSession();
+ }
+
+
+ public class BtnClick {
+
+ }
+}
diff --git a/app/src/main/java/com/example/mir4updater/activity/install/InstallViewModel.java b/app/src/main/java/com/example/mir4updater/activity/install/InstallViewModel.java
new file mode 100644
index 0000000..47a4e82
--- /dev/null
+++ b/app/src/main/java/com/example/mir4updater/activity/install/InstallViewModel.java
@@ -0,0 +1,19 @@
+package com.example.mir4updater.activity.install;
+
+import com.example.mir4updater.base.mvvm.BaseViewModel;
+import com.example.mir4updater.databinding.ActivityInstallBinding;
+import com.example.mir4updater.databinding.ActivityMainBinding;
+import com.trello.rxlifecycle4.android.ActivityEvent;
+
+public class InstallViewModel extends BaseViewModel {
+
+ @Override
+ public ActivityInstallBinding getVDBinding() {
+ return binding;
+ }
+
+ @Override
+ public void onDestroy() {
+
+ }
+}
diff --git a/app/src/main/java/com/example/mir4updater/activity/main/MainActivity.java b/app/src/main/java/com/example/mir4updater/activity/main/MainActivity.java
index c244d1f..0a1a671 100644
--- a/app/src/main/java/com/example/mir4updater/activity/main/MainActivity.java
+++ b/app/src/main/java/com/example/mir4updater/activity/main/MainActivity.java
@@ -1,7 +1,6 @@
package com.example.mir4updater.activity.main;
import android.content.ContentResolver;
-import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -16,6 +15,7 @@ import androidx.annotation.Nullable;
import androidx.documentfile.provider.DocumentFile;
import com.example.mir4updater.R;
+import com.example.mir4updater.activity.install.InstallActivity;
import com.example.mir4updater.activity.update.UpdateActivity;
import com.example.mir4updater.activity.upload.UploadActivity;
import com.example.mir4updater.base.mvvm.BaseMvvmActivity;
@@ -24,17 +24,12 @@ import com.example.mir4updater.config.CommonConfig;
import com.example.mir4updater.databinding.ActivityMainBinding;
import com.example.mir4updater.utils.HashUtils;
import com.example.mir4updater.utils.PermissionUtils;
-import com.example.mir4updater.utils.XapkUtils;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.hjq.toast.Toaster;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
-import com.wuliang.lib.XapkInstaller;
-import com.wuliang.lib.XapkInstallerFactoryKt;
-
-import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.File;
@@ -567,9 +562,13 @@ public class MainActivity extends BaseMvvmActivity
-
+ tools:context=".activity.nstall.InstallActivity">
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file