From de597bdd36c5f4dfb0d3db05ebf448c2c17fc7f0 Mon Sep 17 00:00:00 2001 From: kehaoyuan Date: Wed, 1 Jul 2020 16:06:03 +0800 Subject: [PATCH 1/8] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=8C=E6=88=90Xapk?= =?UTF-8?q?=E8=A7=A3=E5=8E=8B=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 3 + .../java/com/gh/common/util/Extensions.kt | 11 ++- .../java/com/gh/common/util/XapkInstaller.kt | 90 +++++++++++++++++++ .../main/java/com/gh/common/util/ZipHelper.kt | 86 ++++++++++++++++++ .../com/gh/gamecenter/XapkTestActivity.kt | 42 +++++++++ .../fragment/SearchToolbarFragment.java | 13 ++- .../main/res/layout/activity_test_xapk.xml | 19 ++++ 7 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/gh/common/util/XapkInstaller.kt create mode 100644 app/src/main/java/com/gh/common/util/ZipHelper.kt create mode 100644 app/src/main/java/com/gh/gamecenter/XapkTestActivity.kt create mode 100644 app/src/main/res/layout/activity_test_xapk.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 75316f157c..4ec2e80671 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -515,6 +515,9 @@ android:name=".qa.answer.draft.AnswerDraftActivity" android:screenOrientation="portrait" /> + + ", "") } -fun String.containHtmlTag() : Boolean { +fun String.containHtmlTag(): Boolean { val pattern = Pattern.compile("<(\"[^\"]*\"|'[^']*'|[^'\">])*>") val matcher = pattern.matcher(this) return matcher.find() @@ -275,6 +275,15 @@ fun throwExceptionInDebug(message: String = "", predicate: Boolean = true) { } } +/** + * 主动抛出异常 + */ +fun throwException(message: String = "", predicate: Boolean = true) { + if (predicate) { + throw RuntimeException(message) + } +} + /** * String related */ diff --git a/app/src/main/java/com/gh/common/util/XapkInstaller.kt b/app/src/main/java/com/gh/common/util/XapkInstaller.kt new file mode 100644 index 0000000000..60404018c0 --- /dev/null +++ b/app/src/main/java/com/gh/common/util/XapkInstaller.kt @@ -0,0 +1,90 @@ +package com.gh.common.util + +import com.gh.common.AppExecutor +import com.halo.assistant.HaloApp +import com.lightgame.download.DownloadEntity +import java.lang.Exception +import java.util.concurrent.ConcurrentHashMap + +object XapkInstaller : IUnzipListener { + private const val XAPK_PACKAGE_PATH_TAG = "xapk_package_path" + private const val XAPK_DATA_PATH_TAG = "xapk_data_path" + private const val PACKAGE_EXTENSION_NAME = "apk" + private const val XAPK_EXTENSION_NAME = "xapk" + private const val XAPK_DATA_EXTENSION_NAME = "obb" + + private val mApplicationContext = HaloApp.getInstance().application.applicationContext + + private val mXapkInstallerQueue: ConcurrentHashMap = ConcurrentHashMap() + + // 按顺序解压 + fun installer(downloadEntity: DownloadEntity) { + mXapkInstallerQueue[downloadEntity.path] = downloadEntity + + val filePath = downloadEntity.path + if (XAPK_EXTENSION_NAME == getExtension(filePath)) { + unzipXapkFile(downloadEntity) + } else { + mApplicationContext.startActivity(PackageUtils.getInstallIntent(mApplicationContext, filePath)) + } + } + + private fun unzipXapkFile(downloadEntity: DownloadEntity) { + AppExecutor.lightWeightIoExecutor.execute { ZipHelper.unzipXapk(downloadEntity.path, this) } + } + + override fun onProgress(zipPath: String, unzipPath: String, unzipSize: Long, unzipProgress: Long) { + val downloadEntity = getDownloadEntityInInstallerQueue(zipPath) + + // todo update page + // todo 更新页面需要注意线程切换问题 + } + + override fun onNext(zipPath: String, unzipPath: String) { + val downloadEntity = getDownloadEntityInInstallerQueue(zipPath) + + // todo 如果xapk存在多个apk包或者多个obb数据包,该如何处理?????? + if (PACKAGE_EXTENSION_NAME == getExtension(unzipPath)) { +// throwExceptionInDebug("exist package file", !downloadEntity.meta[XAPK_PACKAGE_PATH_TAG].isNullOrEmpty()) + downloadEntity.meta[XAPK_PACKAGE_PATH_TAG] = unzipPath + } else { +// throwExceptionInDebug("exist data file", !downloadEntity.meta[XAPK_DATA_PATH_TAG].isNullOrEmpty()) + downloadEntity.meta[XAPK_DATA_PATH_TAG] = unzipPath + } + + // update download database + // todo 更新页面需要注意线程切换问题 + } + + override fun onFailure(zipPath: String, exception: Exception) { + val downloadEntity = getDownloadEntityInInstallerQueue(zipPath) + + // todo handle ???????? + // todo 更新页面需要注意线程切换问题 + } + + override fun onSuccess(zipPath: String) { + val downloadEntity = getDownloadEntityInInstallerQueue(zipPath) + + checkNotNull(downloadEntity.meta[XAPK_DATA_PATH_TAG]) + val pkgPath = checkNotNull(downloadEntity.meta[XAPK_PACKAGE_PATH_TAG]) + mApplicationContext.startActivity(PackageUtils.getInstallIntent(mApplicationContext, pkgPath)) + + mXapkInstallerQueue.remove(downloadEntity.path) + if (mXapkInstallerQueue.isNotEmpty()) { + unzipXapkFile(downloadEntity) + } + + // todo 更新页面需要注意线程切换问题 + } + + private fun getDownloadEntityInInstallerQueue(zipPath: String): DownloadEntity { + val downloadEntity = mXapkInstallerQueue[zipPath] + return checkNotNull(downloadEntity) + } + + fun getExtension(fileName: String): String? { + val lastDotIndex = fileName.lastIndexOf('.') + return if (lastDotIndex == -1) null else fileName.substring(lastDotIndex + 1) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/util/ZipHelper.kt b/app/src/main/java/com/gh/common/util/ZipHelper.kt new file mode 100644 index 0000000000..0b6b7de2da --- /dev/null +++ b/app/src/main/java/com/gh/common/util/ZipHelper.kt @@ -0,0 +1,86 @@ +package com.gh.common.util + +import android.os.Environment +import com.gh.gamecenter.BuildConfig +import java.io.File +import java.lang.Exception +import java.util.zip.ZipFile + +object ZipHelper { + private const val DEFAULT_BUFFER_SIZE = 24 * 1024 + + // todo apk包存放路径/命名等问题... + fun unzipXapk(path: String, listener: IUnzipListener) { + try { + // todo check file type??? + val unzipSize = getUnzipSize(path) + var unzipProgress = 0L + val absolutePath = Environment.getExternalStorageDirectory().absolutePath + ZipFile(File(path)).use { zip -> + for (zipEntry in zip.entries().asSequence()) { + val outputFile = File(absolutePath + File.separator + zipEntry.name) + 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 + listener.onProgress(path, outputFile.path, unzipProgress, unzipSize) + listener.onNext(path, outputFile.path) + continue + } + + // unzip + zip.getInputStream(zipEntry).use { input -> + outputFile.outputStream().use { output -> + val buffer = ByteArray(DEFAULT_BUFFER_SIZE) + var bytes = input.read(buffer) + while (bytes >= 0) { + output.write(buffer, 0, bytes) + unzipProgress += bytes + bytes = input.read(buffer) + listener.onProgress(path, outputFile.path, unzipProgress, unzipSize) + } + } + } + listener.onNext(path, outputFile.path) + } + } + listener.onSuccess(path) + } catch (e: Exception) { + if (BuildConfig.DEBUG) throw e + listener.onFailure(path, e) + } + } + + fun getUnzipSize(path: String): Long { + var totalSize = 0L + for (entry in ZipFile(File(path)).entries()) { + totalSize += entry.size + } + return totalSize + } +} + +interface IUnzipListener { + fun onProgress(zipPath: String, unzipPath: String, unzipSize: Long, unzipProgress: Long) + + fun onNext(zipPath: String, unzipPath: String) + + fun onFailure(zipPath: String, exception: Exception) + + fun onSuccess(zipPath: String) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/XapkTestActivity.kt b/app/src/main/java/com/gh/gamecenter/XapkTestActivity.kt new file mode 100644 index 0000000000..a7efecc2fa --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/XapkTestActivity.kt @@ -0,0 +1,42 @@ +package com.gh.gamecenter + +import android.app.Activity +import android.content.Intent +import android.util.Log +import android.view.View +import com.gh.base.BaseActivity +import com.gh.common.util.XapkInstaller +import com.lightgame.download.DownloadEntity + +class XapkTestActivity : BaseActivity() { + + override fun getLayoutId(): Int { + return R.layout.activity_test_xapk + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode != Activity.RESULT_OK || data == null) return + val filePath = data.data?.path ?: return + Log.e("MainActivity", filePath) + unZipFile(filePath) + } + + fun onSelectListener(view: View) { + val getContentIntent = Intent(Intent.ACTION_GET_CONTENT) + getContentIntent.type = "*/*" + getContentIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) + startActivityForResult(Intent.createChooser(getContentIntent, ""), REQUEST_CODE_GET_FILES) + } + + private fun unZipFile(path: String) { + val downloadEntity = DownloadEntity() + downloadEntity.path = path + downloadEntity.meta = hashMapOf() + XapkInstaller.installer(downloadEntity) + } + + companion object { + const val REQUEST_CODE_GET_FILES: Int = 112 + } +} \ No newline at end of file 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..c019fa61c8 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; @@ -24,10 +27,12 @@ import com.gh.common.util.DisplayUtils; import com.gh.common.util.EntranceUtils; import com.gh.common.util.MtaHelper; import com.gh.download.DownloadManager; +import com.gh.gamecenter.BuildConfig; import com.gh.gamecenter.DownloadManagerActivity; import com.gh.gamecenter.MessageActivity; import com.gh.gamecenter.R; import com.gh.gamecenter.SearchActivity; +import com.gh.gamecenter.XapkTestActivity; import com.gh.gamecenter.entity.GameUpdateEntity; import com.gh.gamecenter.eventbus.EBDownloadStatus; import com.gh.gamecenter.eventbus.EBReuse; @@ -41,8 +46,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; @@ -249,6 +252,12 @@ public class SearchToolbarFragment extends BaseLazyFragment implements View.OnCl startActivity(intent); break; case R.id.actionbar_notification: + // todo test + if (BuildConfig.DEBUG) { + startActivity(new Intent(getContext(), XapkTestActivity.class)); + return; + } + DataUtils.onEvent(getActivity(), "主页", "消息图标"); MtaHelper.onEvent("首页_点击", "顶栏", "消息中心"); DataCollectionUtils.uploadClick(getActivity(), "消息图标", "主页"); 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 @@ + + + +