diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 548fca37ba..9da30ba1d3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,7 +61,7 @@ android_build: script: - export GRADLE_USER_HOME=/home/gitlab-runner/ci-build-cache/$CI_PROJECT_PATH/.gradle - chmod +x ./gradlew - - ./scripts/jenkins_build.sh -c + - ./scripts/jenkins_build.sh $CI_COMMIT_REF_NAME $BEFORE_COMMIT_SHA $CI_COMMIT_SHA #设置打包后的产物,用于job之间共享 artifacts: paths: diff --git a/app/build.gradle b/app/build.gradle index 09896467e4..6009c22c81 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -364,6 +364,7 @@ dependencies { implementation(project(':feature:qq_game')) { exclude group: 'androidx.swiperefreshlayout' } + internalImplementation(project(':module_internal_test')) } File propFile = file('sign.properties') diff --git a/app/src/internal/java/com/gh/vspace/ExternalGameUsage.kt b/app/src/internal/java/com/gh/vspace/ExternalGameUsage.kt new file mode 100644 index 0000000000..1e2d6b2ede --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/ExternalGameUsage.kt @@ -0,0 +1,30 @@ +package com.gh.vspace + +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.annotation.Keep +import com.gh.gamecenter.R +import com.gh.gamecenter.databinding.LayoutPersonalOtherItemBinding +import com.gh.vspace.installexternalgames.InstallExternalGameActivity + +@Keep +class ExternalGameUsage : IExternalGamesUsage { + override fun addInstallExternalGameButton(viewParent: ViewGroup) { + val context = viewParent.context + viewParent.findViewById(R.id.install_game_from_external) ?: run { + val binding = LayoutPersonalOtherItemBinding.inflate(LayoutInflater.from(context)).apply { + root.id = R.id.install_game_from_external + titleTv.text = context.getString(R.string.title_install_external_game) + iconIv.setImageResource(R.drawable.ic_personal_my_game) + root.setOnClickListener { + context.startActivity( + InstallExternalGameActivity.getIntent(context) + .apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK }) + } + } + viewParent.addView(binding.root, 0) + } + } +} \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameAdapter.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameAdapter.kt new file mode 100644 index 0000000000..534b51a36a --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameAdapter.kt @@ -0,0 +1,47 @@ +package com.gh.vspace.installexternalgames + +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.common.utils.goneIf +import com.gh.gamecenter.common.utils.toBinding + +class ExternalGameAdapter(private val games: List, private val onItemClickListener: OnItemClickListener) : + RecyclerView.Adapter() { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExternalGameViewHolder { + return ExternalGameViewHolder(parent.toBinding()) + } + + override fun getItemCount(): Int { + return games.size + } + + override fun onBindViewHolder(holder: ExternalGameViewHolder, position: Int) { + val item = games[position] + holder.apkInfo.text = item.externalGameEntity.let { item -> + """ + 应用程序名:${item.appName} + 版本号:${item.apkVersion} + 包名: ${item.apkPackageName} + 路径:${item.apkPath} + """.trimIndent() + } + holder.install.goneIf(item.isInstalled) { + holder.install.setOnClickListener { + onItemClickListener.onItemClick(item, OnItemClickListener.ClickType.CLICK_INSTALL) + } + } + holder.uninstall.goneIf(!item.isInstalled) { + holder.uninstall.setOnClickListener { + onItemClickListener.onItemClick( + item, + OnItemClickListener.ClickType.CLICK_UNINSTALL + ) + } + } + holder.start.goneIf(!item.isInstalled) { + holder.start.setOnClickListener { + onItemClickListener.onItemClick(item, OnItemClickListener.ClickType.CLICK_START) + } + } + } +} \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameEntity.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameEntity.kt new file mode 100644 index 0000000000..edda06764b --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameEntity.kt @@ -0,0 +1,10 @@ +package com.gh.vspace.installexternalgames + +data class ExternalGameEntity( + val cpuAbi: Set, + val apkPath: String, + val apkFileName: String, + val appName: String, + val apkVersion: String, + val apkPackageName: String, +) \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameUiState.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameUiState.kt new file mode 100644 index 0000000000..6d5400900c --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameUiState.kt @@ -0,0 +1,6 @@ +package com.gh.vspace.installexternalgames + +data class ExternalGameUiState( + val externalGameEntity: ExternalGameEntity, + val isInstalled: Boolean +) \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameViewHolder.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameViewHolder.kt new file mode 100644 index 0000000000..3a339b0088 --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/ExternalGameViewHolder.kt @@ -0,0 +1,11 @@ +package com.gh.vspace.installexternalgames + +import androidx.recyclerview.widget.RecyclerView +import com.gh.gamecenter.databinding.LayoutExternalGameItemBinding + +class ExternalGameViewHolder(binding: LayoutExternalGameItemBinding) : RecyclerView.ViewHolder(binding.root) { + val apkInfo = binding.apkFileInfo + val install = binding.btnInstall + val uninstall = binding.btnUninstall + val start = binding.btnStart +} \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameActivity.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameActivity.kt new file mode 100644 index 0000000000..9e59f8ccdf --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameActivity.kt @@ -0,0 +1,18 @@ +package com.gh.vspace.installexternalgames + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import com.alibaba.android.arouter.facade.annotation.Route +import com.gh.gamecenter.common.base.activity.ToolBarActivity +import com.gh.gamecenter.common.constant.EntranceConsts +import com.gh.gamecenter.setting.view.AboutActivity +import com.gh.gamecenter.setting.view.AboutFragment + +class InstallExternalGameActivity : ToolBarActivity() { + companion object { + fun getIntent(context: Context): Intent { + return getTargetIntent(context, InstallExternalGameActivity::class.java, InstallExternalGameFragment::class.java, Bundle()) + } + } +} \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameFragment.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameFragment.kt new file mode 100644 index 0000000000..6c266d98fd --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameFragment.kt @@ -0,0 +1,153 @@ +package com.gh.vspace.installexternalgames + +import android.app.Dialog +import android.os.Bundle +import androidx.recyclerview.widget.LinearLayoutManager +import com.gh.common.util.DialogUtils +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.fragment.ToolbarFragment +import com.gh.gamecenter.common.exposure.meta.MetaUtil +import com.gh.gamecenter.common.utils.dip2px +import com.gh.gamecenter.common.utils.toColor +import com.gh.gamecenter.common.utils.viewModelProvider +import com.gh.gamecenter.common.view.divider.HorizontalDividerItemDecoration +import com.gh.gamecenter.core.AppExecutor +import com.gh.gamecenter.databinding.FragmentInstallExternalGamesBinding +import com.gh.vspace.VHelper +import com.halo.assistant.HaloApp +import com.lg.vspace.VirtualAppManager +import io.reactivex.Single +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers + +class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener { + + private val mViewModel: InstallExternalGameViewModel by lazy { viewModelProvider() } + + private lateinit var mBinding: FragmentInstallExternalGamesBinding + + private val games = mutableListOf() + private var adapter = ExternalGameAdapter(games, this) + + private var uninstallDisposable: Disposable? = null + + override fun getLayoutId() = 0 + + override fun getInflatedLayout() = + FragmentInstallExternalGamesBinding.inflate(layoutInflater).apply { mBinding = this }.root + + private lateinit var dialog: Dialog + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setNavigationTitle(getString(com.gh.gamecenter.R.string.title_install_external_game)) + initView() + mViewModel.gamesList.observe(this) { + if (dialog.isShowing) { + dialog.dismiss() + } + games.clear() + games.addAll( + it.map { + ExternalGameUiState(it, VHelper.isInstalled(it.apkPackageName)) + } + ) + adapter.notifyDataSetChanged() + } + mViewModel.scanPaths() + + + } + + private fun initView() { + dialog = DialogUtils.showWaitDialog(requireContext(), "") + mBinding.externalGamesList.let { + it.adapter = adapter + it.layoutManager = LinearLayoutManager(requireContext()) + val itemDecoration = HorizontalDividerItemDecoration.Builder(requireContext()) + .size(2F.dip2px()) + .color(R.color.divider.toColor(requireContext())) + .build() + if (it.itemDecorationCount != 0) { + it.removeItemDecorationAt(0) + } + it.addItemDecoration(itemDecoration) + + } + } + + override fun onDestroy() { + super.onDestroy() + if (dialog.isShowing) { + dialog.dismiss() + } + if (uninstallDisposable?.isDisposed != true) { + uninstallDisposable?.dispose() + } + } + + + override fun onItemClick(externalGameUiState: ExternalGameUiState, clickType: OnItemClickListener.ClickType) { + when (clickType) { + OnItemClickListener.ClickType.CLICK_INSTALL -> { + install(externalGameUiState) + } + OnItemClickListener.ClickType.CLICK_UNINSTALL -> { + uninstall(externalGameUiState) + } + OnItemClickListener.ClickType.CLICK_START -> { + start(externalGameUiState) + } + } + } + + private fun install(externalGameUiState: ExternalGameUiState) { + val bit = + externalGameUiState.externalGameEntity.cpuAbi.let { if (it.size == 1 && it.contains("armeabi-v7a")) "32" else "64" } + if (VHelper.showDialogIfVSpaceIsNeeded( + requireContext(), + "", + externalGameUiState.externalGameEntity.appName, + "", + bit = bit + ) + ) return + dialog.show() + externalGameUiState.externalGameEntity.let { + val intent = VirtualAppManager.getInstallIntent(context, it.apkPath, it.apkPackageName) + requireActivity().startActivity(intent) + } + } + + private fun uninstall(externalGameUiState: ExternalGameUiState) { + dialog.show() + uninstallDisposable = Single.create { + VHelper.uninstall(externalGameUiState.externalGameEntity.apkPackageName) + }.subscribeOn(Schedulers.from(AppExecutor.lightWeightIoExecutor)).observeOn(AndroidSchedulers.mainThread()) + .subscribe { t1, t2 -> + if (dialog.isShowing) { + dialog.dismiss() + } + } + } + + + private fun start(externalGameUiState: ExternalGameUiState) { + val intent = VirtualAppManager.get().getStartGameIntent( + externalGameUiState.externalGameEntity.apkPackageName, + "", + externalGameUiState.externalGameEntity.appName, + "", + MetaUtil.getBase64EncodedAndroidId(), + HaloApp.getInstance().gid, + com.gh.gamecenter.BuildConfig.VERSION_NAME, + HaloApp.getInstance().channel, + "", + "" + ) + requireActivity().startActivity(intent) + } + + +} \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameViewModel.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameViewModel.kt new file mode 100644 index 0000000000..b58bfab72d --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/InstallExternalGameViewModel.kt @@ -0,0 +1,58 @@ +package com.gh.vspace.installexternalgames + +import android.app.Application +import android.content.pm.PackageManager +import android.os.Environment +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import com.gh.gamecenter.core.runOnIoThread +import com.gh.vspace.VHelper +import com.lg.vspace.helper.compat.NativeLibraryHelperCompat +import com.lightgame.utils.Utils +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import java.io.File + +class InstallExternalGameViewModel(application: Application) : AndroidViewModel(application) { + private val _gamesList = MutableLiveData>(listOf()) + val gamesList: LiveData> = _gamesList + private val pkgManger = getApplication().packageManager + private var disposable: Disposable = VHelper.callSite.observeOn(AndroidSchedulers.mainThread()).subscribe { + scanPaths() + } + + fun scanPaths() { + runOnIoThread { + _gamesList.postValue(Environment.getExternalStorageDirectory().walk().maxDepth(2) + .filter { it.isFile } + .filter { it.extension == "apk" } + .map { + val packageInfo = pkgManger.getPackageArchiveInfo( + it.absolutePath, + PackageManager.GET_META_DATA or PackageManager.GET_ACTIVITIES + ) + val applicationInfo = packageInfo?.applicationInfo!! + applicationInfo.publicSourceDir = it.absolutePath + applicationInfo.sourceDir = it.absolutePath + ExternalGameEntity( + cpuAbi = NativeLibraryHelperCompat.getPackageAbiList(it.absolutePath), + apkPath = it.absolutePath, + apkFileName = it.name, + apkPackageName = packageInfo.packageName, + apkVersion = packageInfo.versionName, + appName = pkgManger.getApplicationLabel(applicationInfo).toString() + ) + } + .toList()) + } + } + + + override fun onCleared() { + super.onCleared() + disposable.dispose() + } + +} \ No newline at end of file diff --git a/app/src/internal/java/com/gh/vspace/installexternalgames/OnItemClickListener.kt b/app/src/internal/java/com/gh/vspace/installexternalgames/OnItemClickListener.kt new file mode 100644 index 0000000000..3338979f62 --- /dev/null +++ b/app/src/internal/java/com/gh/vspace/installexternalgames/OnItemClickListener.kt @@ -0,0 +1,11 @@ +package com.gh.vspace.installexternalgames + +interface OnItemClickListener { + + enum class ClickType { + CLICK_INSTALL, CLICK_UNINSTALL, CLICK_START + } + + fun onItemClick(externalGameUiState: ExternalGameUiState, clickType: ClickType) + +} \ No newline at end of file diff --git a/app/src/internal/res/layout/fragment_install_external_games.xml b/app/src/internal/res/layout/fragment_install_external_games.xml new file mode 100644 index 0000000000..4dcc1e2be6 --- /dev/null +++ b/app/src/internal/res/layout/fragment_install_external_games.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/internal/res/layout/layout_external_game_item.xml b/app/src/internal/res/layout/layout_external_game_item.xml new file mode 100644 index 0000000000..066c698eec --- /dev/null +++ b/app/src/internal/res/layout/layout_external_game_item.xml @@ -0,0 +1,43 @@ + + + + + + + +