mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Android: Add edit button for cheats
This commit is contained in:
parent
a303b4bc98
commit
43dcbf33ad
@ -22,8 +22,13 @@ public class ARCheat extends AbstractCheat
|
||||
@NonNull
|
||||
public native String getName();
|
||||
|
||||
public native boolean getUserDefined();
|
||||
|
||||
public native boolean getEnabled();
|
||||
|
||||
@Override
|
||||
protected native int trySetImpl(@NonNull String name);
|
||||
|
||||
@Override
|
||||
protected native void setEnabledImpl(boolean enabled);
|
||||
|
||||
|
@ -2,12 +2,26 @@
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.cheats.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public abstract class AbstractCheat implements Cheat
|
||||
{
|
||||
private Runnable mChangedCallback = null;
|
||||
|
||||
public int trySet(@NonNull String name)
|
||||
{
|
||||
if (name.isEmpty())
|
||||
return TRY_SET_FAIL_NO_NAME;
|
||||
|
||||
int result = trySetImpl(name);
|
||||
|
||||
if (result == TRY_SET_SUCCESS)
|
||||
onChanged();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled)
|
||||
{
|
||||
setEnabledImpl(enabled);
|
||||
@ -25,5 +39,7 @@ public abstract class AbstractCheat implements Cheat
|
||||
mChangedCallback.run();
|
||||
}
|
||||
|
||||
protected abstract int trySetImpl(@NonNull String name);
|
||||
|
||||
protected abstract void setEnabledImpl(boolean enabled);
|
||||
}
|
||||
|
@ -3,13 +3,23 @@
|
||||
package org.dolphinemu.dolphinemu.features.cheats.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public interface Cheat
|
||||
{
|
||||
int TRY_SET_FAIL_NO_NAME = -1;
|
||||
int TRY_SET_SUCCESS = 0;
|
||||
|
||||
@NonNull
|
||||
String getName();
|
||||
|
||||
int trySet(@NonNull String name);
|
||||
|
||||
boolean getUserDefined();
|
||||
|
||||
boolean getEnabled();
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
||||
void setChangedCallback(@Nullable Runnable callback);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ public class CheatsViewModel extends ViewModel
|
||||
private boolean mLoaded = false;
|
||||
|
||||
private final MutableLiveData<Cheat> mSelectedCheat = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<Boolean> mIsEditing = new MutableLiveData<>(false);
|
||||
|
||||
private final MutableLiveData<Boolean> mOpenDetailsViewEvent = new MutableLiveData<>(false);
|
||||
|
||||
@ -75,9 +76,22 @@ public class CheatsViewModel extends ViewModel
|
||||
|
||||
public void setSelectedCheat(Cheat cheat)
|
||||
{
|
||||
if (mIsEditing.getValue())
|
||||
setIsEditing(false);
|
||||
|
||||
mSelectedCheat.setValue(cheat);
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getIsEditing()
|
||||
{
|
||||
return mIsEditing;
|
||||
}
|
||||
|
||||
public void setIsEditing(boolean isEditing)
|
||||
{
|
||||
mIsEditing.setValue(isEditing);
|
||||
}
|
||||
|
||||
public LiveData<Boolean> getOpenDetailsViewEvent()
|
||||
{
|
||||
return mOpenDetailsViewEvent;
|
||||
|
@ -22,8 +22,13 @@ public class GeckoCheat extends AbstractCheat
|
||||
@NonNull
|
||||
public native String getName();
|
||||
|
||||
public native boolean getUserDefined();
|
||||
|
||||
public native boolean getEnabled();
|
||||
|
||||
@Override
|
||||
protected native int trySetImpl(@NonNull String name);
|
||||
|
||||
@Override
|
||||
protected native void setEnabledImpl(boolean enabled);
|
||||
|
||||
|
@ -22,8 +22,13 @@ public class PatchCheat extends AbstractCheat
|
||||
@NonNull
|
||||
public native String getName();
|
||||
|
||||
public native boolean getUserDefined();
|
||||
|
||||
public native boolean getEnabled();
|
||||
|
||||
@Override
|
||||
protected native int trySetImpl(@NonNull String name);
|
||||
|
||||
@Override
|
||||
protected native void setEnabledImpl(boolean enabled);
|
||||
|
||||
|
@ -6,6 +6,7 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@ -22,6 +23,9 @@ public class CheatDetailsFragment extends Fragment
|
||||
{
|
||||
private View mRoot;
|
||||
private EditText mEditName;
|
||||
private Button mButtonEdit;
|
||||
private Button mButtonCancel;
|
||||
private Button mButtonOk;
|
||||
|
||||
private CheatsViewModel mViewModel;
|
||||
private Cheat mCheat;
|
||||
@ -39,24 +43,74 @@ public class CheatDetailsFragment extends Fragment
|
||||
{
|
||||
mRoot = view.findViewById(R.id.root);
|
||||
mEditName = view.findViewById(R.id.edit_name);
|
||||
mButtonEdit = view.findViewById(R.id.button_edit);
|
||||
mButtonCancel = view.findViewById(R.id.button_cancel);
|
||||
mButtonOk = view.findViewById(R.id.button_ok);
|
||||
|
||||
CheatsActivity activity = (CheatsActivity) requireActivity();
|
||||
mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class);
|
||||
|
||||
LiveData<Cheat> selectedCheat = mViewModel.getSelectedCheat();
|
||||
selectedCheat.observe(getViewLifecycleOwner(), this::populateFields);
|
||||
populateFields(selectedCheat.getValue());
|
||||
mViewModel.getSelectedCheat().observe(getViewLifecycleOwner(), this::onSelectedCheatUpdated);
|
||||
mViewModel.getIsEditing().observe(getViewLifecycleOwner(), this::onIsEditingUpdated);
|
||||
|
||||
mButtonEdit.setOnClickListener((v) -> mViewModel.setIsEditing(true));
|
||||
mButtonCancel.setOnClickListener((v) ->
|
||||
{
|
||||
mViewModel.setIsEditing(false);
|
||||
onSelectedCheatUpdated(mCheat);
|
||||
});
|
||||
mButtonOk.setOnClickListener(this::onOkClicked);
|
||||
}
|
||||
|
||||
private void populateFields(@Nullable Cheat cheat)
|
||||
private void clearEditErrors()
|
||||
{
|
||||
mEditName.setError(null);
|
||||
}
|
||||
|
||||
private void onOkClicked(View view)
|
||||
{
|
||||
clearEditErrors();
|
||||
|
||||
int result = mCheat.trySet(mEditName.getText().toString());
|
||||
|
||||
switch (result)
|
||||
{
|
||||
case Cheat.TRY_SET_SUCCESS:
|
||||
mViewModel.setIsEditing(false);
|
||||
break;
|
||||
case Cheat.TRY_SET_FAIL_NO_NAME:
|
||||
mEditName.setError(getText(R.string.cheats_error_no_name));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onSelectedCheatUpdated(@Nullable Cheat cheat)
|
||||
{
|
||||
clearEditErrors();
|
||||
|
||||
mRoot.setVisibility(cheat == null ? View.GONE : View.VISIBLE);
|
||||
|
||||
if (cheat != null && cheat != mCheat)
|
||||
boolean userDefined = cheat != null && cheat.getUserDefined();
|
||||
mButtonEdit.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)
|
||||
{
|
||||
mEditName.setText(cheat.getName());
|
||||
}
|
||||
|
||||
mCheat = cheat;
|
||||
}
|
||||
|
||||
private void onIsEditingUpdated(boolean isEditing)
|
||||
{
|
||||
mEditName.setEnabled(isEditing);
|
||||
|
||||
mButtonEdit.setVisibility(isEditing ? View.GONE : View.VISIBLE);
|
||||
mButtonCancel.setVisibility(isEditing ? View.VISIBLE : View.GONE);
|
||||
mButtonOk.setVisibility(isEditing ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,19 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:id="@+id/scroll_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/barrier">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@ -29,11 +42,54 @@
|
||||
android:layout_marginHorizontal="@dimen/spacing_large"
|
||||
android:importantForAutofill="no"
|
||||
android:inputType="text"
|
||||
android:enabled="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/label_name"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:text="Hyrule Field Speed Hack" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/barrier"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="top"
|
||||
app:constraint_referenced_ids="button_edit,button_cancel,button_ok" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_edit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/spacing_large"
|
||||
android:text="@string/cheats_edit"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/spacing_large"
|
||||
android:text="@string/cancel"
|
||||
app:layout_constraintStart_toEndOf="@id/button_edit"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_ok"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_ok"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/spacing_large"
|
||||
android:text="@string/ok"
|
||||
app:layout_constraintStart_toEndOf="@id/button_cancel"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/barrier"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -390,6 +390,8 @@
|
||||
<string name="cheats">Cheats</string>
|
||||
<string name="cheats_with_game_id">Cheats: %1$s</string>
|
||||
<string name="cheats_name">Name</string>
|
||||
<string name="cheats_edit">Edit</string>
|
||||
<string name="cheats_error_no_name">Name can\'t be empty</string>
|
||||
|
||||
<!-- Convert Screen -->
|
||||
<string name="convert_format">Format</string>
|
||||
|
@ -1,5 +1,6 @@
|
||||
add_library(main SHARED
|
||||
Cheats/ARCheat.cpp
|
||||
Cheats/Cheats.h
|
||||
Cheats/GeckoCheat.cpp
|
||||
Cheats/PatchCheat.cpp
|
||||
Config/NativeConfig.cpp
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Core/ConfigManager.h"
|
||||
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||
#include "jni/AndroidCommon/IDCache.h"
|
||||
#include "jni/Cheats/Cheats.h"
|
||||
|
||||
static ActionReplay::ARCode* GetPointer(JNIEnv* env, jobject obj)
|
||||
{
|
||||
@ -39,12 +40,28 @@ 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_getUserDefined(JNIEnv* env,
|
||||
jobject obj)
|
||||
{
|
||||
return static_cast<jboolean>(GetPointer(env, obj)->user_defined);
|
||||
}
|
||||
|
||||
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 jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_trySetImpl(
|
||||
JNIEnv* env, jobject obj, jstring name)
|
||||
{
|
||||
ActionReplay::ARCode* code = GetPointer(env, obj);
|
||||
code->name = GetJString(env, name);
|
||||
|
||||
return TRY_SET_SUCCESS;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_setEnabledImpl(
|
||||
JNIEnv* env, jobject obj, jboolean enabled)
|
||||
{
|
||||
|
7
Source/Android/jni/Cheats/Cheats.h
Normal file
7
Source/Android/jni/Cheats/Cheats.h
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2021 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
constexpr int TRY_SET_FAIL_NO_NAME = -1;
|
||||
constexpr int TRY_SET_SUCCESS = 0;
|
@ -13,6 +13,7 @@
|
||||
#include "Core/GeckoCodeConfig.h"
|
||||
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||
#include "jni/AndroidCommon/IDCache.h"
|
||||
#include "jni/Cheats/Cheats.h"
|
||||
|
||||
static Gecko::GeckoCode* GetPointer(JNIEnv* env, jobject obj)
|
||||
{
|
||||
@ -40,12 +41,28 @@ 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_getUserDefined(JNIEnv* env,
|
||||
jobject obj)
|
||||
{
|
||||
return static_cast<jboolean>(GetPointer(env, obj)->user_defined);
|
||||
}
|
||||
|
||||
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 jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_trySetImpl(
|
||||
JNIEnv* env, jobject obj, jstring name)
|
||||
{
|
||||
Gecko::GeckoCode* code = GetPointer(env, obj);
|
||||
code->name = GetJString(env, name);
|
||||
|
||||
return TRY_SET_SUCCESS;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_setEnabledImpl(JNIEnv* env,
|
||||
jobject obj,
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Core/PatchEngine.h"
|
||||
#include "jni/AndroidCommon/AndroidCommon.h"
|
||||
#include "jni/AndroidCommon/IDCache.h"
|
||||
#include "jni/Cheats/Cheats.h"
|
||||
|
||||
static PatchEngine::Patch* GetPointer(JNIEnv* env, jobject obj)
|
||||
{
|
||||
@ -39,12 +40,28 @@ 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_getUserDefined(JNIEnv* env,
|
||||
jobject obj)
|
||||
{
|
||||
return static_cast<jboolean>(GetPointer(env, obj)->user_defined);
|
||||
}
|
||||
|
||||
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 jint JNICALL Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_trySetImpl(
|
||||
JNIEnv* env, jobject obj, jstring name)
|
||||
{
|
||||
PatchEngine::Patch* patch = GetPointer(env, obj);
|
||||
patch->name = GetJString(env, name);
|
||||
|
||||
return TRY_SET_SUCCESS;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_setEnabledImpl(JNIEnv* env,
|
||||
jobject obj,
|
||||
|
Loading…
Reference in New Issue
Block a user