summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java
diff options
context:
space:
mode:
authorMai <mai.iam2048@gmail.com>2023-03-02 15:04:19 -0500
committerGitHub <noreply@github.com>2023-03-02 15:04:19 -0500
commit78e8669bac069374ee87ecafddba114928d72687 (patch)
treeb8a2c0b35a1b69e21493ba93b647c64167179365 /Source/Android/app/src/main/java
parent1b7969bc621ba74bcd6046a315e50b8762824e46 (diff)
parent3935449ca01e6bab39b1997d9e5cf999a160e48f (diff)
Merge pull request #11614 from t895/kotlin-cheats
Android: Convert Cheats Activity to Kotlin
Diffstat (limited to 'Source/Android/app/src/main/java')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java60
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.kt52
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java45
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.kt47
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java53
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.kt42
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java321
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.kt207
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java78
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.kt72
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.java61
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.kt32
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.java27
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.kt17
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java60
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.kt56
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.java36
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.kt31
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java68
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.kt54
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java178
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.kt170
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java49
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.kt29
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java18
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.kt10
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java82
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.kt74
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java55
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.kt39
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java342
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.kt293
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java176
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.kt138
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.java16
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.kt13
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.java16
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.kt13
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java28
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.kt18
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.java85
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt59
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java56
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.kt38
44 files changed, 1504 insertions, 1910 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java
deleted file mode 100644
index 8a168e8bc5..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-
-public class ARCheat extends AbstractCheat
-{
- @Keep
- private final long mPointer;
-
- public ARCheat()
- {
- mPointer = createNew();
- }
-
- @Keep
- private ARCheat(long pointer)
- {
- mPointer = pointer;
- }
-
- @Override
- public native void finalize();
-
- private native long createNew();
-
- public boolean supportsCreator()
- {
- return false;
- }
-
- public boolean supportsNotes()
- {
- return false;
- }
-
- @NonNull
- public native String getName();
-
- @NonNull
- public native String getCode();
-
- public native boolean getUserDefined();
-
- public native boolean getEnabled();
-
- @Override
- protected native int trySetImpl(@NonNull String name, @NonNull String creator,
- @NonNull String notes, @NonNull String code);
-
- @Override
- protected native void setEnabledImpl(boolean enabled);
-
- @NonNull
- public static native ARCheat[] loadCodes(String gameId, int revision);
-
- public static native void saveCodes(String gameId, int revision, ARCheat[] codes);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.kt
new file mode 100644
index 0000000000..7d3013ee32
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.kt
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import androidx.annotation.Keep
+
+class ARCheat : AbstractCheat {
+ @Keep
+ private val pointer: Long
+
+ constructor() {
+ pointer = createNew()
+ }
+
+ @Keep
+ private constructor(pointer: Long) {
+ this.pointer = pointer
+ }
+
+ external fun finalize()
+
+ private external fun createNew(): Long
+
+ override fun supportsCreator(): Boolean = false
+
+ override fun supportsNotes(): Boolean = false
+
+ external override fun getName(): String
+
+ external override fun getCode(): String
+
+ external override fun getUserDefined(): Boolean
+
+ external override fun getEnabled(): Boolean
+
+ external override fun setEnabledImpl(enabled: Boolean)
+
+ external override fun setCheatImpl(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int
+
+ companion object {
+ @JvmStatic
+ external fun loadCodes(gameId: String, revision: Int): Array<ARCheat>
+
+ @JvmStatic
+ external fun saveCodes(gameId: String, revision: Int, codes: Array<ARCheat>)
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java
deleted file mode 100644
index d672013b4e..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.NonNull;
-
-public abstract class AbstractCheat extends ReadOnlyCheat
-{
- public boolean supportsCode()
- {
- return true;
- }
-
- public int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes,
- @NonNull String code)
- {
- if (!code.isEmpty() && code.charAt(0) == '$')
- {
- int firstLineEnd = code.indexOf('\n');
- if (firstLineEnd == -1)
- {
- name = code.substring(1);
- code = "";
- }
- else
- {
- name = code.substring(1, firstLineEnd);
- code = code.substring(firstLineEnd + 1);
- }
- }
-
- if (name.isEmpty())
- return TRY_SET_FAIL_NO_NAME;
-
- int result = trySetImpl(name, creator, notes, code);
-
- if (result == TRY_SET_SUCCESS)
- onChanged();
-
- return result;
- }
-
- protected abstract int trySetImpl(@NonNull String name, @NonNull String creator,
- @NonNull String notes, @NonNull String code);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.kt
new file mode 100644
index 0000000000..d9e17ffd41
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.kt
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import org.dolphinemu.dolphinemu.features.cheats.model.Cheat.Companion.TRY_SET_FAIL_NO_NAME
+import org.dolphinemu.dolphinemu.features.cheats.model.Cheat.Companion.TRY_SET_SUCCESS
+
+abstract class AbstractCheat : ReadOnlyCheat() {
+ override fun supportsCode(): Boolean {
+ return true
+ }
+
+ override fun setCheat(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int {
+ var finalName = name
+ var finalCode = code
+ if (finalCode.isNotEmpty() && finalCode[0] == '$') {
+ val firstLineEnd = finalCode.indexOf('\n')
+ if (firstLineEnd == -1) {
+ finalName = finalCode.substring(1)
+ finalCode = ""
+ } else {
+ finalName = finalCode.substring(1, firstLineEnd)
+ finalCode = finalCode.substring(firstLineEnd + 1)
+ }
+ }
+
+ if (finalName.isEmpty()) return TRY_SET_FAIL_NO_NAME
+
+ val result = setCheatImpl(finalName, creator, notes, finalCode)
+
+ if (result == TRY_SET_SUCCESS) onChanged()
+
+ return result
+ }
+
+ protected abstract fun setCheatImpl(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java
deleted file mode 100644
index 142931b9cb..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public interface Cheat
-{
- int TRY_SET_FAIL_CODE_MIXED_ENCRYPTION = -3;
- int TRY_SET_FAIL_NO_CODE_LINES = -2;
- int TRY_SET_FAIL_NO_NAME = -1;
- int TRY_SET_SUCCESS = 0;
- // Result codes greater than 0 represent an error on the corresponding code line (one-indexed)
-
- boolean supportsCreator();
-
- boolean supportsNotes();
-
- boolean supportsCode();
-
- @NonNull
- String getName();
-
- @NonNull
- default String getCreator()
- {
- return "";
- }
-
- @NonNull
- default String getNotes()
- {
- return "";
- }
-
- @NonNull
- default String getCode()
- {
- return "";
- }
-
- int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes,
- @NonNull String code);
-
- boolean getUserDefined();
-
- boolean getEnabled();
-
- void setEnabled(boolean enabled);
-
- void setChangedCallback(@Nullable Runnable callback);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.kt
new file mode 100644
index 0000000000..848acf3ea1
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.kt
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+interface Cheat {
+ fun supportsCreator(): Boolean
+
+ fun supportsNotes(): Boolean
+
+ fun supportsCode(): Boolean
+
+ fun getName(): String = ""
+
+ fun getCreator(): String = ""
+
+ fun getNotes(): String = ""
+
+ fun getCode(): String = ""
+
+ fun setCheat(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int
+
+ fun getUserDefined(): Boolean
+
+ fun getEnabled(): Boolean
+
+ fun setEnabled(isChecked: Boolean)
+
+ fun setChangedCallback(callback: Runnable?)
+
+ companion object {
+ // Result codes greater than 0 represent an error on the corresponding code line (one-indexed)
+ const val TRY_SET_FAIL_CODE_MIXED_ENCRYPTION = -3
+ const val TRY_SET_FAIL_NO_CODE_LINES = -2
+ const val TRY_SET_FAIL_NO_NAME = -1
+ const val TRY_SET_SUCCESS = 0
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java
deleted file mode 100644
index 4d9900c168..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java
+++ /dev/null
@@ -1,321 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-import androidx.lifecycle.ViewModel;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-public class CheatsViewModel extends ViewModel
-{
- private boolean mLoaded = false;
-
- private int mSelectedCheatPosition = -1;
- private final MutableLiveData<Cheat> mSelectedCheat = new MutableLiveData<>(null);
- private final MutableLiveData<Boolean> mIsAdding = new MutableLiveData<>(false);
- private final MutableLiveData<Boolean> mIsEditing = new MutableLiveData<>(false);
-
- private final MutableLiveData<Integer> mCheatAddedEvent = new MutableLiveData<>(null);
- private final MutableLiveData<Integer> mCheatChangedEvent = new MutableLiveData<>(null);
- private final MutableLiveData<Integer> mCheatDeletedEvent = new MutableLiveData<>(null);
- private final MutableLiveData<Integer> mGeckoCheatsDownloadedEvent = new MutableLiveData<>(null);
- private final MutableLiveData<Boolean> mOpenDetailsViewEvent = new MutableLiveData<>(false);
-
- private GraphicsModGroup mGraphicsModGroup;
- private ArrayList<GraphicsMod> mGraphicsMods;
- private ArrayList<PatchCheat> mPatchCheats;
- private ArrayList<ARCheat> mARCheats;
- private ArrayList<GeckoCheat> mGeckoCheats;
-
- private boolean mGraphicsModsNeedSaving = false;
- private boolean mPatchCheatsNeedSaving = false;
- private boolean mARCheatsNeedSaving = false;
- private boolean mGeckoCheatsNeedSaving = false;
-
- public void load(String gameID, int revision)
- {
- if (mLoaded)
- return;
-
- mGraphicsModGroup = GraphicsModGroup.load(gameID);
- mGraphicsMods = new ArrayList<>();
- Collections.addAll(mGraphicsMods, mGraphicsModGroup.getMods());
-
- mPatchCheats = new ArrayList<>();
- Collections.addAll(mPatchCheats, PatchCheat.loadCodes(gameID, revision));
-
- mARCheats = new ArrayList<>();
- Collections.addAll(mARCheats, ARCheat.loadCodes(gameID, revision));
-
- mGeckoCheats = new ArrayList<>();
- Collections.addAll(mGeckoCheats, GeckoCheat.loadCodes(gameID, revision));
-
- for (GraphicsMod mod : mGraphicsMods)
- {
- mod.setChangedCallback(() -> mGraphicsModsNeedSaving = true);
- }
- for (PatchCheat cheat : mPatchCheats)
- {
- cheat.setChangedCallback(() -> mPatchCheatsNeedSaving = true);
- }
- for (ARCheat cheat : mARCheats)
- {
- cheat.setChangedCallback(() -> mARCheatsNeedSaving = true);
- }
- for (GeckoCheat cheat : mGeckoCheats)
- {
- cheat.setChangedCallback(() -> mGeckoCheatsNeedSaving = true);
- }
-
- mLoaded = true;
- }
-
- public void saveIfNeeded(String gameID, int revision)
- {
- if (mGraphicsModsNeedSaving)
- {
- mGraphicsModGroup.save();
- mGraphicsModsNeedSaving = false;
- }
-
- if (mPatchCheatsNeedSaving)
- {
- PatchCheat.saveCodes(gameID, revision, mPatchCheats.toArray(new PatchCheat[0]));
- mPatchCheatsNeedSaving = false;
- }
-
- if (mARCheatsNeedSaving)
- {
- ARCheat.saveCodes(gameID, revision, mARCheats.toArray(new ARCheat[0]));
- mARCheatsNeedSaving = false;
- }
-
- if (mGeckoCheatsNeedSaving)
- {
- GeckoCheat.saveCodes(gameID, revision, mGeckoCheats.toArray(new GeckoCheat[0]));
- mGeckoCheatsNeedSaving = false;
- }
- }
-
- public LiveData<Cheat> getSelectedCheat()
- {
- return mSelectedCheat;
- }
-
- public void setSelectedCheat(Cheat cheat, int position)
- {
- if (mIsEditing.getValue())
- setIsEditing(false);
-
- mSelectedCheat.setValue(cheat);
- mSelectedCheatPosition = position;
- }
-
- public LiveData<Boolean> getIsAdding()
- {
- return mIsAdding;
- }
-
- public void startAddingCheat(Cheat cheat, int position)
- {
- mSelectedCheat.setValue(cheat);
- mSelectedCheatPosition = position;
-
- mIsAdding.setValue(true);
- mIsEditing.setValue(true);
- }
-
- public void finishAddingCheat()
- {
- if (!mIsAdding.getValue())
- throw new IllegalStateException();
-
- mIsAdding.setValue(false);
- mIsEditing.setValue(false);
-
- Cheat cheat = mSelectedCheat.getValue();
-
- if (cheat instanceof PatchCheat)
- {
- mPatchCheats.add((PatchCheat) mSelectedCheat.getValue());
- cheat.setChangedCallback(() -> mPatchCheatsNeedSaving = true);
- mPatchCheatsNeedSaving = true;
- }
- else if (cheat instanceof ARCheat)
- {
- mARCheats.add((ARCheat) mSelectedCheat.getValue());
- cheat.setChangedCallback(() -> mPatchCheatsNeedSaving = true);
- mARCheatsNeedSaving = true;
- }
- else if (cheat instanceof GeckoCheat)
- {
- mGeckoCheats.add((GeckoCheat) mSelectedCheat.getValue());
- cheat.setChangedCallback(() -> mGeckoCheatsNeedSaving = true);
- mGeckoCheatsNeedSaving = true;
- }
- else
- {
- throw new UnsupportedOperationException();
- }
-
- notifyCheatAdded();
- }
-
- public LiveData<Boolean> getIsEditing()
- {
- return mIsEditing;
- }
-
- public void setIsEditing(boolean isEditing)
- {
- mIsEditing.setValue(isEditing);
-
- if (mIsAdding.getValue() && !isEditing)
- {
- mIsAdding.setValue(false);
- setSelectedCheat(null, -1);
- }
- }
-
- /**
- * When a cheat is added, the integer stored in the returned LiveData
- * changes to the position of that cheat, then changes back to null.
- */
- public LiveData<Integer> getCheatAddedEvent()
- {
- return mCheatAddedEvent;
- }
-
- private void notifyCheatAdded()
- {
- mCheatAddedEvent.setValue(mSelectedCheatPosition);
- mCheatAddedEvent.setValue(null);
- }
-
- /**
- * When a cheat is edited, the integer stored in the returned LiveData
- * changes to the position of that cheat, then changes back to null.
- */
- public LiveData<Integer> getCheatChangedEvent()
- {
- return mCheatChangedEvent;
- }
-
- /**
- * Notifies that an edit has been made to the contents of the currently selected cheat.
- */
- public void notifySelectedCheatChanged()
- {
- notifyCheatChanged(mSelectedCheatPosition);
- }
-
- /**
- * Notifies that an edit has been made to the contents of the cheat at the given position.
- */
- public void notifyCheatChanged(int position)
- {
- mCheatChangedEvent.setValue(position);
- mCheatChangedEvent.setValue(null);
- }
-
- /**
- * When a cheat is deleted, the integer stored in the returned LiveData
- * changes to the position of that cheat, then changes back to null.
- */
- public LiveData<Integer> getCheatDeletedEvent()
- {
- return mCheatDeletedEvent;
- }
-
- public void deleteSelectedCheat()
- {
- Cheat cheat = mSelectedCheat.getValue();
- int position = mSelectedCheatPosition;
-
- setSelectedCheat(null, -1);
-
- if (mPatchCheats.remove(cheat))
- mPatchCheatsNeedSaving = true;
- if (mARCheats.remove(cheat))
- mARCheatsNeedSaving = true;
- if (mGeckoCheats.remove(cheat))
- mGeckoCheatsNeedSaving = true;
-
- notifyCheatDeleted(position);
- }
-
- /**
- * Notifies that the cheat at the given position has been deleted.
- */
- private void notifyCheatDeleted(int position)
- {
- mCheatDeletedEvent.setValue(position);
- mCheatDeletedEvent.setValue(null);
- }
-
- /**
- * When Gecko cheats are downloaded, the integer stored in the returned LiveData
- * changes to the number of cheats added, then changes back to null.
- */
- public LiveData<Integer> getGeckoCheatsDownloadedEvent()
- {
- return mGeckoCheatsDownloadedEvent;
- }
-
- public int addDownloadedGeckoCodes(GeckoCheat[] cheats)
- {
- int cheatsAdded = 0;
-
- for (GeckoCheat cheat : cheats)
- {
- if (!mGeckoCheats.contains(cheat))
- {
- mGeckoCheats.add(cheat);
- cheatsAdded++;
- }
- }
-
- if (cheatsAdded != 0)
- {
- mGeckoCheatsNeedSaving = true;
- mGeckoCheatsDownloadedEvent.setValue(cheatsAdded);
- mGeckoCheatsDownloadedEvent.setValue(null);
- }
-
- return cheatsAdded;
- }
-
- public LiveData<Boolean> getOpenDetailsViewEvent()
- {
- return mOpenDetailsViewEvent;
- }
-
- public void openDetailsView()
- {
- mOpenDetailsViewEvent.setValue(true);
- mOpenDetailsViewEvent.setValue(false);
- }
-
- public ArrayList<GraphicsMod> getGraphicsMods()
- {
- return mGraphicsMods;
- }
-
- public ArrayList<PatchCheat> getPatchCheats()
- {
- return mPatchCheats;
- }
-
- public ArrayList<ARCheat> getARCheats()
- {
- return mARCheats;
- }
-
- public ArrayList<GeckoCheat> getGeckoCheats()
- {
- return mGeckoCheats;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.kt
new file mode 100644
index 0000000000..e2d52bbc78
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.kt
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat.Companion.loadCodes
+import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat.Companion.saveCodes
+import kotlin.collections.ArrayList
+
+class CheatsViewModel : ViewModel() {
+ private var loaded = false
+
+ private var selectedCheatPosition = -1
+ private val _selectedCheat = MutableLiveData<Cheat?>(null)
+ val selectedCheat: LiveData<Cheat?> get() = _selectedCheat
+ private val _isAdding = MutableLiveData(false)
+ val isAdding: LiveData<Boolean> get() = _isAdding
+ private val _isEditing = MutableLiveData(false)
+ val isEditing: LiveData<Boolean> get() = _isEditing
+
+ private val _cheatAddedEvent = MutableLiveData<Int?>(null)
+ val cheatAddedEvent: LiveData<Int?> get() = _cheatAddedEvent
+ private val _cheatChangedEvent = MutableLiveData<Int?>(null)
+ val cheatChangedEvent: LiveData<Int?> get() = _cheatChangedEvent
+ private val _cheatDeletedEvent = MutableLiveData<Int?>(null)
+ val cheatDeletedEvent: LiveData<Int?> get() = _cheatDeletedEvent
+ private val _geckoCheatsDownloadedEvent = MutableLiveData<Int?>(null)
+ val geckoCheatsDownloadedEvent: LiveData<Int?> get() = _geckoCheatsDownloadedEvent
+ private val _openDetailsViewEvent = MutableLiveData(false)
+ val openDetailsViewEvent: LiveData<Boolean> get() = _openDetailsViewEvent
+
+ private var graphicsModGroup: GraphicsModGroup? = null
+ var graphicsMods: ArrayList<GraphicsMod> = ArrayList()
+ var patchCheats: ArrayList<PatchCheat> = ArrayList()
+ var aRCheats: ArrayList<ARCheat> = ArrayList()
+ var geckoCheats: ArrayList<GeckoCheat> = ArrayList()
+
+ private var graphicsModsNeedSaving = false
+ private var patchCheatsNeedSaving = false
+ private var aRCheatsNeedSaving = false
+ private var geckoCheatsNeedSaving = false
+
+ fun load(gameID: String, revision: Int) {
+ if (loaded) return
+
+ graphicsModGroup = GraphicsModGroup.load(gameID)
+ graphicsMods.addAll(graphicsModGroup!!.mods)
+ patchCheats.addAll(PatchCheat.loadCodes(gameID, revision))
+ aRCheats.addAll(loadCodes(gameID, revision))
+ geckoCheats.addAll(GeckoCheat.loadCodes(gameID, revision))
+
+ for (mod in graphicsMods) {
+ mod.setChangedCallback { graphicsModsNeedSaving = true }
+ }
+ for (cheat in patchCheats) {
+ cheat.setChangedCallback { patchCheatsNeedSaving = true }
+ }
+ for (cheat in aRCheats) {
+ cheat.setChangedCallback { aRCheatsNeedSaving = true }
+ }
+ for (cheat in geckoCheats) {
+ cheat.setChangedCallback { geckoCheatsNeedSaving = true }
+ }
+
+ loaded = true
+ }
+
+ fun saveIfNeeded(gameID: String, revision: Int) {
+ if (graphicsModsNeedSaving) {
+ graphicsModGroup!!.save()
+ graphicsModsNeedSaving = false
+ }
+
+ if (patchCheatsNeedSaving) {
+ PatchCheat.saveCodes(gameID, revision, patchCheats.toTypedArray())
+ patchCheatsNeedSaving = false
+ }
+
+ if (aRCheatsNeedSaving) {
+ saveCodes(gameID, revision, aRCheats.toTypedArray())
+ aRCheatsNeedSaving = false
+ }
+
+ if (geckoCheatsNeedSaving) {
+ GeckoCheat.saveCodes(gameID, revision, geckoCheats.toTypedArray())
+ geckoCheatsNeedSaving = false
+ }
+ }
+
+ fun setSelectedCheat(cheat: Cheat?, position: Int) {
+ if (isEditing.value!!) setIsEditing(false)
+
+ _selectedCheat.value = cheat
+ selectedCheatPosition = position
+ }
+
+ fun startAddingCheat(cheat: Cheat?, position: Int) {
+ _selectedCheat.value = cheat
+ selectedCheatPosition = position
+
+ _isAdding.value = true
+ _isEditing.value = true
+ }
+
+ fun finishAddingCheat() {
+ check(isAdding.value!!)
+
+ _isAdding.value = false
+ _isEditing.value = false
+
+ when (val cheat = selectedCheat.value) {
+ is PatchCheat -> {
+ patchCheats.add(cheat)
+ cheat.setChangedCallback(Runnable { patchCheatsNeedSaving = true })
+ patchCheatsNeedSaving = true
+ }
+ is ARCheat -> {
+ aRCheats.add(cheat)
+ cheat.setChangedCallback(Runnable { patchCheatsNeedSaving = true })
+ aRCheatsNeedSaving = true
+ }
+ is GeckoCheat -> {
+ geckoCheats.add(cheat)
+ cheat.setChangedCallback(Runnable { geckoCheatsNeedSaving = true })
+ geckoCheatsNeedSaving = true
+ }
+ else -> throw UnsupportedOperationException()
+ }
+
+ notifyCheatAdded()
+ }
+
+ fun setIsEditing(isEditing: Boolean) {
+ _isEditing.value = isEditing
+ if (isAdding.value!! && !isEditing) {
+ _isAdding.value = false
+ setSelectedCheat(null, -1)
+ }
+ }
+
+ private fun notifyCheatAdded() {
+ _cheatAddedEvent.value = selectedCheatPosition
+ _cheatAddedEvent.value = null
+ }
+
+ /**
+ * Notifies that an edit has been made to the contents of the currently selected cheat.
+ */
+ fun notifySelectedCheatChanged() {
+ notifyCheatChanged(selectedCheatPosition)
+ }
+
+ /**
+ * Notifies that an edit has been made to the contents of the cheat at the given position.
+ */
+ private fun notifyCheatChanged(position: Int) {
+ _cheatChangedEvent.value = position
+ _cheatChangedEvent.value = null
+ }
+
+ fun deleteSelectedCheat() {
+ val cheat = selectedCheat.value
+ val position = selectedCheatPosition
+
+ setSelectedCheat(null, -1)
+
+ if (patchCheats.remove(cheat)) patchCheatsNeedSaving = true
+ if (aRCheats.remove(cheat)) aRCheatsNeedSaving = true
+ if (geckoCheats.remove(cheat)) geckoCheatsNeedSaving = true
+
+ notifyCheatDeleted(position)
+ }
+
+ /**
+ * Notifies that the cheat at the given position has been deleted.
+ */
+ private fun notifyCheatDeleted(position: Int) {
+ _cheatDeletedEvent.value = position
+ _cheatDeletedEvent.value = null
+ }
+
+ fun addDownloadedGeckoCodes(cheats: Array<GeckoCheat>): Int {
+ var cheatsAdded = 0
+
+ for (cheat in cheats) {
+ if (!geckoCheats.contains(cheat)) {
+ geckoCheats.add(cheat)
+ cheatsAdded++
+ }
+ }
+
+ if (cheatsAdded != 0) {
+ geckoCheatsNeedSaving = true
+ _geckoCheatsDownloadedEvent.value = cheatsAdded
+ _geckoCheatsDownloadedEvent.value = null
+ }
+
+ return cheatsAdded
+ }
+
+ fun openDetailsView() {
+ _openDetailsViewEvent.value = true
+ _openDetailsViewEvent.value = false
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java
deleted file mode 100644
index 4397a95d81..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class GeckoCheat extends AbstractCheat
-{
- @Keep
- private final long mPointer;
-
- public GeckoCheat()
- {
- mPointer = createNew();
- }
-
- @Keep
- private GeckoCheat(long pointer)
- {
- mPointer = pointer;
- }
-
- @Override
- public native void finalize();
-
- private native long createNew();
-
- @Override
- public boolean equals(@Nullable Object obj)
- {
- return obj != null && getClass() == obj.getClass() && equalsImpl((GeckoCheat) obj);
- }
-
- public boolean supportsCreator()
- {
- return true;
- }
-
- public boolean supportsNotes()
- {
- return true;
- }
-
- @NonNull
- public native String getName();
-
- @NonNull
- public native String getCreator();
-
- @NonNull
- public native String getNotes();
-
- @NonNull
- public native String getCode();
-
- public native boolean getUserDefined();
-
- public native boolean getEnabled();
-
- public native boolean equalsImpl(@NonNull GeckoCheat other);
-
- @Override
- protected native int trySetImpl(@NonNull String name, @NonNull String creator,
- @NonNull String notes, @NonNull String code);
-
- @Override
- protected native void setEnabledImpl(boolean enabled);
-
- @NonNull
- public static native GeckoCheat[] loadCodes(String gameId, int revision);
-
- public static native void saveCodes(String gameId, int revision, GeckoCheat[] codes);
-
- @Nullable
- public static native GeckoCheat[] downloadCodes(String gameTdbId);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.kt
new file mode 100644
index 0000000000..7a23066545
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.kt
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import androidx.annotation.Keep
+
+class GeckoCheat : AbstractCheat {
+ @Keep
+ private val mPointer: Long
+
+ constructor() {
+ mPointer = createNew()
+ }
+
+ @Keep
+ private constructor(pointer: Long) {
+ mPointer = pointer
+ }
+
+ external fun finalize()
+
+ private external fun createNew(): Long
+
+ override fun equals(other: Any?): Boolean {
+ return other != null && javaClass == other.javaClass && equalsImpl(other as GeckoCheat)
+ }
+
+ override fun hashCode(): Int {
+ return mPointer.hashCode()
+ }
+
+ override fun supportsCreator(): Boolean {
+ return true
+ }
+
+ override fun supportsNotes(): Boolean {
+ return true
+ }
+
+ external override fun getName(): String
+
+ external override fun getCreator(): String
+
+ external override fun getNotes(): String
+
+ external override fun getCode(): String
+
+ external override fun getUserDefined(): Boolean
+
+ external override fun getEnabled(): Boolean
+
+ private external fun equalsImpl(other: GeckoCheat): Boolean
+
+ external override fun setCheatImpl(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int
+
+ external override fun setEnabledImpl(enabled: Boolean)
+
+ companion object {
+ @JvmStatic
+ external fun loadCodes(gameId: String, revision: Int): Array<GeckoCheat>
+
+ @JvmStatic
+ external fun saveCodes(gameId: String, revision: Int, codes: Array<GeckoCheat>)
+
+ @JvmStatic
+ external fun downloadCodes(gameTdbId: String): Array<GeckoCheat>?
+ }
+} \ No newline at end of file
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.java
deleted file mode 100644
index 241d15093f..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class GraphicsMod extends ReadOnlyCheat
-{
- @Keep
- private final long mPointer;
-
- // When a C++ GraphicsModGroup object is destroyed, it also destroys the GraphicsMods it owns.
- // To avoid getting dangling pointers, we keep a reference to the GraphicsModGroup here.
- @Keep
- private final GraphicsModGroup mParent;
-
- @Keep
- private GraphicsMod(long pointer, GraphicsModGroup parent)
- {
- mPointer = pointer;
- mParent = parent;
- }
-
- public boolean supportsCreator()
- {
- return true;
- }
-
- public boolean supportsNotes()
- {
- return true;
- }
-
- public boolean supportsCode()
- {
- return false;
- }
-
- @NonNull
- public native String getName();
-
- @NonNull
- public native String getCreator();
-
- @NonNull
- public native String getNotes();
-
- public boolean getUserDefined()
- {
- // Technically graphics mods can be user defined, but we don't support editing graphics mods
- // in the GUI, and editability is what this really controls
- return false;
- }
-
- public native boolean getEnabled();
-
- @Override
- protected native void setEnabledImpl(boolean enabled);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.kt
new file mode 100644
index 0000000000..3ea8c23e43
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod.kt
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import androidx.annotation.Keep
+
+class GraphicsMod @Keep private constructor(
+ @Keep private val pointer: Long,
+ // When a C++ GraphicsModGroup object is destroyed, it also destroys the GraphicsMods it owns.
+ // To avoid getting dangling pointers, we keep a reference to the GraphicsModGroup here.
+ @Keep private val parent: GraphicsModGroup
+) : ReadOnlyCheat() {
+ override fun supportsCreator(): Boolean = true
+
+ override fun supportsNotes(): Boolean = true
+
+ override fun supportsCode(): Boolean = false
+
+ external override fun getName(): String
+
+ external override fun getCreator(): String
+
+ external override fun getNotes(): String
+
+ // Technically graphics mods can be user defined, but we don't support editing graphics mods
+ // in the GUI, and editability is what this really controls
+ override fun getUserDefined(): Boolean = false
+
+ external override fun getEnabled(): Boolean
+
+ external override fun setEnabledImpl(enabled: Boolean)
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.java
deleted file mode 100644
index 0cb0a3c456..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-
-public class GraphicsModGroup
-{
- @Keep
- private final long mPointer;
-
- @Keep
- private GraphicsModGroup(long pointer)
- {
- mPointer = pointer;
- }
-
- @Override
- public native void finalize();
-
- @NonNull
- public native GraphicsMod[] getMods();
-
- public native void save();
-
- @NonNull
- public static native GraphicsModGroup load(String gameId);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.kt
new file mode 100644
index 0000000000..5d9b8b2217
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup.kt
@@ -0,0 +1,17 @@
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import androidx.annotation.Keep
+
+class GraphicsModGroup @Keep private constructor(@field:Keep private val pointer: Long) {
+ external fun finalize()
+
+ val mods: Array<GraphicsMod>
+ external get
+
+ external fun save()
+
+ companion object {
+ @JvmStatic
+ external fun load(gameId: String): GraphicsModGroup
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java
deleted file mode 100644
index 9dcb9f8111..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-
-public class PatchCheat extends AbstractCheat
-{
- @Keep
- private final long mPointer;
-
- public PatchCheat()
- {
- mPointer = createNew();
- }
-
- @Keep
- private PatchCheat(long pointer)
- {
- mPointer = pointer;
- }
-
- @Override
- public native void finalize();
-
- private native long createNew();
-
- public boolean supportsCreator()
- {
- return false;
- }
-
- public boolean supportsNotes()
- {
- return false;
- }
-
- @NonNull
- public native String getName();
-
- @NonNull
- public native String getCode();
-
- public native boolean getUserDefined();
-
- public native boolean getEnabled();
-
- @Override
- protected native int trySetImpl(@NonNull String name, @NonNull String creator,
- @NonNull String notes, @NonNull String code);
-
- @Override
- protected native void setEnabledImpl(boolean enabled);
-
- @NonNull
- public static native PatchCheat[] loadCodes(String gameId, int revision);
-
- public static native void saveCodes(String gameId, int revision, PatchCheat[] codes);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.kt
new file mode 100644
index 0000000000..f4cd9660a4
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.kt
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+import androidx.annotation.Keep
+
+class PatchCheat : AbstractCheat {
+ @Keep
+ private val pointer: Long
+
+ constructor() {
+ pointer = createNew()
+ }
+
+ @Keep
+ private constructor(pointer: Long) {
+ this.pointer = pointer
+ }
+
+ external fun finalize()
+
+ private external fun createNew(): Long
+
+ override fun supportsCreator(): Boolean {
+ return false
+ }
+
+ override fun supportsNotes(): Boolean {
+ return false
+ }
+
+ external override fun getName(): String
+
+ external override fun getCode(): String
+
+ external override fun getUserDefined(): Boolean
+
+ external override fun getEnabled(): Boolean
+
+ external override fun setCheatImpl(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int
+
+ external override fun setEnabledImpl(enabled: Boolean)
+
+ companion object {
+ @JvmStatic
+ external fun loadCodes(gameId: String, revision: Int): Array<PatchCheat>
+
+ @JvmStatic
+ external fun saveCodes(gameId: String, revision: Int, codes: Array<PatchCheat>)
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.java
deleted file mode 100644
index 9751c414f8..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.java
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public abstract class ReadOnlyCheat implements Cheat
-{
- private Runnable mChangedCallback = null;
-
- public int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes,
- @NonNull String code)
- {
- throw new UnsupportedOperationException();
- }
-
- public void setEnabled(boolean enabled)
- {
- setEnabledImpl(enabled);
- onChanged();
- }
-
- public void setChangedCallback(@Nullable Runnable callback)
- {
- mChangedCallback = callback;
- }
-
- protected void onChanged()
- {
- if (mChangedCallback != null)
- mChangedCallback.run();
- }
-
- protected abstract void setEnabledImpl(boolean enabled);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.kt
new file mode 100644
index 0000000000..542a407792
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ReadOnlyCheat.kt
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.model
+
+abstract class ReadOnlyCheat : Cheat {
+ private var onChangedCallback: Runnable? = null
+
+ override fun setCheat(
+ name: String,
+ creator: String,
+ notes: String,
+ code: String
+ ): Int {
+ throw UnsupportedOperationException()
+ }
+
+ override fun setEnabled(isChecked: Boolean) {
+ setEnabledImpl(isChecked)
+ onChanged()
+ }
+
+ override fun setChangedCallback(callback: Runnable?) {
+ onChangedCallback = callback
+ }
+
+ protected fun onChanged() {
+ if (onChangedCallback != null) onChangedCallback!!.run()
+ }
+
+ protected abstract fun setEnabledImpl(enabled: Boolean)
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java
deleted file mode 100644
index fd6617cd21..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java
+++ /dev/null
@@ -1,68 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModelProvider;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
-import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat;
-import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
-import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat;
-import org.dolphinemu.dolphinemu.features.cheats.model.PatchCheat;
-
-public class ActionViewHolder extends CheatItemViewHolder implements View.OnClickListener
-{
- private final TextView mName;
-
- private CheatsActivity mActivity;
- private CheatsViewModel mViewModel;
- private int mString;
- private int mPosition;
-
- public ActionViewHolder(@NonNull ListItemSubmenuBinding binding)
- {
- super(binding.getRoot());
-
- mName = binding.textSettingName;
-
- binding.getRoot().setOnClickListener(this);
- }
-
- public void bind(CheatsActivity activity, CheatItem item, int position)
- {
- mActivity = activity;
- mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
- mString = item.getString();
- mPosition = position;
-
- mName.setText(mString);
- }
-
- public void onClick(View root)
- {
- if (mString == R.string.cheats_add_ar)
- {
- mViewModel.startAddingCheat(new ARCheat(), mPosition);
- mViewModel.openDetailsView();
- }
- else if (mString == R.string.cheats_add_gecko)
- {
- mViewModel.startAddingCheat(new GeckoCheat(), mPosition);
- mViewModel.openDetailsView();
- }
- else if (mString == R.string.cheats_add_patch)
- {
- mViewModel.startAddingCheat(new PatchCheat(), mPosition);
- mViewModel.openDetailsView();
- }
- else if (mString == R.string.cheats_download_gecko)
- {
- mActivity.downloadGeckoCodes();
- }
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.kt
new file mode 100644
index 0000000000..3bab551ca4
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.kt
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.view.View
+import android.widget.TextView
+import androidx.lifecycle.ViewModelProvider
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding
+import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat
+import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel
+import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat
+import org.dolphinemu.dolphinemu.features.cheats.model.PatchCheat
+
+class ActionViewHolder(binding: ListItemSubmenuBinding) : CheatItemViewHolder(binding.root),
+ View.OnClickListener {
+ private val mName: TextView
+
+ private lateinit var activity: CheatsActivity
+ private lateinit var viewModel: CheatsViewModel
+ private var string = 0
+ private var position = 0
+
+ init {
+ mName = binding.textSettingName
+ binding.root.setOnClickListener(this)
+ }
+
+ override fun bind(activity: CheatsActivity, item: CheatItem, position: Int) {
+ this.activity = activity
+ viewModel = ViewModelProvider(this.activity)[CheatsViewModel::class.java]
+ string = item.string
+ this.position = position
+ mName.setText(string)
+ }
+
+ override fun onClick(root: View) {
+ when(string) {
+ R.string.cheats_add_ar -> {
+ viewModel.startAddingCheat(ARCheat(), position)
+ viewModel.openDetailsView()
+ }
+ R.string.cheats_add_gecko -> {
+ viewModel.startAddingCheat(GeckoCheat(), position)
+ viewModel.openDetailsView()
+ }
+ R.string.cheats_add_patch -> {
+ viewModel.startAddingCheat(PatchCheat(), position)
+ viewModel.openDetailsView()
+ }
+ R.string.cheats_download_gecko -> activity.downloadGeckoCodes()
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java
deleted file mode 100644
index 9e118e3932..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.FragmentCheatDetailsBinding;
-import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
-import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
-
-public class CheatDetailsFragment extends Fragment
-{
- private CheatsViewModel mViewModel;
- private Cheat mCheat;
-
- private FragmentCheatDetailsBinding mBinding;
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState)
- {
- mBinding = FragmentCheatDetailsBinding.inflate(inflater, container, false);
- return mBinding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
- {
- CheatsActivity activity = (CheatsActivity) requireActivity();
- mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
-
- mViewModel.getSelectedCheat().observe(getViewLifecycleOwner(), this::onSelectedCheatUpdated);
- mViewModel.getIsEditing().observe(getViewLifecycleOwner(), this::onIsEditingUpdated);
-
- mBinding.buttonDelete.setOnClickListener(this::onDeleteClicked);
- mBinding.buttonEdit.setOnClickListener(this::onEditClicked);
- mBinding.buttonCancel.setOnClickListener(this::onCancelClicked);
- mBinding.buttonOk.setOnClickListener(this::onOkClicked);
-
- CheatsActivity.setOnFocusChangeListenerRecursively(view,
- (v, hasFocus) -> activity.onDetailsViewFocusChange(hasFocus));
- }
-
- @Override
- public void onDestroyView()
- {
- super.onDestroyView();
- mBinding = null;
- }
-
- private void clearEditErrors()
- {
- mBinding.editName.setError(null);
- mBinding.editCode.setError(null);
- }
-
- private void onDeleteClicked(View view)
- {
- new MaterialAlertDialogBuilder(requireContext())
- .setMessage(getString(R.string.cheats_delete_confirmation, mCheat.getName()))
- .setPositiveButton(R.string.yes, (dialog, i) -> mViewModel.deleteSelectedCheat())
- .setNegativeButton(R.string.no, null)
- .show();
- }
-
- private void onEditClicked(View view)
- {
- mViewModel.setIsEditing(true);
- mBinding.buttonOk.requestFocus();
- }
-
- private void onCancelClicked(View view)
- {
- mViewModel.setIsEditing(false);
- onSelectedCheatUpdated(mCheat);
- mBinding.buttonDelete.requestFocus();
- }
-
- private void onOkClicked(View view)
- {
- clearEditErrors();
-
- int result = mCheat.trySet(mBinding.editNameInput.getText().toString(),
- mBinding.editCreatorInput.getText().toString(),
- mBinding.editNotesInput.getText().toString(),
- mBinding.editCodeInput.getText().toString());
-
- switch (result)
- {
- case Cheat.TRY_SET_SUCCESS:
- if (mViewModel.getIsAdding().getValue())
- {
- mViewModel.finishAddingCheat();
- onSelectedCheatUpdated(mCheat);
- }
- else
- {
- mViewModel.notifySelectedCheatChanged();
- mViewModel.setIsEditing(false);
- }
- mBinding.buttonEdit.requestFocus();
- break;
- case Cheat.TRY_SET_FAIL_NO_NAME:
- mBinding.editName.setError(getString(R.string.cheats_error_no_name));
- mBinding.scrollView.smoothScrollTo(0, mBinding.editNameInput.getTop());
- break;
- case Cheat.TRY_SET_FAIL_NO_CODE_LINES:
- mBinding.editCode.setError(getString(R.string.cheats_error_no_code_lines));
- mBinding.scrollView.smoothScrollTo(0, mBinding.editCodeInput.getBottom());
- break;
- case Cheat.TRY_SET_FAIL_CODE_MIXED_ENCRYPTION:
- mBinding.editCode.setError(getString(R.string.cheats_error_mixed_encryption));
- mBinding.scrollView.smoothScrollTo(0, mBinding.editCodeInput.getBottom());
- break;
- default:
- mBinding.editCode.setError(getString(R.string.cheats_error_on_line, result));
- mBinding.scrollView.smoothScrollTo(0, mBinding.editCodeInput.getBottom());
- break;
- }
- }
-
- private void onSelectedCheatUpdated(@Nullable Cheat cheat)
- {
- clearEditErrors();
-
- mBinding.root.setVisibility(cheat == null ? View.GONE : View.VISIBLE);
-
- int creatorVisibility = cheat != null && cheat.supportsCreator() ? View.VISIBLE : View.GONE;
- int notesVisibility = cheat != null && cheat.supportsNotes() ? View.VISIBLE : View.GONE;
- int codeVisibility = cheat != null && cheat.supportsCode() ? View.VISIBLE : View.GONE;
- mBinding.editCreator.setVisibility(creatorVisibility);
- mBinding.editNotes.setVisibility(notesVisibility);
- mBinding.editCode.setVisibility(codeVisibility);
-
- boolean userDefined = cheat != null && cheat.getUserDefined();
- mBinding.buttonDelete.setEnabled(userDefined);
- mBinding.buttonEdit.setEnabled(userDefined);
-
- // If the fragment was recreated while editing a cheat, it's vital that we
- // don't repopulate the fields, otherwise the user's changes will be lost
- boolean isEditing = mViewModel.getIsEditing().getValue();
-
- if (!isEditing && cheat != null)
- {
- mBinding.editNameInput.setText(cheat.getName());
- mBinding.editCreatorInput.setText(cheat.getCreator());
- mBinding.editNotesInput.setText(cheat.getNotes());
- mBinding.editCodeInput.setText(cheat.getCode());
- }
-
- mCheat = cheat;
- }
-
- private void onIsEditingUpdated(boolean isEditing)
- {
- mBinding.editNameInput.setEnabled(isEditing);
- mBinding.editCreatorInput.setEnabled(isEditing);
- mBinding.editNotesInput.setEnabled(isEditing);
- mBinding.editCodeInput.setEnabled(isEditing);
-
- mBinding.buttonDelete.setVisibility(isEditing ? View.GONE : View.VISIBLE);
- mBinding.buttonEdit.setVisibility(isEditing ? View.GONE : View.VISIBLE);
- mBinding.buttonCancel.setVisibility(isEditing ? View.VISIBLE : View.GONE);
- mBinding.buttonOk.setVisibility(isEditing ? View.VISIBLE : View.GONE);
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.kt
new file mode 100644
index 0000000000..1d473ef6e0
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.kt
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.content.DialogInterface
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.databinding.FragmentCheatDetailsBinding
+import org.dolphinemu.dolphinemu.features.cheats.model.Cheat
+import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel
+
+class CheatDetailsFragment : Fragment() {
+ private lateinit var viewModel: CheatsViewModel
+ private var cheat: Cheat? = null
+
+ private var _binding: FragmentCheatDetailsBinding? = null
+ private val binding get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentCheatDetailsBinding.inflate(inflater, container, false)
+ return binding.getRoot()
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val activity = requireActivity() as CheatsActivity
+ viewModel = ViewModelProvider(activity)[CheatsViewModel::class.java]
+
+ viewModel.selectedCheat.observe(viewLifecycleOwner) { cheat: Cheat? ->
+ onSelectedCheatUpdated(
+ cheat
+ )
+ }
+ viewModel.isEditing.observe(viewLifecycleOwner) { isEditing: Boolean ->
+ onIsEditingUpdated(
+ isEditing
+ )
+ }
+
+ binding.buttonDelete.setOnClickListener { onDeleteClicked() }
+ binding.buttonEdit.setOnClickListener { onEditClicked() }
+ binding.buttonCancel.setOnClickListener { onCancelClicked() }
+ binding.buttonOk.setOnClickListener { onOkClicked() }
+
+ CheatsActivity.setOnFocusChangeListenerRecursively(
+ view
+ ) { _: View?, hasFocus: Boolean -> activity.onDetailsViewFocusChange(hasFocus) }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ private fun clearEditErrors() {
+ binding.editName.error = null
+ binding.editCode.error = null
+ }
+
+ private fun onDeleteClicked() {
+ MaterialAlertDialogBuilder(requireContext())
+ .setMessage(getString(R.string.cheats_delete_confirmation, cheat!!.getName()))
+ .setPositiveButton(R.string.yes) { _: DialogInterface?, i: Int -> viewModel.deleteSelectedCheat() }
+ .setNegativeButton(R.string.no, null)
+ .show()
+ }
+
+ private fun onEditClicked() {
+ viewModel.setIsEditing(true)
+ binding.buttonOk.requestFocus()
+ }
+
+ private fun onCancelClicked() {
+ viewModel.setIsEditing(false)
+ onSelectedCheatUpdated(cheat)
+ binding.buttonDelete.requestFocus()
+ }
+
+ private fun onOkClicked() {
+ clearEditErrors()
+
+ val result = cheat!!.setCheat(
+ binding.editNameInput.text.toString(),
+ binding.editCreatorInput.text.toString(),
+ binding.editNotesInput.text.toString(),
+ binding.editCodeInput.text.toString()
+ )
+
+ when (result) {
+ Cheat.TRY_SET_SUCCESS -> {
+ if (viewModel.isAdding.value!!) {
+ viewModel.finishAddingCheat()
+ onSelectedCheatUpdated(cheat)
+ } else {
+ viewModel.notifySelectedCheatChanged()
+ viewModel.setIsEditing(false)
+ }
+ binding.buttonEdit.requestFocus()
+ }
+ Cheat.TRY_SET_FAIL_NO_NAME -> {
+ binding.editName.error = getString(R.string.cheats_error_no_name)
+ binding.scrollView.smoothScrollTo(0, binding.editNameInput.top)
+ }
+ Cheat.TRY_SET_FAIL_NO_CODE_LINES -> {
+ binding.editCode.error = getString(R.string.cheats_error_no_code_lines)
+ binding.scrollView.smoothScrollTo(0, binding.editCodeInput.bottom)
+ }
+ Cheat.TRY_SET_FAIL_CODE_MIXED_ENCRYPTION -> {
+ binding.editCode.error = getString(R.string.cheats_error_mixed_encryption)
+ binding.scrollView.smoothScrollTo(0, binding.editCodeInput.bottom)
+ }
+ else -> {
+ binding.editCode.error = getString(R.string.cheats_error_on_line, result)
+ binding.scrollView.smoothScrollTo(0, binding.editCodeInput.bottom)
+ }
+ }
+ }
+
+ private fun onSelectedCheatUpdated(cheat: Cheat?) {
+ clearEditErrors()
+
+ binding.root.visibility = if (cheat == null) View.GONE else View.VISIBLE
+
+ val creatorVisibility =
+ if (cheat != null && cheat.supportsCreator()) View.VISIBLE else View.GONE
+ val notesVisibility =
+ if (cheat != null && cheat.supportsNotes()) View.VISIBLE else View.GONE
+ val codeVisibility = if (cheat != null && cheat.supportsCode()) View.VISIBLE else View.GONE
+ binding.editCreator.visibility = creatorVisibility
+ binding.editNotes.visibility = notesVisibility
+ binding.editCode.visibility = codeVisibility
+
+ val userDefined = cheat != null && cheat.getUserDefined()
+ binding.buttonDelete.isEnabled = userDefined
+ binding.buttonEdit.isEnabled = userDefined
+
+ // If the fragment was recreated while editing a cheat, it's vital that we
+ // don't repopulate the fields, otherwise the user's changes will be lost
+ val isEditing = viewModel.isEditing.value!!
+
+ if (!isEditing && cheat != null) {
+ binding.editNameInput.setText(cheat.getName())
+ binding.editCreatorInput.setText(cheat.getCreator())
+ binding.editNotesInput.setText(cheat.getNotes())
+ binding.editCodeInput.setText(cheat.getCode())
+ }
+ this.cheat = cheat
+ }
+
+ private fun onIsEditingUpdated(isEditing: Boolean) {
+ binding.editNameInput.isEnabled = isEditing
+ binding.editCreatorInput.isEnabled = isEditing
+ binding.editNotesInput.isEnabled = isEditing
+ binding.editCodeInput.isEnabled = isEditing
+
+ binding.buttonDelete.visibility = if (isEditing) View.GONE else View.VISIBLE
+ binding.buttonEdit.visibility = if (isEditing) View.GONE else View.VISIBLE
+ binding.buttonCancel.visibility = if (isEditing) View.VISIBLE else View.GONE
+ binding.buttonOk.visibility = if (isEditing) View.VISIBLE else View.GONE
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java
deleted file mode 100644
index b293cd84d1..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
-
-public class CheatItem
-{
- public static final int TYPE_CHEAT = 0;
- public static final int TYPE_HEADER = 1;
- public static final int TYPE_ACTION = 2;
-
- private final @Nullable Cheat mCheat;
- private final int mString;
- private final int mType;
-
- public CheatItem(@NonNull Cheat cheat)
- {
- mCheat = cheat;
- mString = 0;
- mType = TYPE_CHEAT;
- }
-
- public CheatItem(int type, int string)
- {
- mCheat = null;
- mString = string;
- mType = type;
- }
-
- @Nullable
- public Cheat getCheat()
- {
- return mCheat;
- }
-
- public int getString()
- {
- return mString;
- }
-
- public int getType()
- {
- return mType;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.kt
new file mode 100644
index 0000000000..55b13a5be5
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.kt
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import org.dolphinemu.dolphinemu.features.cheats.model.Cheat
+
+class CheatItem {
+ val cheat: Cheat?
+ val string: Int
+ val type: Int
+
+ constructor(cheat: Cheat) {
+ this.cheat = cheat
+ string = 0
+ type = TYPE_CHEAT
+ }
+
+ constructor(type: Int, string: Int) {
+ cheat = null
+ this.string = string
+ this.type = type
+ }
+
+ companion object {
+ const val TYPE_CHEAT = 0
+ const val TYPE_HEADER = 1
+ const val TYPE_ACTION = 2
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java
deleted file mode 100644
index 681f3ae785..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java
+++ /dev/null
@@ -1,18 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-public abstract class CheatItemViewHolder extends RecyclerView.ViewHolder
-{
- public CheatItemViewHolder(@NonNull View itemView)
- {
- super(itemView);
- }
-
- public abstract void bind(CheatsActivity activity, CheatItem item, int position);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.kt
new file mode 100644
index 0000000000..5795d3bc54
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.kt
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+
+abstract class CheatItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ abstract fun bind(activity: CheatsActivity, item: CheatItem, position: Int)
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java
deleted file mode 100644
index b7dc0c8e4e..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.graphics.Insets;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.WindowInsetsCompat;
-import androidx.fragment.app.Fragment;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.LinearLayoutManager;
-
-import com.google.android.material.color.MaterialColors;
-import com.google.android.material.divider.MaterialDividerItemDecoration;
-import com.google.android.material.elevation.ElevationOverlayProvider;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.FragmentCheatListBinding;
-import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
-
-public class CheatListFragment extends Fragment
-{
- private FragmentCheatListBinding mBinding;
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState)
- {
- mBinding = FragmentCheatListBinding.inflate(inflater, container, false);
- return mBinding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
- {
- CheatsActivity activity = (CheatsActivity) requireActivity();
- CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
-
- mBinding.cheatList.setAdapter(new CheatsAdapter(activity, viewModel));
- mBinding.cheatList.setLayoutManager(new LinearLayoutManager(activity));
-
- MaterialDividerItemDecoration divider =
- new MaterialDividerItemDecoration(requireActivity(), LinearLayoutManager.VERTICAL);
- divider.setLastItemDecorated(false);
- mBinding.cheatList.addItemDecoration(divider);
-
- @ColorInt int color =
- new ElevationOverlayProvider(mBinding.cheatsWarning.getContext()).compositeOverlay(
- MaterialColors.getColor(mBinding.cheatsWarning, R.attr.colorSurface),
- getResources().getDimensionPixelSize(R.dimen.elevated_app_bar));
- mBinding.cheatsWarning.setBackgroundColor(color);
- mBinding.gfxModsWarning.setBackgroundColor(color);
-
- setInsets();
- }
-
- @Override
- public void onDestroyView()
- {
- super.onDestroyView();
- mBinding = null;
- }
-
- private void setInsets()
- {
- ViewCompat.setOnApplyWindowInsetsListener(mBinding.cheatList, (v, windowInsets) ->
- {
- Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
- v.setPadding(0, 0, 0,
- insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_xtralarge));
- return windowInsets;
- });
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.kt
new file mode 100644
index 0000000000..093c8c3fbe
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.kt
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.ColorInt
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.google.android.material.color.MaterialColors
+import com.google.android.material.divider.MaterialDividerItemDecoration
+import com.google.android.material.elevation.ElevationOverlayProvider
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.databinding.FragmentCheatListBinding
+import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel
+
+class CheatListFragment : Fragment() {
+ private var _binding: FragmentCheatListBinding? = null
+ private val binding get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentCheatListBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val activity = requireActivity() as CheatsActivity
+ val viewModel = ViewModelProvider(activity)[CheatsViewModel::class.java]
+
+ binding.cheatList.adapter = CheatsAdapter(activity, viewModel)
+ binding.cheatList.layoutManager = LinearLayoutManager(activity)
+
+ val divider = MaterialDividerItemDecoration(requireActivity(), LinearLayoutManager.VERTICAL)
+ divider.isLastItemDecorated = false
+ binding.cheatList.addItemDecoration(divider)
+
+ @ColorInt val color =
+ ElevationOverlayProvider(binding.cheatsWarning.context).compositeOverlay(
+ MaterialColors.getColor(binding.cheatsWarning, R.attr.colorSurface),
+ resources.getDimensionPixelSize(R.dimen.elevated_app_bar).toFloat()
+ )
+ binding.cheatsWarning.setBackgroundColor(color)
+ binding.gfxModsWarning.setBackgroundColor(color)
+
+ setInsets()
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ private fun setInsets() {
+ ViewCompat.setOnApplyWindowInsetsListener(binding.cheatList) { v: View, windowInsets: WindowInsetsCompat ->
+ val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(
+ 0,
+ 0,
+ 0,
+ insets.bottom + resources.getDimensionPixelSize(R.dimen.spacing_xtralarge)
+ )
+ windowInsets
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java
deleted file mode 100644
index 0623aeabeb..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.view.View;
-import android.widget.CompoundButton;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.ViewModelProvider;
-
-import org.dolphinemu.dolphinemu.databinding.ListItemCheatBinding;
-import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
-import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
-
-public class CheatViewHolder extends CheatItemViewHolder
- implements View.OnClickListener, CompoundButton.OnCheckedChangeListener
-{
- private final ListItemCheatBinding mBinding;
-
- private CheatsViewModel mViewModel;
- private Cheat mCheat;
- private int mPosition;
-
- public CheatViewHolder(@NonNull ListItemCheatBinding binding)
- {
- super(binding.getRoot());
- mBinding = binding;
- }
-
- public void bind(CheatsActivity activity, CheatItem item, int position)
- {
- mBinding.cheatSwitch.setOnCheckedChangeListener(null);
-
- mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
- mCheat = item.getCheat();
- mPosition = position;
-
- mBinding.textName.setText(mCheat.getName());
- mBinding.cheatSwitch.setChecked(mCheat.getEnabled());
-
- mBinding.root.setOnClickListener(this);
- mBinding.cheatSwitch.setOnCheckedChangeListener(this);
- }
-
- public void onClick(View root)
- {
- mViewModel.setSelectedCheat(mCheat, mPosition);
- mViewModel.openDetailsView();
- }
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
- {
- mCheat.setEnabled(isChecked);
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.kt
new file mode 100644
index 0000000000..15b51e7299
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.kt
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.view.View
+import android.widget.CompoundButton
+import androidx.lifecycle.ViewModelProvider
+import org.dolphinemu.dolphinemu.databinding.ListItemCheatBinding
+import org.dolphinemu.dolphinemu.features.cheats.model.Cheat
+import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel
+
+class CheatViewHolder(private val binding: ListItemCheatBinding) :
+ CheatItemViewHolder(binding.getRoot()),
+ View.OnClickListener,
+ CompoundButton.OnCheckedChangeListener {
+ private lateinit var viewModel: CheatsViewModel
+ private lateinit var cheat: Cheat
+ private var position = 0
+
+ override fun bind(activity: CheatsActivity, item: CheatItem, position: Int) {
+ binding.cheatSwitch.setOnCheckedChangeListener(null)
+ viewModel = ViewModelProvider(activity)[CheatsViewModel::class.java]
+ cheat = item.cheat!!
+ this.position = position
+ binding.textName.text = cheat.getName()
+ binding.cheatSwitch.isChecked = cheat.getEnabled()
+ binding.root.setOnClickListener(this)
+ binding.cheatSwitch.setOnCheckedChangeListener(this)
+ }
+
+ override fun onClick(root: View) {
+ viewModel.setSelectedCheat(cheat, position)
+ viewModel.openDetailsView()
+ }
+
+ override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
+ cheat.setEnabled(isChecked)
+ }
+}
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<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;
- }
- });
- }
- }
-}
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<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)
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java
deleted file mode 100644
index 3335795c75..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java
+++ /dev/null
@@ -1,176 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.ListItemCheatBinding;
-import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding;
-import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
-import org.dolphinemu.dolphinemu.features.cheats.model.ARCheat;
-import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel;
-import org.dolphinemu.dolphinemu.features.cheats.model.GeckoCheat;
-import org.dolphinemu.dolphinemu.features.cheats.model.GraphicsMod;
-import org.dolphinemu.dolphinemu.features.cheats.model.PatchCheat;
-
-import java.util.ArrayList;
-
-public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
-{
- private final CheatsActivity mActivity;
- private final CheatsViewModel mViewModel;
-
- public CheatsAdapter(CheatsActivity activity, CheatsViewModel viewModel)
- {
- mActivity = activity;
- mViewModel = viewModel;
-
- mViewModel.getCheatAddedEvent().observe(activity, (position) ->
- {
- if (position != null)
- notifyItemInserted(position);
- });
-
- mViewModel.getCheatChangedEvent().observe(activity, (position) ->
- {
- if (position != null)
- notifyItemChanged(position);
- });
-
- mViewModel.getCheatDeletedEvent().observe(activity, (position) ->
- {
- if (position != null)
- notifyItemRemoved(position);
- });
-
- mViewModel.getGeckoCheatsDownloadedEvent().observe(activity, (cheatsAdded) ->
- {
- if (cheatsAdded != null)
- {
- int positionEnd = getItemCount() - 2; // Skip "Add Gecko Code" and "Download Gecko Codes"
- int positionStart = positionEnd - cheatsAdded;
- notifyItemRangeInserted(positionStart, cheatsAdded);
- }
- });
- }
-
- @NonNull
- @Override
- public CheatItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
- {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-
- switch (viewType)
- {
- case CheatItem.TYPE_CHEAT:
- ListItemCheatBinding listItemCheatBinding = ListItemCheatBinding.inflate(inflater);
- addViewListeners(listItemCheatBinding.getRoot());
- return new CheatViewHolder(listItemCheatBinding);
- case CheatItem.TYPE_HEADER:
- ListItemHeaderBinding listItemHeaderBinding = ListItemHeaderBinding.inflate(inflater);
- addViewListeners(listItemHeaderBinding.getRoot());
- return new HeaderViewHolder(listItemHeaderBinding);
- case CheatItem.TYPE_ACTION:
- ListItemSubmenuBinding listItemSubmenuBinding = ListItemSubmenuBinding.inflate(inflater);
- addViewListeners(listItemSubmenuBinding.getRoot());
- return new ActionViewHolder(listItemSubmenuBinding);
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public void onBindViewHolder(@NonNull CheatItemViewHolder holder, int position)
- {
- holder.bind(mActivity, getItemAt(position), position);
- }
-
- @Override
- public int getItemCount()
- {
- return mViewModel.getGraphicsMods().size() + mViewModel.getPatchCheats().size() +
- mViewModel.getARCheats().size() + mViewModel.getGeckoCheats().size() + 8;
- }
-
- @Override
- public int getItemViewType(int position)
- {
- return getItemAt(position).getType();
- }
-
- private void addViewListeners(View view)
- {
- CheatsActivity.setOnFocusChangeListenerRecursively(view,
- (v, hasFocus) -> mActivity.onListViewFocusChange(hasFocus));
- }
-
- private CheatItem getItemAt(int position)
- {
- // Graphics mods
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_graphics_mod);
- position -= 1;
-
- ArrayList<GraphicsMod> graphicsMods = mViewModel.getGraphicsMods();
- if (position < graphicsMods.size())
- return new CheatItem(graphicsMods.get(position));
- position -= graphicsMods.size();
-
- // Patches
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_patch);
- position -= 1;
-
- ArrayList<PatchCheat> patchCheats = mViewModel.getPatchCheats();
- if (position < patchCheats.size())
- return new CheatItem(patchCheats.get(position));
- position -= patchCheats.size();
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_patch);
- position -= 1;
-
- // AR codes
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_ar);
- position -= 1;
-
- ArrayList<ARCheat> arCheats = mViewModel.getARCheats();
- if (position < arCheats.size())
- return new CheatItem(arCheats.get(position));
- position -= arCheats.size();
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_ar);
- position -= 1;
-
- // Gecko codes
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_gecko);
- position -= 1;
-
- ArrayList<GeckoCheat> geckoCheats = mViewModel.getGeckoCheats();
- if (position < geckoCheats.size())
- return new CheatItem(geckoCheats.get(position));
- position -= geckoCheats.size();
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_gecko);
- position -= 1;
-
- if (position == 0)
- return new CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_download_gecko);
-
- throw new IndexOutOfBoundsException();
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.kt
new file mode 100644
index 0000000000..c75330375d
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.kt
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.databinding.ListItemCheatBinding
+import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding
+import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding
+import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel
+import org.dolphinemu.dolphinemu.features.cheats.ui.CheatsActivity.Companion.setOnFocusChangeListenerRecursively
+
+class CheatsAdapter(
+ private val activity: CheatsActivity,
+ private val viewModel: CheatsViewModel
+) : RecyclerView.Adapter<CheatItemViewHolder>() {
+ init {
+ viewModel.cheatAddedEvent.observe(activity) { position: Int? ->
+ position?.let { notifyItemInserted(it) }
+ }
+
+ viewModel.cheatChangedEvent.observe(activity) { position: Int? ->
+ position?.let { notifyItemChanged(it) }
+ }
+
+ viewModel.cheatDeletedEvent.observe(activity) { position: Int? ->
+ position?.let { notifyItemRemoved(it) }
+ }
+
+ viewModel.geckoCheatsDownloadedEvent.observe(activity) { cheatsAdded: Int? ->
+ cheatsAdded?.let {
+ val positionEnd = itemCount - 2 // Skip "Add Gecko Code" and "Download Gecko Codes"
+ val positionStart = positionEnd - it
+ notifyItemRangeInserted(positionStart, it)
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheatItemViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ return when (viewType) {
+ CheatItem.TYPE_CHEAT -> {
+ val listItemCheatBinding = ListItemCheatBinding.inflate(inflater)
+ addViewListeners(listItemCheatBinding.getRoot())
+ CheatViewHolder(listItemCheatBinding)
+ }
+ CheatItem.TYPE_HEADER -> {
+ val listItemHeaderBinding = ListItemHeaderBinding.inflate(inflater)
+ addViewListeners(listItemHeaderBinding.root)
+ HeaderViewHolder(listItemHeaderBinding)
+ }
+ CheatItem.TYPE_ACTION -> {
+ val listItemSubmenuBinding = ListItemSubmenuBinding.inflate(inflater)
+ addViewListeners(listItemSubmenuBinding.root)
+ ActionViewHolder(listItemSubmenuBinding)
+ }
+ else -> throw UnsupportedOperationException()
+ }
+ }
+
+ override fun onBindViewHolder(holder: CheatItemViewHolder, position: Int) {
+ holder.bind(activity, getItemAt(position), position)
+ }
+
+ override fun getItemCount(): Int {
+ return viewModel.graphicsMods.size + viewModel.patchCheats.size + viewModel.aRCheats.size +
+ viewModel.geckoCheats.size + 8
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return getItemAt(position).type
+ }
+
+ private fun addViewListeners(view: View) {
+ setOnFocusChangeListenerRecursively(view) { _: View?, hasFocus: Boolean ->
+ activity.onListViewFocusChange(
+ hasFocus
+ )
+ }
+ }
+
+ private fun getItemAt(position: Int): CheatItem {
+ // Graphics mods
+ var itemPosition = position
+ if (itemPosition == 0) return CheatItem(
+ CheatItem.TYPE_HEADER,
+ R.string.cheats_header_graphics_mod
+ )
+ itemPosition -= 1
+
+ val graphicsMods = viewModel.graphicsMods
+ if (itemPosition < graphicsMods.size) return CheatItem(graphicsMods[itemPosition])
+ itemPosition -= graphicsMods.size
+
+ // Patches
+ if (itemPosition == 0) return CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_patch)
+ itemPosition -= 1
+
+ val patchCheats = viewModel.patchCheats
+ if (itemPosition < patchCheats.size) return CheatItem(patchCheats[itemPosition])
+ itemPosition -= patchCheats.size
+
+ if (itemPosition == 0) return CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_patch)
+ itemPosition -= 1
+
+ // AR codes
+ if (itemPosition == 0) return CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_ar)
+ itemPosition -= 1
+
+ val arCheats = viewModel.aRCheats
+ if (itemPosition < arCheats.size) return CheatItem(arCheats[itemPosition])
+ itemPosition -= arCheats.size
+
+ if (itemPosition == 0) return CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_ar)
+ itemPosition -= 1
+
+ // Gecko codes
+ if (itemPosition == 0) return CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_gecko)
+ itemPosition -= 1
+
+ val geckoCheats = viewModel.geckoCheats
+ if (itemPosition < geckoCheats.size) return CheatItem(geckoCheats[itemPosition])
+ itemPosition -= geckoCheats.size
+
+ if (itemPosition == 0) return CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_gecko)
+ itemPosition -= 1
+
+ if (itemPosition == 0) return CheatItem(
+ CheatItem.TYPE_ACTION,
+ R.string.cheats_download_gecko
+ )
+
+ throw IndexOutOfBoundsException()
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.java
deleted file mode 100644
index f2dfd7b341..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
-import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
-
-public class CheatsDisabledWarningFragment extends SettingDisabledWarningFragment
-{
- public CheatsDisabledWarningFragment()
- {
- super(BooleanSetting.MAIN_ENABLE_CHEATS, MenuTag.CONFIG_GENERAL,
- R.string.cheats_disabled_warning);
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.kt
new file mode 100644
index 0000000000..4209835011
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsDisabledWarningFragment.kt
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
+import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
+
+class CheatsDisabledWarningFragment : SettingDisabledWarningFragment(
+ BooleanSetting.MAIN_ENABLE_CHEATS,
+ MenuTag.CONFIG_GENERAL,
+ R.string.cheats_disabled_warning
+)
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.java
deleted file mode 100644
index eaf26f094b..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.java
+++ /dev/null
@@ -1,16 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
-import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
-
-public class GraphicsModsDisabledWarningFragment extends SettingDisabledWarningFragment
-{
- public GraphicsModsDisabledWarningFragment()
- {
- super(BooleanSetting.GFX_MODS_ENABLE, MenuTag.ADVANCED_GRAPHICS,
- R.string.gfx_mods_disabled_warning);
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.kt
new file mode 100644
index 0000000000..7790ba300e
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/GraphicsModsDisabledWarningFragment.kt
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
+import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
+
+class GraphicsModsDisabledWarningFragment : SettingDisabledWarningFragment(
+ BooleanSetting.GFX_MODS_ENABLE,
+ MenuTag.ADVANCED_GRAPHICS,
+ R.string.gfx_mods_disabled_warning
+)
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java
deleted file mode 100644
index 8f347d363e..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.view.View;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding;
-
-public class HeaderViewHolder extends CheatItemViewHolder
-{
- private TextView mHeaderName;
-
- public HeaderViewHolder(@NonNull ListItemHeaderBinding binding)
- {
- super(binding.getRoot());
-
- mHeaderName = binding.textHeaderName;
- }
-
- public void bind(CheatsActivity activity, CheatItem item, int position)
- {
- mHeaderName.setText(item.getString());
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.kt
new file mode 100644
index 0000000000..b314ef58a6
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.kt
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.widget.TextView
+import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding
+
+class HeaderViewHolder(binding: ListItemHeaderBinding) : CheatItemViewHolder(binding.root) {
+ private val headerName: TextView
+
+ init {
+ headerName = binding.textHeaderName
+ }
+
+ override fun bind(activity: CheatsActivity, item: CheatItem, position: Int) {
+ headerName.setText(item.string)
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.java
deleted file mode 100644
index 7e703f3fd1..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.java
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.cheats.ui;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import org.dolphinemu.dolphinemu.databinding.FragmentCheatWarningBinding;
-import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.Settings;
-import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
-import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
-
-public abstract class SettingDisabledWarningFragment extends Fragment
- implements View.OnClickListener
-{
- private View mView;
-
- private final AbstractBooleanSetting mSetting;
- private final MenuTag mSettingShortcut;
- private final int mText;
-
- private FragmentCheatWarningBinding mBinding;
-
- public SettingDisabledWarningFragment(AbstractBooleanSetting setting, MenuTag settingShortcut,
- int text)
- {
- mSetting = setting;
- mSettingShortcut = settingShortcut;
- mText = text;
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState)
- {
- mBinding = FragmentCheatWarningBinding.inflate(inflater, container, false);
- return mBinding.getRoot();
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
- {
- mView = view;
-
- mBinding.textWarning.setText(mText);
- mBinding.buttonSettings.setOnClickListener(this);
-
- CheatsActivity activity = (CheatsActivity) requireActivity();
- CheatsActivity.setOnFocusChangeListenerRecursively(view,
- (v, hasFocus) -> activity.onListViewFocusChange(hasFocus));
- }
-
- @Override
- public void onResume()
- {
- super.onResume();
-
- CheatsActivity activity = (CheatsActivity) requireActivity();
- try (Settings settings = activity.loadGameSpecificSettings())
- {
- boolean cheatsEnabled = mSetting.getBoolean(settings);
- mView.setVisibility(cheatsEnabled ? View.GONE : View.VISIBLE);
- }
- }
-
- @Override
- public void onDestroyView()
- {
- super.onDestroyView();
- mBinding = null;
- }
-
- public void onClick(View view)
- {
- SettingsActivity.launch(requireContext(), mSettingShortcut);
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt
new file mode 100644
index 0000000000..f67b0f3862
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/SettingDisabledWarningFragment.kt
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.cheats.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import org.dolphinemu.dolphinemu.databinding.FragmentCheatWarningBinding
+import org.dolphinemu.dolphinemu.features.cheats.ui.CheatsActivity.Companion.setOnFocusChangeListenerRecursively
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting
+import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
+import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity
+
+abstract class SettingDisabledWarningFragment(
+ private val setting: AbstractBooleanSetting,
+ private val settingShortcut: MenuTag,
+ private val text: Int
+) : Fragment(), View.OnClickListener {
+ private var _binding: FragmentCheatWarningBinding? = null
+ private val binding get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ _binding = FragmentCheatWarningBinding.inflate(inflater, container, false)
+ return binding.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ binding.textWarning.setText(text)
+ binding.buttonSettings.setOnClickListener(this)
+ val activity = requireActivity() as CheatsActivity
+ setOnFocusChangeListenerRecursively(view) { _: View?, hasFocus: Boolean ->
+ activity.onListViewFocusChange(hasFocus)
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ val activity = requireActivity() as CheatsActivity
+ activity.loadGameSpecificSettings().use { settings ->
+ val cheatsEnabled = setting.getBoolean(settings)
+ requireView().visibility = if (cheatsEnabled) View.GONE else View.VISIBLE
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ override fun onClick(view: View) {
+ SettingsActivity.launch(requireContext(), settingShortcut)
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java
deleted file mode 100644
index 0ab829fa77..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.ui;
-
-import android.content.Context;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
-import androidx.activity.OnBackPressedCallback;
-import androidx.annotation.NonNull;
-import androidx.slidingpanelayout.widget.SlidingPaneLayout;
-
-public class TwoPaneOnBackPressedCallback extends OnBackPressedCallback
- implements SlidingPaneLayout.PanelSlideListener
-{
- private final SlidingPaneLayout mSlidingPaneLayout;
-
- public TwoPaneOnBackPressedCallback(@NonNull SlidingPaneLayout slidingPaneLayout)
- {
- super(slidingPaneLayout.isSlideable() && slidingPaneLayout.isOpen());
- mSlidingPaneLayout = slidingPaneLayout;
- slidingPaneLayout.addPanelSlideListener(this);
- }
-
- @Override
- public void handleOnBackPressed()
- {
- mSlidingPaneLayout.close();
- }
-
- @Override
- public void onPanelSlide(@NonNull View panel, float slideOffset)
- {
- }
-
- @Override
- public void onPanelOpened(@NonNull View panel)
- {
- setEnabled(true);
- }
-
- @Override
- public void onPanelClosed(@NonNull View panel)
- {
- closeKeyboard();
- setEnabled(false);
- }
-
- private void closeKeyboard()
- {
- InputMethodManager manager = (InputMethodManager) mSlidingPaneLayout.getContext()
- .getSystemService(Context.INPUT_METHOD_SERVICE);
- manager.hideSoftInputFromWindow(mSlidingPaneLayout.getRootView().getWindowToken(), 0);
- }
-}
-
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.kt
new file mode 100644
index 0000000000..e9fa0679d2
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.kt
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.ui
+
+import android.content.Context
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+import androidx.activity.OnBackPressedCallback
+import androidx.slidingpanelayout.widget.SlidingPaneLayout
+import androidx.slidingpanelayout.widget.SlidingPaneLayout.PanelSlideListener
+
+class TwoPaneOnBackPressedCallback(private val slidingPaneLayout: SlidingPaneLayout) :
+ OnBackPressedCallback(slidingPaneLayout.isSlideable && slidingPaneLayout.isOpen),
+ PanelSlideListener {
+ init {
+ slidingPaneLayout.addPanelSlideListener(this)
+ }
+
+ override fun handleOnBackPressed() {
+ slidingPaneLayout.close()
+ }
+
+ override fun onPanelSlide(panel: View, slideOffset: Float) {}
+ override fun onPanelOpened(panel: View) {
+ isEnabled = true
+ }
+
+ override fun onPanelClosed(panel: View) {
+ closeKeyboard()
+ isEnabled = false
+ }
+
+ private fun closeKeyboard() {
+ val manager = slidingPaneLayout.context
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ manager.hideSoftInputFromWindow(slidingPaneLayout.rootView.windowToken, 0)
+ }
+}