diff --git a/app/src/main/java/com/gh/base/BaseActivity.java b/app/src/main/java/com/gh/base/BaseActivity.java index c9031cedae..a96f199260 100644 --- a/app/src/main/java/com/gh/base/BaseActivity.java +++ b/app/src/main/java/com/gh/base/BaseActivity.java @@ -18,12 +18,17 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Lifecycle; + import com.gh.base.fragment.BaseFragment; import com.gh.common.constant.Constants; import com.gh.common.util.DialogUtils; import com.gh.common.util.DisplayUtils; import com.gh.common.util.MtaHelper; -import com.gh.common.util.PackageUtils; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.RunningUtils; import com.gh.common.util.ShareUtils; import com.gh.common.util.StringUtils; @@ -44,10 +49,6 @@ import org.json.JSONObject; import java.lang.ref.WeakReference; import java.util.List; -import androidx.annotation.NonNull; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; -import androidx.lifecycle.Lifecycle; import butterknife.ButterKnife; import pub.devrel.easypermissions.EasyPermissions; @@ -206,7 +207,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy if (FileUtils.isEmptyFile(showDialog.getPath())) { toast(R.string.install_failure_hint); } else { - startActivity(PackageUtils.getUninstallIntent(BaseActivity.this, showDialog.getPath())); + PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath()); } }); } else if (LOGIN_EXCEPTION.equals(showDialog.getType())) { diff --git a/app/src/main/java/com/gh/common/databind/BindingAdapters.java b/app/src/main/java/com/gh/common/databind/BindingAdapters.java index afbb370a3f..bc8bd6c4a2 100644 --- a/app/src/main/java/com/gh/common/databind/BindingAdapters.java +++ b/app/src/main/java/com/gh/common/databind/BindingAdapters.java @@ -40,6 +40,7 @@ import com.gh.common.util.LogUtils; import com.gh.common.util.MtaHelper; import com.gh.common.util.NewsUtils; import com.gh.common.util.NumberUtils; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.PermissionHelper; import com.gh.common.util.PlatformUtils; @@ -467,7 +468,7 @@ public class BindingAdapters { if (gameEntity.getApk().size() == 1) { DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl()); if (downloadEntity != null) { - PackageUtils.launchSetup(v.getContext(), downloadEntity); + PackageInstaller.install(v.getContext(), downloadEntity); } } break; diff --git a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java index 612a31ba1e..59f4daa6d2 100644 --- a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java +++ b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java @@ -6,6 +6,8 @@ import android.view.View; import com.gh.common.constant.Config; import com.gh.common.repository.ReservationRepository; import com.gh.common.view.DownloadProgressBar; +import com.gh.common.xapk.XapkInstaller; +import com.gh.common.xapk.XapkUnzipStatus; import com.gh.download.DownloadManager; import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.viewholder.DetailViewHolder; @@ -13,7 +15,6 @@ import com.gh.gamecenter.entity.LinkEntity; import com.gh.gamecenter.entity.PluginLocation; import com.gh.gamecenter.manager.PackagesManager; import com.lightgame.download.DownloadEntity; -import com.lightgame.utils.Utils; /** * Created by khy on 27/06/17. @@ -112,8 +113,18 @@ public class DetailDownloadUtils { } public static void detailInvalidate(DetailViewHolder viewHolder) { - viewHolder.mDownloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10)); DownloadEntity downloadEntity = viewHolder.downloadEntity; + String xapkStatus = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS); + + if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) { + String percent = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_PERCENT); + viewHolder.mDownloadPb.setText("解压中(" + percent + "%)"); + viewHolder.mDownloadPb.setProgress((int) (Float.valueOf(percent) * 10)); + viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.XAPK_UNZIPPING); + return; + } + + viewHolder.mDownloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10)); switch (downloadEntity.getStatus()) { case downloading: case pause: diff --git a/app/src/main/java/com/gh/common/util/DialogUtils.java b/app/src/main/java/com/gh/common/util/DialogUtils.java index faf9e7bd13..831e2053ba 100644 --- a/app/src/main/java/com/gh/common/util/DialogUtils.java +++ b/app/src/main/java/com/gh/common/util/DialogUtils.java @@ -39,6 +39,7 @@ import com.gh.common.view.LimitHeightLinearLayout; import com.gh.common.view.MaxHeightNestedScrollView; import com.gh.gamecenter.AboutActivity; import com.gh.gamecenter.R; +import com.gh.gamecenter.SuggestionActivity; import com.gh.gamecenter.WebActivity; import com.gh.gamecenter.adapter.viewholder.PrivacyPolicyItemViewHolder; import com.gh.gamecenter.databinding.ImprintContentItemBinding; @@ -48,9 +49,11 @@ import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.entity.PrivacyPolicyEntity; import com.gh.gamecenter.entity.SettingsEntity; import com.gh.gamecenter.entity.TrackableEntity; +import com.gh.gamecenter.suggest.SuggestType; import com.halo.assistant.HaloApp; import com.halo.assistant.fragment.SettingsFragment; import com.lightgame.adapter.BaseRecyclerAdapter; +import com.lightgame.download.DownloadEntity; import com.lightgame.utils.AppManager; import com.lightgame.utils.Utils; @@ -1425,6 +1428,36 @@ public class DialogUtils { dialog.show(); } + public static void showUnzipFailureDialog(Context context, DownloadEntity downloadEntity) { + final Context activityContext = checkDialogContext(context); + + final Dialog dialog = new Dialog(activityContext, R.style.GhAlertDialog); + Window window = dialog.getWindow(); + if (window != null) { + window.setBackgroundDrawableResource(android.R.color.transparent); + } + + View contentView = LayoutInflater.from(activityContext).inflate(R.layout.dialog_unzip_failure, null); + + View cancelBtn = contentView.findViewById(R.id.cancel); + View confirmBtn = contentView.findViewById(R.id.confirm); + + + cancelBtn.setOnClickListener(v -> { + dialog.dismiss(); + }); + + confirmBtn.setOnClickListener(v -> { + dialog.dismiss(); + String hint = "《" + downloadEntity.getName() + "》游戏安装包解压失败,问题反馈:"; + SuggestionActivity.startSuggestionActivity(activityContext, SuggestType.normal, null, hint); + }); + + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.setContentView(contentView); + dialog.show(); + } + /** * @param context may be is application context * @return activity context diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.java b/app/src/main/java/com/gh/common/util/DownloadItemUtils.java index d0d82d7c39..7230203598 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.java +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.java @@ -21,6 +21,8 @@ import com.gh.common.dialog.ReserveDialogFragment; import com.gh.common.exposure.ExposureEvent; import com.gh.common.history.HistoryHelper; import com.gh.common.repository.ReservationRepository; +import com.gh.common.xapk.XapkInstaller; +import com.gh.common.xapk.XapkUnzipStatus; import com.gh.download.DownloadManager; import com.gh.download.dialog.DownloadDialog; import com.gh.gamecenter.DownloadManagerActivity; @@ -261,6 +263,7 @@ public class DownloadItemUtils { boolean isShowPlatform, boolean isNormal) { updateItemViewStatus(holder, true, null); + holder.gameProgressbar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_bg_style)); String platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform()); @@ -321,6 +324,17 @@ public class DownloadItemUtils { holder.gameDownloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style)); } } else if (status.equals(DownloadStatus.done)) { + String xapkStatus = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS); + if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) { + String percent = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_PERCENT); + holder.gameProgressbar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_xapk_style)); + holder.gameDownloadSpeed.setText(R.string.unzipping); + holder.gameProgressbar.setProgress((int) (Float.valueOf(percent) * 10)); + holder.gameDownloadPercentage.setText(percent + "%"); + holder.gameDownloadBtn.setText(R.string.unzipping); + return; + } + holder.gameProgressbar.setProgress(1000); if (isShowPlatform && platform != null) { holder.gameDownloadSpeed.setText(String.format("%s - 下载完成", platform)); @@ -559,10 +573,6 @@ public class DownloadItemUtils { } DataUtils.onGameLaunchEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location); PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName()); - } else if (str.equals(context.getString(R.string.waiting)) - || str.equals(context.getString(R.string.downloading))) { - context.startActivity(DownloadManagerActivity.getDownloadMangerIntent(context, - apk.getUrl(), entrance + "+(" + location.split(":")[0] + ")")); } else if (str.equals(context.getString(R.string.update))) { if (entrance.contains("我的游戏")) { MtaHelper.onEvent("我的游戏_启动", "更新", gameEntity.getName()); @@ -571,6 +581,9 @@ public class DownloadItemUtils { DialogUtils.checkDownload(context, apk.getSize(), isSubscribe -> update(context, gameEntity, entrance, location, isSubscribe, traceEvent)); }); + } else { + context.startActivity(DownloadManagerActivity.getDownloadMangerIntent(context, + apk.getUrl(), entrance + "+(" + location.split(":")[0] + ")")); } } @@ -631,9 +644,9 @@ public class DownloadItemUtils { } adapter.notifyItemChanged(position); } else if (PackageUtils.isCanPluggable(apkEntity)) { - DialogUtils.showPluginDialog(context, () -> context.startActivity(PackageUtils.getUninstallIntent(context, path))); + DialogUtils.showPluginDialog(context, () -> PackageInstaller.uninstall(context, path)); } else { - PackageUtils.launchSetup(context, downloadEntity); + PackageInstaller.install(context, downloadEntity); } } } diff --git a/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt b/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt index 51e1d0990a..380c1a0176 100644 --- a/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt +++ b/app/src/main/java/com/gh/common/util/DownloadNotificationHelper.kt @@ -10,6 +10,8 @@ import android.os.Build import androidx.core.app.NotificationCompat import com.gh.common.AppExecutor import com.gh.common.constant.Constants +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.gamecenter.R import com.halo.assistant.HaloApp import com.lightgame.download.DownloadEntity @@ -38,9 +40,10 @@ object DownloadNotificationHelper { var requireUpdateNotificationGroupDelay = false val notificationManager = getNotificationManager() val downloadNotificationId = (entity.gameId + entity.packageName).hashCode() + val xapkStatus = entity.meta[XapkInstaller.XAPK_UNZIP_STATUS] val intent = Intent() - if (entity.status == DownloadStatus.done) { + if (entity.status == DownloadStatus.done && xapkStatus != XapkUnzipStatus.FAILURE.name) { intent.putExtra(EntranceUtils.KEY_DATA, entity.toJson()) intent.putExtra(EntranceUtils.KEY_PATH, entity.path) intent.action = ACTION_INSTALL @@ -65,18 +68,23 @@ object DownloadNotificationHelper { .setContentIntent(pendingIntent) .setGroup(DOWNLOAD_GROUP_KEY) .setWhen(whenTime) - .setProgress(PROGRESS_MAX, entity.percent.toInt(), false) - when (entity.status) { - DownloadStatus.downloading -> builder.setContentText(String.format("%s(剩%s)", - SpeedUtils.getSpeed(entity.speed), - SpeedUtils.getRemainTime(entity.size, entity.progress, entity.speed * 1024))) - DownloadStatus.done -> builder.setContentText("下载完成,点击立即安装") - DownloadStatus.waiting -> builder.setContentText("等待中") - DownloadStatus.subscribe, - DownloadStatus.timeout, - DownloadStatus.neterror -> builder.setContentText("已暂停,连接WiFi自动下载") - else -> builder.setContentText("暂停中") + + if (xapkStatus == XapkUnzipStatus.FAILURE.name) { + builder.setContentText("《" + entity.name + "》解压失败,点击查看详情~") + } else { + when (entity.status) { + DownloadStatus.downloading -> builder.setContentText(String.format("%s(剩%s)", + SpeedUtils.getSpeed(entity.speed), + SpeedUtils.getRemainTime(entity.size, entity.progress, entity.speed * 1024))) + DownloadStatus.done -> builder.setContentText("下载完成,点击立即安装") + DownloadStatus.waiting -> builder.setContentText("等待中") + DownloadStatus.subscribe, + DownloadStatus.timeout, + DownloadStatus.neterror -> builder.setContentText("已暂停,连接WiFi自动下载") + else -> builder.setContentText("暂停中") + } + builder.setProgress(PROGRESS_MAX, entity.percent.toInt(), false) } when { @@ -91,6 +99,11 @@ object DownloadNotificationHelper { tryCatchInRelease { val notification = builder.build() // 可能会抛出异常 notification.flags = notification.flags or Notification.FLAG_NO_CLEAR + if (xapkStatus == XapkUnzipStatus.FAILURE.name) { + notification.flags = notification.flags or Notification.FLAG_AUTO_CANCEL + } else { + notification.flags = notification.flags or Notification.FLAG_NO_CLEAR + } if (entity.status == DownloadStatus.delete || entity.status == DownloadStatus.cancel @@ -98,7 +111,8 @@ object DownloadNotificationHelper { || entity.status == DownloadStatus.notfound || entity.status == DownloadStatus.overflow || (entity.status == DownloadStatus.done // 触发安装事件以后也 cancel 掉通知 - && !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty())) { + && !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty() + && xapkStatus != XapkUnzipStatus.FAILURE.name)) { requireUpdateNotificationGroupDelay = true notificationManager.cancel(entity.path, DOWNLOAD_NOTIFICATION_ID) } else { diff --git a/app/src/main/java/com/gh/common/util/DownloadObserver.kt b/app/src/main/java/com/gh/common/util/DownloadObserver.kt index 5931c38411..f437cf2288 100644 --- a/app/src/main/java/com/gh/common/util/DownloadObserver.kt +++ b/app/src/main/java/com/gh/common/util/DownloadObserver.kt @@ -6,6 +6,7 @@ import android.preference.PreferenceManager import com.gh.base.BaseActivity import com.gh.common.constant.Constants import com.gh.common.exposure.ExposureUtils +import com.gh.common.xapk.XapkInstaller import com.gh.download.DownloadManager import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.R @@ -43,6 +44,10 @@ object DownloadObserver { fun initObserver() { val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { + // todo 如何处理xapk安装问题 + val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] + if (!xapkStatus.isNullOrEmpty()) return + val gameId = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER) val downloadManager = DownloadManager.getInstance(HaloApp.getInstance().application) @@ -103,7 +108,7 @@ object DownloadObserver { MtaHelper.onEvent("软件更新", "下载完成") tryWithDefaultCatch { // 会有 ActivityNotFoundException 异常,catch 掉不管了 - mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path, true)) + PackageInstaller.install(mApplication, downloadEntity) } DataLogUtils.uploadUpgradeLog(mApplication, "install") //上传更新安装数据 } else { @@ -131,7 +136,7 @@ object DownloadObserver { if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) { downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES" tryWithDefaultCatch { - mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path)) + PackageInstaller.install(mApplication, downloadEntity) } } else { // 弹出卸载提示框 diff --git a/app/src/main/java/com/gh/common/util/Extensions.kt b/app/src/main/java/com/gh/common/util/Extensions.kt index 83943de99c..baea8c6bfd 100644 --- a/app/src/main/java/com/gh/common/util/Extensions.kt +++ b/app/src/main/java/com/gh/common/util/Extensions.kt @@ -277,6 +277,15 @@ fun throwExceptionInDebug(message: String = "", predicate: Boolean = true) { } } +/** + * 主动抛出异常 + */ +fun throwException(message: String = "", predicate: Boolean = true) { + if (predicate) { + throw RuntimeException(message) + } +} + /** * String related */ @@ -688,6 +697,17 @@ fun LottieAnimationView.doOnAnimationEnd(action: () -> Unit) { }) } +fun String?.getExtension(): String? { + this ?: return null + + tryCatchInRelease { + val lastDotIndex = this.lastIndexOf('.') + return if (lastDotIndex == -1) null else this.substring(lastDotIndex + 1) + } + return null +} + + /** * 检查内容是否一致 * @return true:相同 false:不同 diff --git a/app/src/main/java/com/gh/common/util/PackageInstaller.kt b/app/src/main/java/com/gh/common/util/PackageInstaller.kt new file mode 100644 index 0000000000..daff10a5d8 --- /dev/null +++ b/app/src/main/java/com/gh/common/util/PackageInstaller.kt @@ -0,0 +1,103 @@ +package com.gh.common.util + +import android.app.Application +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import androidx.core.content.FileProvider +import com.gh.common.constant.Constants +import com.gh.common.xapk.XapkInstaller +import com.gh.gamecenter.BuildConfig +import com.lightgame.download.DownloadEntity +import com.lightgame.utils.Utils +import java.io.File + +object PackageInstaller { + + /** + * 启动安装应用程序并取消通知栏安装消息 + * + * 兼容所有包安装(apk,xapk),后续可加上apks + */ + @JvmStatic + fun install(context: Context, downloadEntity: DownloadEntity) { + // 取消状态栏下载完成的通知,若存在 + downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES" + DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity) + + val pkgPath = downloadEntity.path + if (XapkInstaller.XAPK_EXTENSION_NAME == pkgPath.getExtension()) { + XapkInstaller.install(context, downloadEntity) + } else { + install(context, downloadEntity.path) + } + } + + /** + * 启动安装应用程序 + * + * 除非特殊情况,请勿使用此方法进行应用安装操作 + * 除非你已经确定该文件一定是Apk + */ + @JvmStatic + fun install(context: Context, pkgPath: String) { + try { + if (PackageUtils.isCanLaunchSetup(context, pkgPath)) { + val installIntent = getInstallIntent(context, pkgPath) + context.startActivity(installIntent) + } else { + DialogUtils.showPluginDialog(context) { + uninstall(context, pkgPath) + } + } + } catch (e: Exception) { + Utils.toast(context, e.message) + } + } + + /** + * 获取启动安装意图 + * + * 除非特殊情况,请勿使用此方法进行应用安装操作 + * 除非你已经确定该文件一定是Apk且该次安装无需检查签名情况 + */ + @JvmStatic + fun getInstallIntent(context: Context, path: String): Intent { + var uri = Uri.fromFile(File(path)) + val installIntent = Intent(Intent.ACTION_VIEW) + if ("smartisan" == Build.MANUFACTURER) { + installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, File(path)) + installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + installIntent.setDataAndType(uri, "application/vnd.android.package-archive") + installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } else { + // 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现 + // Application 上下文就更不用说了 + val pkgName = PackageUtils.getPackageNameByPath(context, path) + if (pkgName == context.packageName || context is Application) { + installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + installIntent.setDataAndType(uri, "application/vnd.android.package-archive") + } + InstallUtils.getInstance(context).addInstall(PackageUtils.getPackageNameByPath(context, path)) + return installIntent + } + + /** + * 卸载应用 + */ + @JvmStatic + fun uninstall(context: Context, path: String) { + val uninstallIntent = Intent() + uninstallIntent.action = Intent.ACTION_DELETE + uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT) + val packageName = PackageUtils.getPackageNameByPath(context, path) + uninstallIntent.data = Uri.parse("package:$packageName") + InstallUtils.getInstance(context).addUninstall(packageName) + context.startActivity(uninstallIntent) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/PackageUtils.java b/app/src/main/java/com/gh/common/util/PackageUtils.java index 9b34b6b9d8..9cd25382e1 100644 --- a/app/src/main/java/com/gh/common/util/PackageUtils.java +++ b/app/src/main/java/com/gh/common/util/PackageUtils.java @@ -1,7 +1,6 @@ package com.gh.common.util; import android.app.ActivityManager; -import android.app.Application; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -11,19 +10,18 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.text.TextUtils; +import androidx.annotation.Nullable; + import com.g00fy2.versioncompare.Version; -import com.gh.common.constant.Constants; import com.gh.gamecenter.BuildConfig; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.entity.GameUpdateEntity; import com.gh.gamecenter.manager.PackagesManager; import com.halo.assistant.HaloApp; -import com.lightgame.download.DownloadEntity; import com.lightgame.utils.Utils; import org.json.JSONArray; @@ -31,7 +29,6 @@ import org.json.JSONException; import org.json.JSONObject; import java.io.ByteArrayInputStream; -import java.io.File; import java.io.InputStream; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; @@ -42,9 +39,6 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import androidx.annotation.Nullable; -import androidx.core.content.FileProvider; - public class PackageUtils { public static final String publicKey = "OpenSSLRSAPublicKey{modulus=a8c4bb5748fec8d5c35db1a7a182d41ba4721a91131a417330af79ef4ddb43f9fa0ff4907b0a613bfe152de0ed8fc1b2e6f94a908aa98a5f7adc1ce814ba7ec919d75d9910bdfd8649b4789da6a90ffb61f0d23ac4f828a78fcd0d6f6120c1c43c1f87f7498a89eb40ca8e32dfc2f9d5c10d612b95192870223674e241e53305abf320d7eed76ded398778576e4db7b17b3bc6a792f13de5e43a6a5fae4276c73e6990ce97f68dff0ec16fc9594f175c8d49cd0d7877340d9de60942ca0efc737e50b6c295dfe0713e4532b4e810e1ea11b702b4a27753e41559cbceb247e7f044ec4e3ab2e8bccd8b9fd71286e63307550bcde86deee95adb8133076269135b,publicExponent=10001}"; @@ -232,29 +226,6 @@ public class PackageUtils { return ret; } - /* - * 启动安装应用程序 - */ - public static void launchSetup(final Context context, DownloadEntity downloadEntity) { - // 取消状态栏下载完成的通知,若存在 - downloadEntity.getMeta().put(Constants.MARK_ALREADY_TRIGGERED_INSTALLATION, "YES"); - DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity); - launchSetup(context, downloadEntity.getPath()); - } - - private static void launchSetup(final Context context, final String path) { - try { - if (isCanLaunchSetup(context, path)) { - context.startActivity(PackageUtils.getInstallIntent(context, path)); - } else { - DialogUtils.showPluginDialog(context, - () -> context.startActivity(PackageUtils.getUninstallIntent(context, path))); - } - } catch (Exception e) { - Utils.toast(context, e.getMessage()); - } - } - /** * 根据 path 获取 apk 信息确定处理方式 * @@ -291,37 +262,6 @@ public class PackageUtils { return compareSignatureBetweenInstalledAppWithApk(context, packageName, path); } - /* - * 根据路径,获取安装游戏的意图 - */ - public static Intent getInstallIntent(Context context, String path) { - return getInstallIntent(context, path, false); - } - - public static Intent getInstallIntent(Context context, String path, boolean isInAppUpdate) { - Uri uri = Uri.fromFile(new File(path)); - Intent installIntent = new Intent(Intent.ACTION_VIEW); - if ("smartisan".equals(Build.MANUFACTURER)) { - installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, new File(path)); - installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - installIntent.setDataAndType(uri, "application/vnd.android.package-archive"); - installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - } else { - // 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现 - // Application 上下文就更不用说了 - if (isInAppUpdate || context instanceof Application) { - installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - installIntent.setDataAndType(uri, "application/vnd.android.package-archive"); - } - InstallUtils.getInstance(context).addInstall(getPackageNameByPath(context, path)); - return installIntent; - } - /* * 根据包名,获取卸载游戏的意图 */ diff --git a/app/src/main/java/com/gh/common/view/DownloadProgressBar.java b/app/src/main/java/com/gh/common/view/DownloadProgressBar.java index 6cf25e48d8..de627e97c0 100644 --- a/app/src/main/java/com/gh/common/view/DownloadProgressBar.java +++ b/app/src/main/java/com/gh/common/view/DownloadProgressBar.java @@ -15,12 +15,12 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.widget.ProgressBar; -import com.gh.common.util.DisplayUtils; -import com.gh.gamecenter.R; - import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; +import com.gh.common.util.DisplayUtils; +import com.gh.gamecenter.R; + public class DownloadProgressBar extends ProgressBar { public static final int MAX_LENGTH = 1000; public static final int DOWNLOAD_NORMAL_STYLE = 0; @@ -38,7 +38,11 @@ public class DownloadProgressBar extends ProgressBar { DOWNLOADING_PLUGIN, RESERVABLE, RESERVED, - H5_GAME + H5_GAME, + + XAPK_UNZIPPING, + XAPK_SUCCESS, + XAPK_FAILURE, } private PorterDuffXfermode mDuffXFerMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN); @@ -214,6 +218,12 @@ public class DownloadProgressBar extends ProgressBar { setProgressDrawable(getResources().getDrawable(R.drawable.game_item_btn_pause_dn)); mDefaultColor = ContextCompat.getColor(getContext(), R.color.all_white); break; + case XAPK_FAILURE: + case XAPK_SUCCESS: + case XAPK_UNZIPPING: + setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_xapk_detail_style)); + mDefaultColor = ContextCompat.getColor(getContext(), R.color.all_white); + break; } mDownloadType = downloadType; diff --git a/app/src/main/java/com/gh/common/xapk/IXapkUnzipListener.kt b/app/src/main/java/com/gh/common/xapk/IXapkUnzipListener.kt new file mode 100644 index 0000000000..09bc393f18 --- /dev/null +++ b/app/src/main/java/com/gh/common/xapk/IXapkUnzipListener.kt @@ -0,0 +1,15 @@ +package com.gh.common.xapk + +import com.lightgame.download.DownloadEntity + +interface IXapkUnzipListener { + fun onProgress(downloadEntity: DownloadEntity, unzipPath: String, unzipSize: Long, unzipProgress: Long) + + fun onNext(downloadEntity: DownloadEntity, unzipPath: String) + + fun onCancel(downloadEntity: DownloadEntity) + + fun onFailure(downloadEntity: DownloadEntity, exception: Exception) + + fun onSuccess(downloadEntity: DownloadEntity) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt new file mode 100644 index 0000000000..4e6bacc762 --- /dev/null +++ b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt @@ -0,0 +1,163 @@ +package com.gh.common.xapk + +import android.content.Context +import com.gh.common.AppExecutor +import com.gh.common.util.* +import com.gh.download.DownloadManager +import com.halo.assistant.HaloApp +import com.lightgame.download.DataChanger +import com.lightgame.download.DownloadEntity +import com.lightgame.utils.Utils +import java.text.DecimalFormat +import java.util.* + +/** + * 目前已知的Xapk内容是:只有一个apk包和一个或做多个obb数据包(多余文件不解压,如果存在多个apk包,则以下安装无效) + * + * 每次安装都会重新跑一遍解压安装流程 + * 目前检验是否已解压的根据是存在相同文件名且大小一致(暂时想不到校验MD5值的方法) + * + * obb文件直接解压至根目录即可,无需操作文件 + * apk文件这解压的gh-download文件夹中 + */ +object XapkInstaller : IXapkUnzipListener { + private const val XAPK_PACKAGE_PATH_TAG = "xapk_package_path" + + // private const val XAPK_DATA_PATH_TAG = "xapk_data_path" + const val XAPK_EXTENSION_NAME = "xapk" + + // 通过解压过程存放于 DownloadEntity meta + const val XAPK_UNZIP_PERCENT = "unzip_percent" + const val XAPK_UNZIP_STATUS = "unzip_status" + + const val XAPK_DATA_EXTENSION_NAME = "obb" + const val PACKAGE_EXTENSION_NAME = "apk" + + private var mContext = HaloApp.getInstance().application.applicationContext + + // 是否需要开启特定线程处理 + private val mXapkUnzipThreadMap = Collections.synchronizedMap(HashMap()) + + // 按并行解压 + @JvmStatic + fun install(context: Context, downloadEntity: DownloadEntity) { + this.mContext = context + + val filePath = downloadEntity.path + if (XAPK_EXTENSION_NAME == filePath.getExtension()) { + unzipXapkFile(downloadEntity) + } else { + throwExceptionInDebug("如果时Apk包请使用PackageInstaller进行安装") + PackageInstaller.install(mContext, downloadEntity) + } + } + + private var startTime = 0L + + private fun unzipXapkFile(downloadEntity: DownloadEntity) { + if (mXapkUnzipThreadMap[downloadEntity.path] == null) { + startTime = System.currentTimeMillis() + val xapkUnzipThread = XapkUnzipThread(downloadEntity, this) + xapkUnzipThread.start() + mXapkUnzipThreadMap[downloadEntity.path] = xapkUnzipThread + } else { + debugOnly { + Utils.log("unzip", "重复解压,该文件解压已在队列中") + } + } + } + + override fun onProgress(downloadEntity: DownloadEntity, unzipPath: String, unzipSize: Long, unzipProgress: Long) { + AppExecutor.uiExecutor.execute { + val df = DecimalFormat("#.0") + var percent = 0.0 + tryCatchInRelease { + percent = df.format((unzipSize.toFloat() / unzipProgress) * 100).toDouble() + } + downloadEntity.meta[XAPK_UNZIP_PERCENT] = percent.toString() + downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.UNZIPPING.name + DataChanger.notifyDataChanged(downloadEntity) + } + + debugOnly { + Utils.log("unzip", "onProgress->" + (unzipSize.toFloat() / unzipProgress)) + } + } + + override fun onNext(downloadEntity: DownloadEntity, unzipPath: String) { + if (PACKAGE_EXTENSION_NAME == unzipPath.getExtension()) { + downloadEntity.meta[XAPK_PACKAGE_PATH_TAG] = unzipPath + } + + debugOnly { + Utils.log("unzip", "onNext->$unzipPath") + } + } + + /** + * 取消解压回调 + * + * 取消后的表现与下载完成一致 + */ + override fun onCancel(downloadEntity: DownloadEntity) { + mXapkUnzipThreadMap.remove(downloadEntity.path) + + AppExecutor.uiExecutor.execute { + downloadEntity.meta[XAPK_UNZIP_PERCENT] = "0.0" + downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.CANCEL.name + DataChanger.notifyDataChanged(downloadEntity) + DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity) + } + } + + override fun onFailure(downloadEntity: DownloadEntity, exception: Exception) { + mXapkUnzipThreadMap.remove(downloadEntity.path) + + AppExecutor.uiExecutor.execute { + downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.FAILURE.name + DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity) + DataChanger.notifyDataChanged(downloadEntity) + DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity) + } + + debugOnly { + Utils.log("unzip", "onFailure->$exception") + } + } + + override fun onSuccess(downloadEntity: DownloadEntity) { + mXapkUnzipThreadMap.remove(downloadEntity.path) + + AppExecutor.uiExecutor.execute { + val pkgPath = checkNotNull(downloadEntity.meta[XAPK_PACKAGE_PATH_TAG]) + PackageInstaller.install(mContext, pkgPath) + + downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0" + downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.SUCCESS.name + DataChanger.notifyDataChanged(downloadEntity) + DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity) + } + + debugOnly { + Utils.log("unzip", "onSuccess->${downloadEntity.path}") + Utils.log("unzip", "解压时长:" + (System.currentTimeMillis() - startTime)) + } + } + + @JvmStatic + fun cancelUnzipTask(downloadEntity: DownloadEntity) { + val xapkUnzipThread = mXapkUnzipThreadMap[downloadEntity.path] + if (xapkUnzipThread != null) { + xapkUnzipThread.canceled = true + } else { + onCancel(downloadEntity) // 刷新页面 + } + } +} + +enum class XapkUnzipStatus(status: String) { + UNZIPPING("unzipping"), + SUCCESS("success"), + CANCEL("cancel"), + FAILURE("failure"); +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/xapk/XapkUnzipThread.kt b/app/src/main/java/com/gh/common/xapk/XapkUnzipThread.kt new file mode 100644 index 0000000000..724b13bca6 --- /dev/null +++ b/app/src/main/java/com/gh/common/xapk/XapkUnzipThread.kt @@ -0,0 +1,106 @@ +package com.gh.common.xapk + +import android.os.Environment +import com.gh.common.util.getExtension +import com.gh.common.util.throwException +import com.gh.gamecenter.BuildConfig +import com.halo.assistant.HaloApp +import com.lightgame.download.DownloadEntity +import com.lightgame.download.FileUtils +import com.lightgame.utils.Utils +import java.io.File +import java.util.zip.ZipFile + +class XapkUnzipThread(private var mDownloadEntity: DownloadEntity, + private var mUnzipListener: IXapkUnzipListener) : Thread() { + + private val mDefaultBufferSize = 1024 * 1024 + + var canceled = false + + override fun run() { + super.run() + try { + val path = mDownloadEntity.path + val unzipSize = getUnzipSize(path) + + val msg = FileUtils.isCanDownload(HaloApp.getInstance().application, unzipSize) + if (!msg.isNullOrEmpty()) { + // 空间不足应该不用刷新页面,保持不变就好 + Utils.toast(HaloApp.getInstance().application, "设备存储空间不足,请清理后重试!") + return + } + var unzipProgress = 0L + val absolutePath = Environment.getExternalStorageDirectory().absolutePath + val xapkFile = File(path) + + ZipFile(xapkFile).use { zip -> + for (zipEntry in zip.entries().asSequence()) { + val outputFile = if (zipEntry.name.getExtension() == XapkInstaller.XAPK_DATA_EXTENSION_NAME) { + File(absolutePath + File.separator + zipEntry.name) + } else if (zipEntry.name.getExtension() == XapkInstaller.PACKAGE_EXTENSION_NAME) { + // apk文件名称 = xapk文件名+本身的文件名称 + val fileName = xapkFile.nameWithoutExtension + "_" + zipEntry.name + File(FileUtils.getDownloadPath(HaloApp.getInstance().application, fileName)) + } else continue // 暂时只需要解压xpk/obb文件 + + if (zipEntry.isDirectory) { + if (!outputFile.exists()) { + throwException("unzip create file path failure", !outputFile.mkdirs()) + } + continue + } + + if (!outputFile.parentFile.exists()) { + throwException("unzip create file path failure", !outputFile.parentFile.mkdirs()) + } + + // 如果存在同名且大小一致,可以认为该文件已经解压缩(在不解压缩的情况下如果如果获取压缩文件的MD5????) + if (!outputFile.exists()) { + throwException("unzip create file failure", !outputFile.createNewFile()) + } else if (outputFile.length() != zipEntry.size) { + throwException("unzip delete existing file failure", !outputFile.delete()) + throwException("unzip create file failure", !outputFile.createNewFile()) + } else { + unzipProgress += zipEntry.size + mUnzipListener.onProgress(mDownloadEntity, outputFile.path, unzipProgress, unzipSize) + mUnzipListener.onNext(mDownloadEntity, outputFile.path) + continue + } + + // unzip + zip.getInputStream(zipEntry).use { input -> + outputFile.outputStream().use { output -> + val buffer = ByteArray(mDefaultBufferSize) + var bytes = input.read(buffer) + while (bytes >= 0) { + output.write(buffer, 0, bytes) + unzipProgress += bytes + bytes = input.read(buffer) + if (canceled) { + mUnzipListener.onCancel(mDownloadEntity) + return + } else { + mUnzipListener.onProgress(mDownloadEntity, outputFile.path, unzipProgress, unzipSize) + } + } + } + } + mUnzipListener.onNext(mDownloadEntity, outputFile.path) + } + } + mUnzipListener.onSuccess(mDownloadEntity) + } catch (e: Exception) { + if (BuildConfig.DEBUG) throw e + mUnzipListener.onFailure(mDownloadEntity, e) + } + } + + private fun getUnzipSize(path: String): Long { + var totalSize = 0L + for (entry in ZipFile(File(path)).entries()) { + totalSize += entry.size + } + return totalSize + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/download/DownloadManager.java b/app/src/main/java/com/gh/download/DownloadManager.java index 20bb6bb74a..73ac77a767 100644 --- a/app/src/main/java/com/gh/download/DownloadManager.java +++ b/app/src/main/java/com/gh/download/DownloadManager.java @@ -22,12 +22,15 @@ import com.gh.common.util.AppDebugConfig; import com.gh.common.util.DataCollectionUtils; import com.gh.common.util.DeviceUtils; import com.gh.common.util.DialogUtils; +import com.gh.common.util.ExtensionsKt; import com.gh.common.util.GdtHelper; import com.gh.common.util.HomePluggableHelper; import com.gh.common.util.MD5Utils; import com.gh.common.util.NetworkUtils; import com.gh.common.util.PackageUtils; import com.gh.common.util.SPUtils; +import com.gh.common.xapk.XapkInstaller; +import com.gh.gamecenter.BuildConfig; import com.gh.gamecenter.entity.ApkEntity; import com.gh.gamecenter.entity.GameEntity; import com.gh.gamecenter.entity.GameUpdateEntity; @@ -243,8 +246,15 @@ public class DownloadManager implements DownloadStatusListener { DownloadEntity downloadEntity = new DownloadEntity(); downloadEntity.setUrl(apkEntity.getUrl()); downloadEntity.setName(gameEntity.getName()); + // todo test + String fileExtension = "apk"; + if (BuildConfig.DEBUG) { + if (XapkInstaller.XAPK_EXTENSION_NAME.equals(ExtensionsKt.getExtension(apkEntity.getUrl()))) { + fileExtension = XapkInstaller.XAPK_EXTENSION_NAME; + } + } downloadEntity.setPath(FileUtils.getDownloadPath(context, - MD5Utils.getContentMD5(gameEntity.getName() + "_" + System.currentTimeMillis()) + ".apk")); + MD5Utils.getContentMD5(gameEntity.getName() + "_" + System.currentTimeMillis()) + "." + fileExtension)); downloadEntity.setETag(apkEntity.getEtag()); downloadEntity.setIcon(gameEntity.getIcon()); downloadEntity.setPlatform(apkEntity.getPlatform()); diff --git a/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt b/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt index ac531eb480..63fce2a68e 100644 --- a/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt +++ b/app/src/main/java/com/gh/download/dialog/DownloadDialogItemViewHolder.kt @@ -211,10 +211,10 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas } else { if (PackageUtils.isCanPluggable(apkEntity)) { DialogUtils.showPluginDialog(it.context) { - it.context.startActivity(PackageUtils.getUninstallIntent(it.context, downloadEntity.path)) + PackageInstaller.uninstall(it.context, downloadEntity.path) } } else { - PackageUtils.launchSetup(it.context, downloadEntity) + PackageInstaller.install(it.context, downloadEntity) } } diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index c1bc8b80dd..38159ac0d5 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -53,6 +53,7 @@ import com.gh.common.util.LogUtils; import com.gh.common.util.LunchType; import com.gh.common.util.MtaHelper; import com.gh.common.util.NotificationHelper; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.PlatformUtils; import com.gh.common.util.PushHelper; @@ -444,7 +445,7 @@ public class MainActivity extends BaseActivity { "提示", msg, "直接退出", "立即安装", - () -> handler.postDelayed(() -> PackageUtils.launchSetup(MainActivity.this, finalDownloadEntity), 200), + () -> handler.postDelayed(() -> PackageInstaller.install(MainActivity.this, finalDownloadEntity), 200), this::finish); return true; } @@ -772,7 +773,7 @@ public class MainActivity extends BaseActivity { // "操作", "卸载完成", // "网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().getApplication())); - startActivity(PackageUtils.getInstallIntent(this, mDownloadEntity.getPath())); + PackageInstaller.install(this, mDownloadEntity); } // 更新已安装游戏 diff --git a/app/src/main/java/com/gh/gamecenter/adapter/CleanApkAdapter.java b/app/src/main/java/com/gh/gamecenter/adapter/CleanApkAdapter.java index 85ae97651c..f646c2130d 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/CleanApkAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/CleanApkAdapter.java @@ -11,9 +11,12 @@ import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.TextView; +import androidx.collection.ArrayMap; +import androidx.core.content.ContextCompat; + import com.gh.common.util.BitmapUtils; import com.gh.common.util.MtaHelper; -import com.gh.common.util.PackageUtils; +import com.gh.common.util.PackageInstaller; import com.gh.gamecenter.R; import com.gh.gamecenter.adapter.viewholder.KcSelectGameViewHolder; import com.gh.gamecenter.entity.InstallGameEntity; @@ -28,8 +31,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -import androidx.collection.ArrayMap; -import androidx.core.content.ContextCompat; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; @@ -74,17 +75,17 @@ public class CleanApkAdapter extends BaseRecyclerAdapter private void init() { Observable.create(new ObservableOnSubscribe() { - @Override - public void subscribe(ObservableEmitter emitter) { - // 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError - FindAllAPKPath(Environment.getExternalStorageDirectory()); - LoadApkData(); - emitter.onComplete(); - } - }) + @Override + public void subscribe(ObservableEmitter emitter) { + // 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError + FindAllAPKPath(Environment.getExternalStorageDirectory()); + LoadApkData(); + emitter.onComplete(); + } + }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(new Response(){ + .subscribe(new Response() { @Override public void onComplete() { super.onComplete(); @@ -264,7 +265,7 @@ public class CleanApkAdapter extends BaseRecyclerAdapter @Override public void onClick(View v) { if (mIsScanOver) { - mContext.startActivity(PackageUtils.getInstallIntent(mContext, gameEntity.getGamePath())); + mContext.startActivity(PackageInstaller.getInstallIntent(mContext, gameEntity.getGamePath())); } } }); diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java index 1baf8bde29..6f8f350ee9 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.java @@ -5,6 +5,10 @@ import android.content.Intent; import android.text.TextUtils; import android.view.View; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentActivity; + import com.gh.common.dialog.CertificationDialog; import com.gh.common.dialog.DeviceRemindDialog; import com.gh.common.dialog.GameOffServiceDialogFragment; @@ -19,6 +23,7 @@ import com.gh.common.util.DialogUtils; import com.gh.common.util.DownloadDialogHelper; import com.gh.common.util.LogUtils; import com.gh.common.util.MtaHelper; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.PermissionHelper; import com.gh.common.util.ReservationHelper; @@ -40,10 +45,6 @@ import com.lightgame.utils.Utils; import org.greenrobot.eventbus.EventBus; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentActivity; - /** * Created by khy on 27/06/17. * 详情页面下载ViewHolder @@ -135,16 +136,8 @@ public class DetailViewHolder { MtaHelper.onEvent("游戏详情_新", "更新", mGameEntity.getName()); } - String autoDownloadPlatform = v.getTag() instanceof String ? (String) v.getTag() : ""; v.setTag(null); switch (mViewHolder.mDownloadPb.getDownloadType()) { - case DOWNLOADING_PLUGIN: - case DOWNLOADING_NORMAL: - Intent intent = DownloadManagerActivity.getDownloadMangerIntent(mViewHolder.context, - mGameEntity.getApk().get(0).getUrl(), - StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])")); - mViewHolder.context.startActivity(intent); - break; case NONE_WITH_HINT: case NONE: String offStatus = mGameEntity.getDownloadOffStatus(); @@ -212,7 +205,7 @@ public class DetailViewHolder { mDownloadEntity = DownloadManager.getInstance(mViewHolder.context).getDownloadEntityByUrl(mGameEntity.getApk().get(0).getUrl()); } if (mDownloadEntity != null) { - PackageUtils.launchSetup(mViewHolder.context, mDownloadEntity); + PackageInstaller.install(mViewHolder.context, mDownloadEntity); } }); break; @@ -248,14 +241,21 @@ public class DetailViewHolder { MtaHelper.onEvent("H5页面", "入口", "详情页_" + mGameEntity.getName()); LinkEntity linkEntity = mGameEntity.getH5Link(); - boolean isPlay = "play".equals(linkEntity.getType()); // 是否为开始玩 + boolean isPlay = "play".equals(linkEntity.getType()); // 是否为开始玩 if (isPlay) { HistoryHelper.insertGameEntity(mGameEntity); } - Intent i = new Intent(WebActivity.getIntentForWebGame(mViewHolder.context, linkEntity.getLink(), mGameEntity.getName(),isPlay)); + Intent i = new Intent(WebActivity.getIntentForWebGame(mViewHolder.context, linkEntity.getLink(), mGameEntity.getName(), isPlay)); mViewHolder.context.startActivity(i); break; + + default: + Intent intent = DownloadManagerActivity.getDownloadMangerIntent(mViewHolder.context, + mGameEntity.getApk().get(0).getUrl(), + StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])")); + mViewHolder.context.startActivity(intent); + break; } } diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameDownloadViewHolder.java b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameDownloadViewHolder.java index 3ea5713e13..efc0f1e21f 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameDownloadViewHolder.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameDownloadViewHolder.java @@ -37,6 +37,9 @@ public class GameDownloadViewHolder extends BaseRecyclerViewHolder { @BindView(R.id.dm_item_tv_startorpause) public TextView dmStartorpause; + @BindView(R.id.unzip_failure_hint) + public View unzipFailureHint; + public GameDownloadViewHolder(View itemView) { super(itemView); } diff --git a/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt b/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt index cf3fb1dc4f..c5e26fb44f 100644 --- a/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/amway/AmwayFragment.kt @@ -7,11 +7,14 @@ import android.view.View import android.view.ViewGroup import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton import com.gh.common.TimeElapsedHelper import com.gh.common.exposure.ExposureListener import com.gh.common.util.* import com.gh.common.view.VerticalItemDecoration +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.amway.search.AmwaySearchActivity @@ -42,6 +45,10 @@ class AmwayFragment : ListFragment() { private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mAdapter?.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } } } @@ -201,4 +208,18 @@ class AmwayFragment : ListFragment() { } } + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mAdapter?.getGameEntityByPackage(downloadEntity.packageName) ?: return + for (gameAndPosition in data) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.position) + if (targetView != null) { + if (targetView is RecyclerView) { + // todo 如果时竖向专题该怎么判断? + } else { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListAdapter.kt b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListAdapter.kt index 90f011a286..affa700591 100644 --- a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListAdapter.kt @@ -28,9 +28,10 @@ class NewCategoryListAdapter(context: Context, private val mViewModel: NewCategoryListViewModel, private val mEntrance: String?) : ListAdapter(context), IExposable { - private val mPositionAndPackageMap = HashMap() private val mExposureEventSparseArray: SparseArray = SparseArray() + val positionAndPackageMap = HashMap() + override fun setListData(updateData: MutableList?) { // 记录游戏位置 if (updateData != null) { @@ -40,14 +41,14 @@ class NewCategoryListAdapter(context: Context, for (apkEntity in gameEntity.getApk()) { packages += apkEntity.packageName } - mPositionAndPackageMap[packages + i] = i + positionAndPackageMap[packages + i] = i } } super.setListData(updateData) } fun clearPositionAndPackageMap() { - mPositionAndPackageMap.clear() + positionAndPackageMap.clear() } override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean { @@ -128,9 +129,9 @@ class NewCategoryListAdapter(context: Context, } fun notifyItemByDownload(download: DownloadEntity) { - for (key in mPositionAndPackageMap.keys) { + for (key in positionAndPackageMap.keys) { if (key.contains(download.packageName) && key.contains(download.gameId)) { - val position = mPositionAndPackageMap[key] + val position = positionAndPackageMap[key] if (position != null && mEntityList != null && position < mEntityList.size) { mEntityList[position].getEntryMap()[download.platform] = download notifyItemChanged(position) @@ -140,9 +141,9 @@ class NewCategoryListAdapter(context: Context, } fun notifyItemAndRemoveDownload(status: EBDownloadStatus) { - for (key in mPositionAndPackageMap.keys) { + for (key in positionAndPackageMap.keys) { if (key.contains(status.packageName) && key.contains(status.gameId)) { - val position = mPositionAndPackageMap[key] + val position = positionAndPackageMap[key] if (position != null && mEntityList != null && position < mEntityList.size) { mEntityList[position].getEntryMap().remove(status.platform) notifyItemChanged(position) diff --git a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt index 226eed75ce..ad65531877 100644 --- a/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category/NewCategoryListFragment.kt @@ -8,10 +8,13 @@ import butterknife.BindView import com.ethanhua.skeleton.Skeleton import com.gh.common.constant.Constants import com.gh.common.exposure.ExposureListener +import com.gh.common.util.DialogUtils import com.gh.common.util.EntranceUtils import com.gh.common.util.observeNonNull import com.gh.common.util.viewModelProvider import com.gh.common.view.ConfigFilterView +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter @@ -22,6 +25,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity +import com.lightgame.utils.Utils import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -29,8 +33,10 @@ class NewCategoryListFragment : ListFragment @@ -154,4 +164,16 @@ class NewCategoryListFragment : ListFragment { downloadEntity = downloadingList.get(position - doneList.size() - 2); } String icon = downloadEntity.getIcon(); - if (!TextUtils.isEmpty(icon) && icon.contains("KuaiChuanIcon")) { // 来自快传的apk - try { - JSONObject jsonObject = new JSONObject(icon); - String apkPath = jsonObject.getString("KuaiChuanIcon"); - android.content.pm.PackageManager pm = mContext.getPackageManager(); - PackageInfo info = pm.getPackageArchiveInfo(apkPath, - android.content.pm.PackageManager.GET_ACTIVITIES); - if (info != null) { - ApplicationInfo appInfo = info.applicationInfo; - appInfo.sourceDir = apkPath; - appInfo.publicSourceDir = apkPath; - Bitmap bitmap = BitmapUtils.drawableToBitmap(appInfo.loadIcon(pm), true); - viewHolder.dmIcon.setImageBitmap(bitmap); - } - } catch (JSONException e) { - e.printStackTrace(); - } - - } else if (!TextUtils.isEmpty(icon)) { -// viewHolder.dmIcon.setImageURI(icon); + if (!TextUtils.isEmpty(icon)) { ImageUtils.display(viewHolder.dmIcon, icon); } else { ImageUtils.display(viewHolder.dmIcon, R.mipmap.logo); @@ -175,6 +153,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.dmDownloads.setText("下载完成"); viewHolder.dmDelete.setVisibility(View.VISIBLE); viewHolder.dmStartorpause.setTextColor(Color.WHITE); + if (downloadEntity.isPluggable() && PackagesManager.INSTANCE.isInstalled(downloadEntity.getPackageName())) { viewHolder.dmStartorpause.setText("安装"); @@ -246,7 +225,29 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { viewHolder.dmSpeed.setText(downloadEntity.getPercent() + "%"); } - viewHolder.dmProgressbar.setProgress((int) (downloadEntity.getPercent() * 10)); + String xapkStatus = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS); + if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) { + viewHolder.dmStartorpause.setText("取消"); + + String percent = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_PERCENT); + viewHolder.dmProgressbar.setProgress((int) (Float.parseFloat(percent == null ? "0" : percent) * 10)); + viewHolder.dmProgressbar.setProgressDrawable(mContext.getResources().getDrawable(R.drawable.progressbar_xapk_style)); + + viewHolder.dmSpeed.setText((percent + "%")); + + viewHolder.dmDownloads.setText("解压中"); + viewHolder.unzipFailureHint.setVisibility(View.GONE); + } else if (XapkUnzipStatus.FAILURE.name().equals(xapkStatus)) { + viewHolder.dmProgressbar.setProgress((int) (downloadEntity.getPercent() * 10)); + viewHolder.dmProgressbar.setProgressDrawable(mContext.getResources().getDrawable(R.drawable.progressbar_bg_style)); + viewHolder.dmDownloads.setTextColor(ContextCompat.getColor(mContext, R.color.text_F10000)); + viewHolder.unzipFailureHint.setVisibility(View.VISIBLE); + viewHolder.dmDownloads.setText("解压失败"); + } else { + viewHolder.dmProgressbar.setProgress((int) (downloadEntity.getPercent() * 10)); + viewHolder.dmProgressbar.setProgressDrawable(mContext.getResources().getDrawable(R.drawable.progressbar_bg_style)); + viewHolder.unzipFailureHint.setVisibility(View.GONE); + } viewHolder.dmStartorpause.setOnClickListener(v -> { String str = ((TextView) v).getText().toString(); @@ -296,11 +297,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { Utils.toast(mContext, R.string.install_failure_hint); removeDownload(downloadEntity); } else { - if (downloadEntity.getName().contains("光环助手")) { - mContext.startActivity(PackageUtils.getInstallIntent(mContext, path)); - } else { - PackageUtils.launchSetup(mContext, downloadEntity); - } + PackageInstaller.install(mContext, downloadEntity); } } break; @@ -339,14 +336,18 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { , "安装包数据校验失败,无法完成下载,建议删除任务重新下载" , "知道了", null, null, null); break; - + case "取消": + XapkInstaller.cancelUnzipTask(downloadEntity); + break; } // DataUtils.onMtaEvent(HaloApp.getInstance().getApplication(), "下载管理", "游戏下载", str); }); viewHolder.dmDownloads.setOnClickListener(v -> { - if (viewHolder.dmDelete.getVisibility() == View.VISIBLE) { + if (viewHolder.unzipFailureHint.getVisibility() == View.VISIBLE) { + DialogUtils.showUnzipFailureDialog(mContext, downloadEntity); + } else if (viewHolder.dmDelete.getVisibility() == View.VISIBLE) { showDeleteDialog(downloadEntity, viewHolder.getPosition()); } }); @@ -486,7 +487,7 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { if (FileUtils.isEmptyFile(path)) { Utils.toast(mContext, R.string.install_failure_hint); } else { - mContext.startActivity(PackageUtils.getUninstallIntent(mContext, path)); + PackageInstaller.uninstall(mContext, path); } }); } @@ -559,13 +560,21 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter { // 显示删除提示框 private void showDeleteDialog(final DownloadEntity entry, final int position) { + String xapkStatus = entry.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS); String msg; - if (doneList.size() != 0 && position <= doneList.size()) { + if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) { + msg = "游戏正在解压安装哦,确定删除?"; + } else if (doneList.size() != 0 && position <= doneList.size()) { msg = "游戏还没安装哦,确定删除?"; } else { msg = "游戏还没下载完,确定删除?"; } - DialogUtils.showWarningDialog(mContext, "删除下载", msg, () -> removeDownload(entry)); + DialogUtils.showWarningDialog(mContext, "删除下载", msg, () -> { + removeDownload(entry); + if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) { + XapkInstaller.cancelUnzipTask(entry); + } + }); } public void initLocationMap() { diff --git a/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java b/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java index 60e244ebbf..0984898c17 100644 --- a/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java +++ b/app/src/main/java/com/gh/gamecenter/download/GameUpdateFragmentAdapter.java @@ -29,6 +29,7 @@ import com.gh.common.util.GsonUtils; import com.gh.common.util.ImageUtils; import com.gh.common.util.MD5Utils; import com.gh.common.util.NetworkUtils; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.PermissionHelper; import com.gh.common.util.PlatformUtils; @@ -380,7 +381,7 @@ class GameUpdateFragmentAdapter extends BaseRecyclerAdapter implemen notifyItemChanged(0); EventBus.getDefault().post(new EBSkip(DownloadManagerActivity.TAG, DownloadManagerActivity.INDEX_DOWNLOAD)); } else if (mContext.getString(R.string.install).equals(str)) { - PackageUtils.launchSetup(mContext, DownloadManager.getInstance(mContext).getDownloadEntityByUrl(updateEntity.getUrl())); + PackageInstaller.install(mContext, DownloadManager.getInstance(mContext).getDownloadEntityByUrl(updateEntity.getUrl())); } // MtaHelper.onEvent("下载管理", "游戏更新", str); }); @@ -536,7 +537,7 @@ class GameUpdateFragmentAdapter extends BaseRecyclerAdapter implemen } else { downloadType = ExposureUtils.DownloadType.UPDATE; } - + GameEntity gameEntity = new GameEntity(updateEntity.getId(), updateEntity.getName()); gameEntity.setGameVersion(updateEntity.getVersion()); diff --git a/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java b/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java index c7eb09ccae..8c4c8330d6 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java +++ b/app/src/main/java/com/gh/gamecenter/fragment/SearchToolbarFragment.java @@ -13,6 +13,9 @@ import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.lifecycle.ViewModelProviders; + import com.gh.base.SearchBarHint; import com.gh.base.fragment.BaseLazyFragment; import com.gh.common.constant.Config; @@ -41,8 +44,6 @@ import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; import java.util.List; -import androidx.annotation.Nullable; -import androidx.lifecycle.ViewModelProviders; import butterknife.BindView; diff --git a/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt b/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt index 0d97af5054..8aa9688e26 100644 --- a/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/game/GameFragment.kt @@ -11,11 +11,10 @@ import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton import com.ethanhua.skeleton.ViewSkeletonScreen import com.gh.common.exposure.ExposureListener -import com.gh.common.util.EntranceUtils -import com.gh.common.util.dip2px -import com.gh.common.util.toJson -import com.gh.common.util.visibleIf +import com.gh.common.util.* import com.gh.common.view.FixLinearLayoutManager +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.LoadStatus @@ -54,6 +53,10 @@ class GameFragment : NormalFragment() { private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mListAdapter.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } } } @@ -203,4 +206,18 @@ class GameFragment : NormalFragment() { } } + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mListAdapter.getGameEntityByPackage(downloadEntity.packageName) + for (gameAndPosition in data) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.position) + if (targetView != null) { + if (targetView is RecyclerView) { + // todo 如果时竖向专题该怎么判断? + } else { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt index 47e333d1c9..162756383c 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailFragment.kt @@ -25,6 +25,8 @@ import com.gh.common.exposure.ExposureEvent import com.gh.common.util.* import com.gh.common.view.FlexLinearLayout import com.gh.common.view.TabIndicatorView +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R @@ -191,6 +193,10 @@ class GameDetailFragment : NormalFragment() { mDownloadEntity = downloadEntity DetailDownloadUtils.detailInvalidate(detailViewHolder) } + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + } } } } diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt index 4daeffad0c..48d30987b5 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/video/TopVideoView.kt @@ -15,7 +15,6 @@ import com.gh.common.constant.Constants import com.gh.common.observer.MuteCallback import com.gh.common.observer.VolumeObserver import com.gh.common.util.* -import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.entity.GameDetailEntity import com.gh.gamecenter.gamedetail.GameDetailViewModel diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt index 4e2209e6c7..e9d7952301 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt @@ -10,9 +10,12 @@ import androidx.recyclerview.widget.RecyclerView import com.gh.base.fragment.BaseFragment import com.gh.common.TimeElapsedHelper import com.gh.common.exposure.ExposureListener +import com.gh.common.util.DialogUtils import com.gh.common.util.MtaHelper import com.gh.common.util.observeNonNull import com.gh.common.view.FixLinearLayoutManager +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.LoadStatus @@ -44,6 +47,10 @@ class HomeFragment : BaseFragment() { private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mListAdapter.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } } } @@ -153,4 +160,19 @@ class HomeFragment : BaseFragment() { mLayoutManager.scrollToPosition(0) } } + + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mListAdapter.getGameEntityByPackage(downloadEntity.packageName) + for (gameAndPosition in data) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.position) + if (targetView != null) { + if (targetView is RecyclerView) { + // todo 如果时竖向专题该怎么判断? + } else { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java index 56b9b5f86c..395fec7f14 100644 --- a/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java +++ b/app/src/main/java/com/gh/gamecenter/manager/UpdateManager.java @@ -22,6 +22,7 @@ import com.gh.common.util.DisplayUtils; import com.gh.common.util.MD5Utils; import com.gh.common.util.MtaHelper; import com.gh.common.util.NetworkUtils; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.SpeedUtils; import com.gh.download.DownloadManager; @@ -272,7 +273,7 @@ public class UpdateManager { File file = new File(path); if (file.exists() && file.length() > 0) { DataLogUtils.uploadUpgradeLog(mContext, "install"); //上传更新安装数据 - mContext.startActivity(PackageUtils.getInstallIntent(mContext, path, true)); + PackageInstaller.install(mContext, path); } else { MtaHelper.onEvent("软件更新", "下载开始"); showDownloadDialog(md5); diff --git a/app/src/main/java/com/gh/gamecenter/receiver/InstallReceiver.java b/app/src/main/java/com/gh/gamecenter/receiver/InstallReceiver.java index 7a880cce14..296ac5e6c1 100644 --- a/app/src/main/java/com/gh/gamecenter/receiver/InstallReceiver.java +++ b/app/src/main/java/com/gh/gamecenter/receiver/InstallReceiver.java @@ -12,6 +12,7 @@ import com.gh.common.util.DownloadNotificationHelper; import com.gh.common.util.EntranceUtils; import com.gh.common.util.ExtensionsKt; import com.gh.common.util.GsonUtils; +import com.gh.common.util.PackageInstaller; import com.gh.common.util.PackageUtils; import com.gh.common.util.RunningUtils; import com.gh.gamecenter.DownloadManagerActivity; @@ -29,19 +30,26 @@ import static com.gh.common.util.EntranceUtils.KEY_TO; * notification安装跳转 */ public class InstallReceiver extends BroadcastReceiver { - + @Override public void onReceive(Context context, Intent intent) { ExtensionsKt.doOnMainProcessOnly(context, () -> { String path = intent.getStringExtra(EntranceUtils.KEY_PATH); - String downloadEntityString = intent.getStringExtra(EntranceUtils.KEY_DATA); - - updateNotification(downloadEntityString); - + DownloadEntity downloadEntity = null; + try { + String downloadEntityString = intent.getStringExtra(EntranceUtils.KEY_DATA); + downloadEntity = GsonUtils.fromJson(downloadEntityString, DownloadEntity.class); + } catch (Exception e) { + e.printStackTrace(); + } + updateNotification(downloadEntity); + if (PackageUtils.isCanLaunchSetup(context, path)) { - Intent installIntent = PackageUtils.getInstallIntent(context, path); - installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(installIntent); + if (downloadEntity != null) { + PackageInstaller.install(context, downloadEntity); + } else { + PackageInstaller.install(context, path); + } } else { if (RunningUtils.isRunning(context)) { if (RunningUtils.isEqualsTop(context, DownloadManagerActivity.class.getName())) { @@ -52,7 +60,7 @@ public class InstallReceiver extends BroadcastReceiver { intent2.setComponent(new ComponentName(context, DownloadManagerActivity.class)); intent2.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); context.startActivity(intent2); - + EventBus.getDefault().post(new EBMiPush("plugin_install", path)); } else { Intent intent2 = new Intent(context, DownloadManagerActivity.class); @@ -74,15 +82,15 @@ public class InstallReceiver extends BroadcastReceiver { } }); } - - private void updateNotification(String downloadEntityString) { + + private void updateNotification(DownloadEntity downloadEntity) { try { - DownloadEntity downloadEntity = GsonUtils.fromJson(downloadEntityString, DownloadEntity.class); - downloadEntity.getMeta().put(Constants.MARK_ALREADY_TRIGGERED_INSTALLATION, "YES"); - DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity); + if (downloadEntity != null) { + downloadEntity.getMeta().put(Constants.MARK_ALREADY_TRIGGERED_INSTALLATION, "YES"); + DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity); + } } catch (Exception e) { e.printStackTrace(); } } - } diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt index 3519af110d..fd9d2aa285 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt @@ -39,10 +39,11 @@ class SearchGameIndexAdapter(context: Context, val key: String) : ListAdapter(context), IExposable { private val dao = SearchHistoryDao(mContext) - private val positionAndPackageMap = HashMap() private var exposureEventArray: SparseArray? = null private var searchMap: ArrayMap = ArrayMap() + val positionAndPackageMap = HashMap() + override fun setListData(updateData: MutableList?) { exposureEventArray = SparseArray(updateData?.size ?: 0) // 记录游戏位置 diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt index 20e04ab8de..887eace92e 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt @@ -18,11 +18,11 @@ import androidx.recyclerview.widget.RecyclerView import butterknife.BindView import butterknife.OnClick import com.gh.common.constant.Config +import com.gh.common.constant.ItemViewType import com.gh.common.exposure.ExposureListener -import com.gh.common.util.AdHelper -import com.gh.common.util.DirectUtils -import com.gh.common.util.DisplayUtils -import com.gh.common.util.MtaHelper +import com.gh.common.util.* +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.SuggestionActivity @@ -45,6 +45,7 @@ class SearchGameIndexFragment : ListFragment = ArrayMap() private var exposureEventArray: SparseArray? = null - private val positionAndPackageMap = HashMap() private var mDao: SearchHistoryDao = SearchHistoryDao(mContext) private val ITEM_SEARCH_FOOTER = 109 + val positionAndPackageMap = HashMap() + override fun setListData(updateData: MutableList?) { exposureEventArray = SparseArray(updateData?.size ?: 0) // 记录游戏位置 diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt index 9151f00135..5b0607e354 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt @@ -20,6 +20,8 @@ import butterknife.OnClick import com.gh.common.constant.Config import com.gh.common.exposure.ExposureListener import com.gh.common.util.* +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.SuggestionActivity @@ -33,6 +35,7 @@ import com.gh.gamecenter.suggest.SuggestType import com.halo.assistant.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity +import com.lightgame.utils.Utils import io.reactivex.Observable import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode @@ -41,8 +44,10 @@ class SearchGameResultFragment : ListFragment(context), IExposable { private val mExposureEventSparseArray: SparseArray = SparseArray() - private val positionAndPackageMap = HashMap() - private val subjectData = mViewModel.subjectData + val positionAndPackageMap = HashMap() + override fun setListData(updateData: MutableList?) { positionAndPackageMap.clear() // 记录游戏位置 diff --git a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt index 5460dbb975..7e362f57da 100644 --- a/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/subject/SubjectListFragment.kt @@ -6,10 +6,13 @@ import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.RecyclerView import com.ethanhua.skeleton.Skeleton import com.gh.common.exposure.ExposureListener +import com.gh.common.util.DialogUtils import com.gh.common.util.DisplayUtils import com.gh.common.util.EntranceUtils import com.gh.common.util.viewModelProviderFromParent import com.gh.common.view.SpacingItemDecoration +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListFragment @@ -41,6 +44,10 @@ class SubjectListFragment : ListFragment(), On private val dataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mAdapter?.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } } } @@ -152,4 +159,16 @@ class SubjectListFragment : ListFragment(), On return false } + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mAdapter?.positionAndPackageMap ?: return + for (gameAndPosition in data) { + if (gameAndPosition.key.contains(downloadEntity.packageName)) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value) + if (targetView != null) { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/tag/TagsListAdapter.kt b/app/src/main/java/com/gh/gamecenter/tag/TagsListAdapter.kt index 045fcb2262..5aa69e1f41 100644 --- a/app/src/main/java/com/gh/gamecenter/tag/TagsListAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/tag/TagsListAdapter.kt @@ -25,9 +25,10 @@ class TagsListAdapter(context: Context, private val mViewModel: TagsListViewModel, private val mEntrance: String?) : ListAdapter(context), IExposable { - private val mPositionAndPackageMap = HashMap() private val mExposureEventSparseArray: SparseArray = SparseArray() + val positionAndPackageMap = HashMap() + override fun setListData(updateData: MutableList?) { // 记录游戏位置 if (updateData != null) { @@ -37,14 +38,14 @@ class TagsListAdapter(context: Context, for (apkEntity in gameEntity.getApk()) { packages += apkEntity.packageName } - mPositionAndPackageMap[packages + i] = i + positionAndPackageMap[packages + i] = i } } super.setListData(updateData) } fun clearPositionAndPackageMap() { - mPositionAndPackageMap.clear() + positionAndPackageMap.clear() } override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean { @@ -115,9 +116,9 @@ class TagsListAdapter(context: Context, } fun notifyItemByDownload(download: DownloadEntity) { - for (key in mPositionAndPackageMap.keys) { + for (key in positionAndPackageMap.keys) { if (key.contains(download.packageName)&& key.contains(download.gameId)) { - val position = mPositionAndPackageMap[key] + val position = positionAndPackageMap[key] if (position != null && mEntityList != null && position < mEntityList.size) { mEntityList[position].getEntryMap()[download.platform] = download notifyItemChanged(position) @@ -127,9 +128,9 @@ class TagsListAdapter(context: Context, } fun notifyItemAndRemoveDownload(status: EBDownloadStatus) { - for (key in mPositionAndPackageMap.keys) { + for (key in positionAndPackageMap.keys) { if (key.contains(status.packageName)&& key.contains(status.gameId)) { - val position = mPositionAndPackageMap[key] + val position = positionAndPackageMap[key] if (position != null && mEntityList != null && position < mEntityList.size) { mEntityList[position].getEntryMap().remove(status.platform) notifyItemChanged(position) diff --git a/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt b/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt index 9a4cbee5ba..ad554f2cef 100644 --- a/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/tag/TagsListFragment.kt @@ -8,10 +8,13 @@ import butterknife.BindView import com.ethanhua.skeleton.Skeleton import com.gh.common.exposure.ExposureListener import com.gh.common.exposure.ExposureSource +import com.gh.common.util.DialogUtils import com.gh.common.util.observeNonNull import com.gh.common.util.viewModelProvider import com.gh.common.view.ConfigFilterView import com.gh.common.view.HorizontalItemDecoration +import com.gh.common.xapk.XapkInstaller +import com.gh.common.xapk.XapkUnzipStatus import com.gh.download.DownloadManager import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListAdapter @@ -42,6 +45,10 @@ class TagsListFragment : ListFragment() { private val mDataWatcher = object : DataWatcher() { override fun onDataChanged(downloadEntity: DownloadEntity) { mAdapter?.notifyItemByDownload(downloadEntity) + + if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) { + showUnzipFailureDialog(downloadEntity) + } } } @@ -171,4 +178,16 @@ class TagsListFragment : ListFragment() { } } + fun showUnzipFailureDialog(downloadEntity: DownloadEntity) { + val data = mAdapter?.positionAndPackageMap ?: return + for (gameAndPosition in data) { + if (gameAndPosition.key.contains(downloadEntity.packageName)) { + val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value) + if (targetView != null) { + DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity) + return + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/drawable-xxhdpi/unzip_failure_hint.png b/app/src/main/res/drawable-xxhdpi/unzip_failure_hint.png new file mode 100644 index 0000000000..3c4b1238ae Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/unzip_failure_hint.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/dialog_unzip_failure_head_background.png b/app/src/main/res/drawable-xxxhdpi/dialog_unzip_failure_head_background.png new file mode 100644 index 0000000000..60306b300a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/dialog_unzip_failure_head_background.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/progressbar_xapk.png b/app/src/main/res/drawable-xxxhdpi/progressbar_xapk.png new file mode 100644 index 0000000000..3be1989cc6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/progressbar_xapk.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/progressbar_xapk_detail.png b/app/src/main/res/drawable-xxxhdpi/progressbar_xapk_detail.png new file mode 100644 index 0000000000..5ed16a1ae7 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/progressbar_xapk_detail.png differ diff --git a/app/src/main/res/drawable/progressbar_bg_style.xml b/app/src/main/res/drawable/progressbar_bg_style.xml index c0e83ab0ed..89eed7d21e 100644 --- a/app/src/main/res/drawable/progressbar_bg_style.xml +++ b/app/src/main/res/drawable/progressbar_bg_style.xml @@ -1,28 +1,28 @@ - + - - - + + + - - + android:endColor="@color/btn_pause" + android:startColor="@color/btn_pause" /> + + - - - - + + + + - - - + android:endColor="@color/theme" + android:startColor="@color/theme" /> + + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/drawable/progressbar_xapk_detail_style.xml b/app/src/main/res/drawable/progressbar_xapk_detail_style.xml new file mode 100644 index 0000000000..64af4abf80 --- /dev/null +++ b/app/src/main/res/drawable/progressbar_xapk_detail_style.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/progressbar_xapk_style.xml b/app/src/main/res/drawable/progressbar_xapk_style.xml new file mode 100644 index 0000000000..90f81e9f17 --- /dev/null +++ b/app/src/main/res/drawable/progressbar_xapk_style.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/unzip_failure_dialog_background.xml b/app/src/main/res/drawable/unzip_failure_dialog_background.xml new file mode 100644 index 0000000000..e10816f10d --- /dev/null +++ b/app/src/main/res/drawable/unzip_failure_dialog_background.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_test_xapk.xml b/app/src/main/res/layout/activity_test_xapk.xml new file mode 100644 index 0000000000..7bc0f46670 --- /dev/null +++ b/app/src/main/res/layout/activity_test_xapk.xml @@ -0,0 +1,19 @@ + + + +