Android: Convert CheatsActivity to Kotlin

This commit is contained in:
Charles Lombardo 2023-03-01 13:39:23 -05:00
parent 3bcc4a0a59
commit 23d9def613
2 changed files with 293 additions and 342 deletions

View file

@ -1,342 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.ui;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsAnimationCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.lifecycle.ViewModelProvider;
import androidx.slidingpanelayout.widget.SlidingPaneLayout;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.elevation.ElevationOverlayProvider;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.databinding.ActivityCheatsBinding;
import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.ui.TwoPaneOnBackPressedCallback;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.utils.InsetsHelper;
import org.dolphinemu.dolphinemu.utils.ThemeHelper;
import java.util.List;
public class CheatsActivity extends AppCompatActivity
implements SlidingPaneLayout.PanelSlideListener
{
private static final String ARG_GAME_ID = "game_id";
private static final String ARG_GAMETDB_ID = "gametdb_id";
private static final String ARG_REVISION = "revision";
private static final String ARG_IS_WII = "is_wii";
private String mGameId;
private String mGameTdbId;
private int mRevision;
private boolean mIsWii;
private CheatsViewModel mViewModel;
private View mCheatListLastFocus;
private View mCheatDetailsLastFocus;
private ActivityCheatsBinding mBinding;
public static void launch(Context context, String gameId, String gameTdbId, int revision,
boolean isWii)
{
Intent intent = new Intent(context, CheatsActivity.class);
intent.putExtra(ARG_GAME_ID, gameId);
intent.putExtra(ARG_GAMETDB_ID, gameTdbId);
intent.putExtra(ARG_REVISION, revision);
intent.putExtra(ARG_IS_WII, isWii);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState)
{
ThemeHelper.setTheme(this);
super.onCreate(savedInstanceState);
MainPresenter.skipRescanningLibrary();
Intent intent = getIntent();
mGameId = intent.getStringExtra(ARG_GAME_ID);
mGameTdbId = intent.getStringExtra(ARG_GAMETDB_ID);
mRevision = intent.getIntExtra(ARG_REVISION, 0);
mIsWii = intent.getBooleanExtra(ARG_IS_WII, true);
setTitle(getString(R.string.cheats_with_game_id, mGameId));
mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
mViewModel.load(mGameId, mRevision);
mBinding = ActivityCheatsBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
mCheatListLastFocus = mBinding.cheatList;
mCheatDetailsLastFocus = mBinding.cheatDetails;
mBinding.slidingPaneLayout.addPanelSlideListener(this);
getOnBackPressedDispatcher().addCallback(this,
new TwoPaneOnBackPressedCallback(mBinding.slidingPaneLayout));
mViewModel.getSelectedCheat().observe(this, this::onSelectedCheatChanged);
onSelectedCheatChanged(mViewModel.getSelectedCheat().getValue());
mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView);
setSupportActionBar(mBinding.toolbarCheats);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setInsets();
@ColorInt int color =
new ElevationOverlayProvider(mBinding.toolbarCheats.getContext()).compositeOverlay(
MaterialColors.getColor(mBinding.toolbarCheats, R.attr.colorSurface),
getResources().getDimensionPixelSize(R.dimen.elevated_app_bar));
mBinding.toolbarCheats.setBackgroundColor(color);
ThemeHelper.setStatusBarColor(this, color);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_settings, menu);
return true;
}
@Override
protected void onStop()
{
super.onStop();
mViewModel.saveIfNeeded(mGameId, mRevision);
}
@Override
public void onPanelSlide(@NonNull View panel, float slideOffset)
{
}
@Override
public void onPanelOpened(@NonNull View panel)
{
boolean rtl = ViewCompat.getLayoutDirection(panel) == ViewCompat.LAYOUT_DIRECTION_RTL;
mCheatDetailsLastFocus.requestFocus(rtl ? View.FOCUS_LEFT : View.FOCUS_RIGHT);
}
@Override
public void onPanelClosed(@NonNull View panel)
{
boolean rtl = ViewCompat.getLayoutDirection(panel) == ViewCompat.LAYOUT_DIRECTION_RTL;
mCheatListLastFocus.requestFocus(rtl ? View.FOCUS_RIGHT : View.FOCUS_LEFT);
}
private void onSelectedCheatChanged(Cheat selectedCheat)
{
boolean cheatSelected = selectedCheat != null;
if (!cheatSelected && mBinding.slidingPaneLayout.isOpen())
mBinding.slidingPaneLayout.close();
mBinding.slidingPaneLayout.setLockMode(cheatSelected ?
SlidingPaneLayout.LOCK_MODE_UNLOCKED : SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED);
}
public void onListViewFocusChange(boolean hasFocus)
{
if (hasFocus)
{
mCheatListLastFocus = mBinding.cheatList.findFocus();
if (mCheatListLastFocus == null)
throw new NullPointerException();
mBinding.slidingPaneLayout.close();
}
}
public void onDetailsViewFocusChange(boolean hasFocus)
{
if (hasFocus)
{
mCheatDetailsLastFocus = mBinding.cheatDetails.findFocus();
if (mCheatDetailsLastFocus == null)
throw new NullPointerException();
mBinding.slidingPaneLayout.open();
}
}
@Override
public boolean onSupportNavigateUp()
{
onBackPressed();
return true;
}
private void openDetailsView(boolean open)
{
if (open)
mBinding.slidingPaneLayout.open();
}
public Settings loadGameSpecificSettings()
{
Settings settings = new Settings();
settings.loadSettings(null, mGameId, mRevision, mIsWii);
return settings;
}
public void downloadGeckoCodes()
{
AlertDialog progressDialog = new MaterialAlertDialogBuilder(this)
.setTitle(R.string.cheats_downloading)
.setView(R.layout.dialog_indeterminate_progress)
.setCancelable(false)
.show();
new Thread(() ->
{
GeckoCheat[] codes = GeckoCheat.downloadCodes(mGameTdbId);
runOnUiThread(() ->
{
progressDialog.dismiss();
if (codes == null)
{
new MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.cheats_download_failed))
.setPositiveButton(R.string.ok, null)
.show();
}
else if (codes.length == 0)
{
new MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.cheats_download_empty))
.setPositiveButton(R.string.ok, null)
.show();
}
else
{
int cheatsAdded = mViewModel.addDownloadedGeckoCodes(codes);
String message = getString(R.string.cheats_download_succeeded, codes.length, cheatsAdded);
new MaterialAlertDialogBuilder(this)
.setMessage(message)
.setPositiveButton(R.string.ok, null)
.show();
}
});
}).start();
}
public static void setOnFocusChangeListenerRecursively(@NonNull View view,
View.OnFocusChangeListener listener)
{
view.setOnFocusChangeListener(listener);
if (view instanceof ViewGroup)
{
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++)
{
View child = viewGroup.getChildAt(i);
setOnFocusChangeListenerRecursively(child, listener);
}
}
}
private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.appbarCheats, (v, windowInsets) ->
{
Insets barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
Insets keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
InsetsHelper.insetAppBar(barInsets, mBinding.appbarCheats);
mBinding.slidingPaneLayout.setPadding(barInsets.left, 0, barInsets.right, 0);
// Set keyboard insets if the system supports smooth keyboard animations
ViewGroup.MarginLayoutParams mlpDetails =
(ViewGroup.MarginLayoutParams) mBinding.cheatDetails.getLayoutParams();
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R)
{
if (keyboardInsets.bottom > 0)
{
mlpDetails.bottomMargin = keyboardInsets.bottom;
}
else
{
mlpDetails.bottomMargin = barInsets.bottom;
}
}
else
{
if (mlpDetails.bottomMargin == 0)
{
mlpDetails.bottomMargin = barInsets.bottom;
}
}
mBinding.cheatDetails.setLayoutParams(mlpDetails);
InsetsHelper.applyNavbarWorkaround(barInsets.bottom, mBinding.workaroundView);
ThemeHelper.setNavigationBarColor(this,
MaterialColors.getColor(mBinding.appbarCheats, R.attr.colorSurface));
return windowInsets;
});
// Update the layout for every frame that the keyboard animates in
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R)
{
ViewCompat.setWindowInsetsAnimationCallback(mBinding.cheatDetails,
new WindowInsetsAnimationCompat.Callback(
WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP)
{
int keyboardInsets = 0;
int barInsets = 0;
@NonNull
@Override
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets,
@NonNull List<WindowInsetsAnimationCompat> runningAnimations)
{
ViewGroup.MarginLayoutParams mlpDetails =
(ViewGroup.MarginLayoutParams) mBinding.cheatDetails.getLayoutParams();
keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
mlpDetails.bottomMargin = Math.max(keyboardInsets, barInsets);
mBinding.cheatDetails.setLayoutParams(mlpDetails);
return insets;
}
});
}
}
}

