Android: Add checkboxes for toggling cheats enabled

This commit is contained in:
JosJuice 2021-07-18 18:54:01 +02:00
parent 67a8855d9a
commit 93a1271386
12 changed files with 253 additions and 11 deletions

View File

@ -5,7 +5,7 @@ package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
public class ARCheat implements Cheat
public class ARCheat extends AbstractCheat
{
@Keep
private final long mPointer;
@ -22,6 +22,13 @@ public class ARCheat implements Cheat
@NonNull
public native String getName();
public native boolean getEnabled();
@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);
}

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.Nullable;
public abstract class AbstractCheat implements Cheat
{
private Runnable mChangedCallback = null;
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);
}

View File

@ -8,4 +8,8 @@ public interface Cheat
{
@NonNull
String getName();
boolean getEnabled();
void setEnabled(boolean enabled);
}

View File

@ -12,6 +12,10 @@ public class CheatsViewModel extends ViewModel
private ARCheat[] mARCheats;
private GeckoCheat[] mGeckoCheats;
private boolean mPatchCheatsNeedSaving = false;
private boolean mARCheatsNeedSaving = false;
private boolean mGeckoCheatsNeedSaving = false;
public void load(String gameID, int revision)
{
if (mLoaded)
@ -21,9 +25,43 @@ public class CheatsViewModel extends ViewModel
mARCheats = ARCheat.loadCodes(gameID, revision);
mGeckoCheats = GeckoCheat.loadCodes(gameID, revision);
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 (mPatchCheatsNeedSaving)
{
PatchCheat.saveCodes(gameID, revision, mPatchCheats);
mPatchCheatsNeedSaving = false;
}
if (mARCheatsNeedSaving)
{
ARCheat.saveCodes(gameID, revision, mARCheats);
mARCheatsNeedSaving = false;
}
if (mGeckoCheatsNeedSaving)
{
GeckoCheat.saveCodes(gameID, revision, mGeckoCheats);
mGeckoCheatsNeedSaving = false;
}
}
public Cheat[] getPatchCheats()
{
return mPatchCheats;

View File

@ -5,7 +5,7 @@ package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
public class GeckoCheat implements Cheat
public class GeckoCheat extends AbstractCheat
{
@Keep
private final long mPointer;
@ -22,6 +22,13 @@ public class GeckoCheat implements Cheat
@NonNull
public native String getName();
public native boolean getEnabled();
@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);
}

View File

@ -5,7 +5,7 @@ package org.dolphinemu.dolphinemu.features.cheats.model;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
public class PatchCheat implements Cheat
public class PatchCheat extends AbstractCheat
{
@Keep
private final long mPointer;
@ -22,6 +22,13 @@ public class PatchCheat implements Cheat
@NonNull
public native String getName();
public native boolean getEnabled();
@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);
}

View File

@ -3,6 +3,8 @@
package org.dolphinemu.dolphinemu.features.cheats.ui;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import androidx.annotation.NonNull;
@ -11,19 +13,35 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
public class CheatViewHolder extends ViewHolder
public class CheatViewHolder extends ViewHolder implements CompoundButton.OnCheckedChangeListener
{
private TextView mName;
private final TextView mName;
private final CheckBox mCheckbox;
private Cheat mCheat;
public CheatViewHolder(@NonNull View itemView)
{
super(itemView);
mName = itemView.findViewById(R.id.text_name);
mCheckbox = itemView.findViewById(R.id.checkbox);
}
public void bind(Cheat item)
{
mCheckbox.setOnCheckedChangeListener(null);
mName.setText(item.getName());
mCheckbox.setChecked(item.getEnabled());
mCheat = item;
mCheckbox.setOnCheckedChangeListener(this);
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
{
mCheat.setEnabled(isChecked);
}
}

View File

@ -18,6 +18,10 @@ public class CheatsActivity extends AppCompatActivity
private static final String ARG_GAME_ID = "game_id";
private static final String ARG_REVISION = "revision";
private String mGameId;
private int mRevision;
private CheatsViewModel mViewModel;
public static void launch(Context context, String gameId, int revision)
{
Intent intent = new Intent(context, CheatsActivity.class);
@ -34,14 +38,22 @@ public class CheatsActivity extends AppCompatActivity
MainPresenter.skipRescanningLibrary();
Intent intent = getIntent();
String gameId = intent.getStringExtra(ARG_GAME_ID);
int revision = intent.getIntExtra(ARG_REVISION, 0);
mGameId = intent.getStringExtra(ARG_GAME_ID);
mRevision = intent.getIntExtra(ARG_REVISION, 0);
setTitle(getString(R.string.cheats_with_game_id, gameId));
setTitle(getString(R.string.cheats_with_game_id, mGameId));
CheatsViewModel viewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
viewModel.load(gameId, revision);
mViewModel = new ViewModelProvider(this).get(CheatsViewModel.class);
mViewModel.load(mGameId, mRevision);
setContentView(R.layout.activity_cheats);
}
@Override
protected void onStop()
{
super.onStop();
mViewModel.saveIfNeeded(mGameId, mRevision);
}
}

