From 6c4b3ffc8e650ce02d49f80d75d4b7568856b3ef Mon Sep 17 00:00:00 2001 From: tongtongstudio Date: Wed, 23 Apr 2025 23:46:43 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BD=BF=E7=94=A8activity=E5=AE=89=E8=A3=85xap?= =?UTF-8?q?k?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 2 +- .../mir4updater/activity/InstallActivity.java | 68 ----- .../activity/install/InstallActivity.java | 279 ++++++++++++++++++ .../activity/install/InstallViewModel.java | 19 ++ .../activity/main/MainActivity.java | 17 +- .../example/mir4updater/utils/FileUtils.java | 15 +- .../example/mir4updater/utils/XapkUtils.java | 18 +- app/src/main/res/layout/activity_install.xml | 46 ++- 8 files changed, 371 insertions(+), 93 deletions(-) delete mode 100644 app/src/main/java/com/example/mir4updater/activity/InstallActivity.java create mode 100644 app/src/main/java/com/example/mir4updater/activity/install/InstallActivity.java create mode 100644 app/src/main/java/com/example/mir4updater/activity/install/InstallViewModel.java 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