View file

@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.ui
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import androidx.annotation.ColorInt
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.slidingpanelayout.widget.SlidingPaneLayout
import androidx.slidingpanelayout.widget.SlidingPaneLayout.PanelSlideListener
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.elevation.ElevationOverlayProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.ActivityCheatsBinding
import org.dolphinemu.dolphinemu.features.cheats.model.Cheat
import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel
import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat.Companion.downloadCodes
import org.dolphinemu.dolphinemu.features.settings.model.Settings
import org.dolphinemu.dolphinemu.ui.TwoPaneOnBackPressedCallback
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
import org.dolphinemu.dolphinemu.utils.InsetsHelper
import org.dolphinemu.dolphinemu.utils.ThemeHelper
class CheatsActivity : AppCompatActivity(), PanelSlideListener {
private var gameId: String? = null
private var gameTdbId: String? = null
private var revision = 0
private var isWii = false
private lateinit var viewModel: CheatsViewModel
private var cheatListLastFocus: View? = null
private var cheatDetailsLastFocus: View? = null
private lateinit var binding: ActivityCheatsBinding
override fun onCreate(savedInstanceState: Bundle?) {
ThemeHelper.setTheme(this)
super.onCreate(savedInstanceState)
MainPresenter.skipRescanningLibrary()
gameId = intent.getStringExtra(ARG_GAME_ID)
gameTdbId = intent.getStringExtra(ARG_GAMETDB_ID)
revision = intent.getIntExtra(ARG_REVISION, 0)
isWii = intent.getBooleanExtra(ARG_IS_WII, true)
title = getString(R.string.cheats_with_game_id, gameId)
viewModel = ViewModelProvider(this)[CheatsViewModel::class.java]
viewModel.load(gameId!!, revision)
binding = ActivityCheatsBinding.inflate(layoutInflater)
setContentView(binding.root)
WindowCompat.setDecorFitsSystemWindows(window, false)
cheatListLastFocus = binding.cheatList
cheatDetailsLastFocus = binding.cheatDetails
binding.slidingPaneLayout.addPanelSlideListener(this)
onBackPressedDispatcher.addCallback(
this,
TwoPaneOnBackPressedCallback(binding.slidingPaneLayout)
)
viewModel.selectedCheat.observe(this) { selectedCheat: Cheat? ->
onSelectedCheatChanged(
selectedCheat
)
}
onSelectedCheatChanged(viewModel.selectedCheat.value)
viewModel.openDetailsViewEvent.observe(this) { open: Boolean -> openDetailsView(open) }
setSupportActionBar(binding.toolbarCheats)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
setInsets()
@ColorInt val color =
ElevationOverlayProvider(binding.toolbarCheats.context).compositeOverlay(
MaterialColors.getColor(binding.toolbarCheats, R.attr.colorSurface),
resources.getDimensionPixelSize(R.dimen.elevated_app_bar).toFloat()
)
binding.toolbarCheats.setBackgroundColor(color)
ThemeHelper.setStatusBarColor(this, color)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.menu_settings, menu)
return true
}
override fun onStop() {
super.onStop()
viewModel.saveIfNeeded(gameId!!, revision)
}
override fun onPanelSlide(panel: View, slideOffset: Float) {}
override fun onPanelOpened(panel: View) {
val rtl = ViewCompat.getLayoutDirection(panel) == ViewCompat.LAYOUT_DIRECTION_RTL
cheatDetailsLastFocus!!.requestFocus(if (rtl) View.FOCUS_LEFT else View.FOCUS_RIGHT)
}
override fun onPanelClosed(panel: View) {
val rtl = ViewCompat.getLayoutDirection(panel) == ViewCompat.LAYOUT_DIRECTION_RTL
cheatListLastFocus!!.requestFocus(if (rtl) View.FOCUS_RIGHT else View.FOCUS_LEFT)
}
private fun onSelectedCheatChanged(selectedCheat: Cheat?) {
val cheatSelected = selectedCheat != null
if (!cheatSelected && binding.slidingPaneLayout.isOpen) binding.slidingPaneLayout.close()
binding.slidingPaneLayout.lockMode =
if (cheatSelected) SlidingPaneLayout.LOCK_MODE_UNLOCKED else SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED
}
fun onListViewFocusChange(hasFocus: Boolean) {
if (hasFocus) {
cheatListLastFocus = binding.cheatList.findFocus()
if (cheatListLastFocus == null) throw NullPointerException()
binding.slidingPaneLayout.close()
}
}
fun onDetailsViewFocusChange(hasFocus: Boolean) {
if (hasFocus) {
cheatDetailsLastFocus = binding.cheatDetails.findFocus()
if (cheatDetailsLastFocus == null) throw NullPointerException()
binding.slidingPaneLayout.open()
}
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
private fun openDetailsView(open: Boolean) {
if (open) binding.slidingPaneLayout.open()
}
fun loadGameSpecificSettings(): Settings {
val settings = Settings()
settings.loadSettings(null, gameId, revision, isWii)
return settings
}
fun downloadGeckoCodes() {
val progressDialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.cheats_downloading)
.setView(R.layout.dialog_indeterminate_progress)
.setCancelable(false)
.show()
lifecycleScope.launchWhenResumed {
withContext(Dispatchers.IO) {
val codes = downloadCodes(gameTdbId!!)
withContext(Dispatchers.Main) {
progressDialog.dismiss()
if (codes == null) {
MaterialAlertDialogBuilder(binding.root.context)
.setMessage(getString(R.string.cheats_download_failed))
.setPositiveButton(R.string.ok, null)
.show()
} else if (codes.isEmpty()) {
MaterialAlertDialogBuilder(binding.root.context)
.setMessage(getString(R.string.cheats_download_empty))
.setPositiveButton(R.string.ok, null)
.show()
} else {
val cheatsAdded = viewModel.addDownloadedGeckoCodes(codes)
val message =
getString(R.string.cheats_download_succeeded, codes.size, cheatsAdded)
MaterialAlertDialogBuilder(binding.root.context)
.setMessage(message)
.setPositiveButton(R.string.ok, null)
.show()
}
}
}
}
}
private fun setInsets() {
ViewCompat.setOnApplyWindowInsetsListener(binding.appbarCheats) { _: View?, windowInsets: WindowInsetsCompat ->
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
InsetsHelper.insetAppBar(barInsets, binding.appbarCheats)
binding.slidingPaneLayout.setPadding(barInsets.left, 0, barInsets.right, 0)
// Set keyboard insets if the system supports smooth keyboard animations
val mlpDetails = binding.cheatDetails.layoutParams as MarginLayoutParams
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
if (keyboardInsets.bottom > 0) {
mlpDetails.bottomMargin = keyboardInsets.bottom
} else {
mlpDetails.bottomMargin = barInsets.bottom
}
} else {
if (mlpDetails.bottomMargin == 0) {
mlpDetails.bottomMargin = barInsets.bottom
}
}
binding.cheatDetails.layoutParams = mlpDetails
InsetsHelper.applyNavbarWorkaround(barInsets.bottom, binding.workaroundView)
ThemeHelper.setNavigationBarColor(
this,
MaterialColors.getColor(binding.appbarCheats, R.attr.colorSurface)
)
windowInsets
}
// Update the layout for every frame that the keyboard animates in
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ViewCompat.setWindowInsetsAnimationCallback(
binding.cheatDetails,
object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
var keyboardInsets = 0
var barInsets = 0
override fun onProgress(
insets: WindowInsetsCompat,
runningAnimations: List<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
val mlpDetails = binding.cheatDetails.layoutParams as MarginLayoutParams
keyboardInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
barInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom
mlpDetails.bottomMargin = keyboardInsets.coerceAtLeast(barInsets)
binding.cheatDetails.layoutParams = mlpDetails
return insets
}
})
}
}
companion object {
private const val ARG_GAME_ID = "game_id"
private const val ARG_GAMETDB_ID = "gametdb_id"
private const val ARG_REVISION = "revision"
private const val ARG_IS_WII = "is_wii"
@JvmStatic
fun launch(
context: Context,
gameId: String,
gameTdbId: String,
revision: Int,
isWii: Boolean
) {
val intent = Intent(context, CheatsActivity::class.java)
intent.putExtra(ARG_GAME_ID, gameId)
intent.putExtra(ARG_GAMETDB_ID, gameTdbId)
intent.putExtra(ARG_REVISION, revision)
intent.putExtra(ARG_IS_WII, isWii)
context.startActivity(intent)
}
@JvmStatic
fun setOnFocusChangeListenerRecursively(
view: View,
listener: View.OnFocusChangeListener?
) {
view.onFocusChangeListener = listener
if (view is ViewGroup) {
for (i in 0 until view.childCount) {
val child = view.getChildAt(i)
setOnFocusChangeListenerRecursively(child, listener)
}
}
}
}
}