View File

@ -16,8 +16,19 @@
tools:text="Hyrule Field Speed Hack"
android:layout_margin="@dimen/spacing_large"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@id/checkbox"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<CheckBox
android:id="@+id/checkbox"
android:layout_width="48dp"
android:layout_height="64dp"
app:layout_constraintStart_toEndOf="@id/text_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center"
android:focusable="true" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -39,6 +39,18 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_getName(JNIEnv* env
return ToJString(env, GetPointer(env, obj)->name);
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_getEnabled(JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_setEnabledImpl(
JNIEnv* env, jobject obj, jboolean enabled)
{
GetPointer(env, obj)->enabled = static_cast<bool>(enabled);
}
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_loadCodes(JNIEnv* env, jclass,
jstring jGameID,
@ -64,4 +76,27 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_loadCodes(JNIEnv* e
return array;
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_saveCodes(
JNIEnv* env, jclass, jstring jGameID, jint revision, jobjectArray jCodes)
{
const jsize size = env->GetArrayLength(jCodes);
std::vector<ActionReplay::ARCode> vector;
vector.reserve(size);
for (jsize i = 0; i < size; ++i)
{
jobject code = reinterpret_cast<jstring>(env->GetObjectArrayElement(jCodes, i));
vector.emplace_back(*GetPointer(env, code));
env->DeleteLocalRef(code);
}
const std::string game_id = GetJString(env, jGameID);
const std::string ini_path = File::GetUserPath(D_GAMESETTINGS_IDX) + game_id + ".ini";
IniFile game_ini_local;
game_ini_local.Load(ini_path);
ActionReplay::SaveCodes(&game_ini_local, vector);
game_ini_local.Save(ini_path);
}
}

View File

@ -40,6 +40,20 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_getName(JNIEnv*
return ToJString(env, GetPointer(env, obj)->name);
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_getEnabled(JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_setEnabledImpl(JNIEnv* env,
jobject obj,
jboolean enabled)
{
GetPointer(env, obj)->enabled = static_cast<bool>(enabled);
}
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_loadCodes(JNIEnv* env, jclass,
jstring jGameID,
@ -64,4 +78,27 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_loadCodes(JNIEnv
return array;
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_saveCodes(
JNIEnv* env, jclass, jstring jGameID, jint revision, jobjectArray jCodes)
{
const jsize size = env->GetArrayLength(jCodes);
std::vector<Gecko::GeckoCode> vector;
vector.reserve(size);
for (jsize i = 0; i < size; ++i)
{
jobject code = reinterpret_cast<jstring>(env->GetObjectArrayElement(jCodes, i));
vector.emplace_back(*GetPointer(env, code));
env->DeleteLocalRef(code);
}
const std::string game_id = GetJString(env, jGameID);
const std::string ini_path = File::GetUserPath(D_GAMESETTINGS_IDX) + game_id + ".ini";
IniFile game_ini_local;
game_ini_local.Load(ini_path);
Gecko::SaveCodes(game_ini_local, vector);
game_ini_local.Save(ini_path);
}
}

View File

@ -39,6 +39,20 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_getName(JNIEnv*
return ToJString(env, GetPointer(env, obj)->name);
}
JNIEXPORT jboolean JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_getEnabled(JNIEnv* env, jobject obj)
{
return static_cast<jboolean>(GetPointer(env, obj)->enabled);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_setEnabledImpl(JNIEnv* env,
jobject obj,
jboolean enabled)
{
GetPointer(env, obj)->enabled = static_cast<bool>(enabled);
}
JNIEXPORT jobjectArray JNICALL
Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_loadCodes(JNIEnv* env, jclass,
jstring jGameID,
@ -64,4 +78,27 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_loadCodes(JNIEnv
return array;
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_saveCodes(
JNIEnv* env, jclass, jstring jGameID, jint revision, jobjectArray jCodes)
{
const jsize size = env->GetArrayLength(jCodes);
std::vector<PatchEngine::Patch> vector;
vector.reserve(size);
for (jsize i = 0; i < size; ++i)
{
jobject code = reinterpret_cast<jstring>(env->GetObjectArrayElement(jCodes, i));
vector.emplace_back(*GetPointer(env, code));
env->DeleteLocalRef(code);
}
const std::string game_id = GetJString(env, jGameID);
const std::string ini_path = File::GetUserPath(D_GAMESETTINGS_IDX) + game_id + ".ini";
IniFile game_ini_local;
game_ini_local.Load(ini_path);
PatchEngine::SavePatchSection(&game_ini_local, vector);
game_ini_local.Save(ini_path);
}
}