Merge pull request #11614 from t895/kotlin-cheats
Android: Convert Cheats Activity to Kotlin
This commit is contained in:
commit
78e8669bac
|
@ -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);
|
|
||||||
}
|
|
|
@ -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>)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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>?
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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>)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
)
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -500,7 +500,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
const jclass ar_cheat_class =
|
const jclass ar_cheat_class =
|
||||||
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/ARCheat");
|
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/ARCheat");
|
||||||
s_ar_cheat_class = reinterpret_cast<jclass>(env->NewGlobalRef(ar_cheat_class));
|
s_ar_cheat_class = reinterpret_cast<jclass>(env->NewGlobalRef(ar_cheat_class));
|
||||||
s_ar_cheat_pointer = env->GetFieldID(ar_cheat_class, "mPointer", "J");
|
s_ar_cheat_pointer = env->GetFieldID(ar_cheat_class, "pointer", "J");
|
||||||
s_ar_cheat_constructor = env->GetMethodID(ar_cheat_class, "<init>", "(J)V");
|
s_ar_cheat_constructor = env->GetMethodID(ar_cheat_class, "<init>", "(J)V");
|
||||||
env->DeleteLocalRef(ar_cheat_class);
|
env->DeleteLocalRef(ar_cheat_class);
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
const jclass patch_cheat_class =
|
const jclass patch_cheat_class =
|
||||||
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat");
|
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat");
|
||||||
s_patch_cheat_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_cheat_class));
|
s_patch_cheat_class = reinterpret_cast<jclass>(env->NewGlobalRef(patch_cheat_class));
|
||||||
s_patch_cheat_pointer = env->GetFieldID(patch_cheat_class, "mPointer", "J");
|
s_patch_cheat_pointer = env->GetFieldID(patch_cheat_class, "pointer", "J");
|
||||||
s_patch_cheat_constructor = env->GetMethodID(patch_cheat_class, "<init>", "(J)V");
|
s_patch_cheat_constructor = env->GetMethodID(patch_cheat_class, "<init>", "(J)V");
|
||||||
env->DeleteLocalRef(patch_cheat_class);
|
env->DeleteLocalRef(patch_cheat_class);
|
||||||
|
|
||||||
|
@ -522,14 +522,14 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup");
|
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup");
|
||||||
s_graphics_mod_group_class =
|
s_graphics_mod_group_class =
|
||||||
reinterpret_cast<jclass>(env->NewGlobalRef(graphics_mod_group_class));
|
reinterpret_cast<jclass>(env->NewGlobalRef(graphics_mod_group_class));
|
||||||
s_graphics_mod_group_pointer = env->GetFieldID(graphics_mod_group_class, "mPointer", "J");
|
s_graphics_mod_group_pointer = env->GetFieldID(graphics_mod_group_class, "pointer", "J");
|
||||||
s_graphics_mod_group_constructor = env->GetMethodID(graphics_mod_group_class, "<init>", "(J)V");
|
s_graphics_mod_group_constructor = env->GetMethodID(graphics_mod_group_class, "<init>", "(J)V");
|
||||||
env->DeleteLocalRef(graphics_mod_group_class);
|
env->DeleteLocalRef(graphics_mod_group_class);
|
||||||
|
|
||||||
const jclass graphics_mod_class =
|
const jclass graphics_mod_class =
|
||||||
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod");
|
env->FindClass("org/dolphinemu/dolphinemu/features/cheats/model/GraphicsMod");
|
||||||
s_graphics_mod_class = reinterpret_cast<jclass>(env->NewGlobalRef(graphics_mod_class));
|
s_graphics_mod_class = reinterpret_cast<jclass>(env->NewGlobalRef(graphics_mod_class));
|
||||||
s_graphics_mod_pointer = env->GetFieldID(graphics_mod_class, "mPointer", "J");
|
s_graphics_mod_pointer = env->GetFieldID(graphics_mod_class, "pointer", "J");
|
||||||
s_graphics_mod_constructor =
|
s_graphics_mod_constructor =
|
||||||
env->GetMethodID(graphics_mod_class, "<init>",
|
env->GetMethodID(graphics_mod_class, "<init>",
|
||||||
"(JLorg/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup;)V");
|
"(JLorg/dolphinemu/dolphinemu/features/cheats/model/GraphicsModGroup;)V");
|
||||||
|
|
|
@ -81,7 +81,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_getEnabled(JNIEnv*
|
||||||
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
|
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_trySetImpl(
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_setCheatImpl(
|
||||||
JNIEnv* env, jobject obj, jstring name, jstring creator, jstring notes, jstring code_string)
|
JNIEnv* env, jobject obj, jstring name, jstring creator, jstring notes, jstring code_string)
|
||||||
{
|
{
|
||||||
ActionReplay::ARCode* code = GetPointer(env, obj);
|
ActionReplay::ARCode* code = GetPointer(env, obj);
|
||||||
|
|
|
@ -99,7 +99,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_equalsImpl(JNIEn
|
||||||
return *GetPointer(env, obj) == *GetPointer(env, other);
|
return *GetPointer(env, obj) == *GetPointer(env, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_trySetImpl(
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_setCheatImpl(
|
||||||
JNIEnv* env, jobject obj, jstring name, jstring creator, jstring notes, jstring code_string)
|
JNIEnv* env, jobject obj, jstring name, jstring creator, jstring notes, jstring code_string)
|
||||||
{
|
{
|
||||||
Gecko::GeckoCode* code = GetPointer(env, obj);
|
Gecko::GeckoCode* code = GetPointer(env, obj);
|
||||||
|
|
|
@ -79,7 +79,7 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_getEnabled(JNIEn
|
||||||
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
|
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_trySetImpl(
|
JNIEXPORT jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_setCheatImpl(
|
||||||
JNIEnv* env, jobject obj, jstring name, jstring creator, jstring notes, jstring code_string)
|
JNIEnv* env, jobject obj, jstring name, jstring creator, jstring notes, jstring code_string)
|
||||||
{
|
{
|
||||||
PatchEngine::Patch* patch = GetPointer(env, obj);
|
PatchEngine::Patch* patch = GetPointer(env, obj);
|
||||||
|
|
Loading…
Reference in New Issue