From 23d9def61382860d235270d6d1ec5b48f5bb1f22 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 1 Mar 2023 13:39:23 -0500 Subject: [PATCH] Android: Convert CheatsActivity to Kotlin --- .../features/cheats/ui/CheatsActivity.java | 342 ------------------ .../features/cheats/ui/CheatsActivity.kt | 293 +++++++++++++++ 2 files changed, 293 insertions(+), 342 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java deleted file mode 100644 index 8941626b92..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ /dev/null @@ -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 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; - } - }); - } - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt new file mode 100644 index 0000000000..11278360cf --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt @@ -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 + ): 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) + } + } + } + } +}