Android: Add the ability to add cheats
This commit is contained in:
parent
6934b9a21d
commit
404eb13e2f
|
@ -10,6 +10,11 @@ public class ARCheat extends AbstractCheat
|
|||
@Keep
|
||||
private final long mPointer;
|
||||
|
||||
public ARCheat()
|
||||
{
|
||||
mPointer = createNew();
|
||||
}
|
||||
|
||||
@Keep
|
||||
private ARCheat(long pointer)
|
||||
{
|
||||
|
@ -19,6 +24,8 @@ public class ARCheat extends AbstractCheat
|
|||
@Override
|
||||
public native void finalize();
|
||||
|
||||
private native long createNew();
|
||||
|
||||
public boolean supportsCreator()
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -6,20 +6,25 @@ 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<Boolean> mOpenDetailsViewEvent = new MutableLiveData<>(false);
|
||||
|
||||
private PatchCheat[] mPatchCheats;
|
||||
private ARCheat[] mARCheats;
|
||||
private GeckoCheat[] mGeckoCheats;
|
||||
private ArrayList<PatchCheat> mPatchCheats;
|
||||
private ArrayList<ARCheat> mARCheats;
|
||||
private ArrayList<GeckoCheat> mGeckoCheats;
|
||||
|
||||
private boolean mPatchCheatsNeedSaving = false;
|
||||
private boolean mARCheatsNeedSaving = false;
|
||||
|
@ -30,9 +35,12 @@ public class CheatsViewModel extends ViewModel
|
|||
if (mLoaded)
|
||||
return;
|
||||
|
||||
mPatchCheats = PatchCheat.loadCodes(gameID, revision);
|
||||
mARCheats = ARCheat.loadCodes(gameID, revision);
|
||||
mGeckoCheats = GeckoCheat.loadCodes(gameID, revision);
|
||||
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 (PatchCheat cheat : mPatchCheats)
|
||||
{
|
||||
|
@ -54,19 +62,19 @@ public class CheatsViewModel extends ViewModel
|
|||
{
|
||||
if (mPatchCheatsNeedSaving)
|
||||
{
|
||||
PatchCheat.saveCodes(gameID, revision, mPatchCheats);
|
||||
PatchCheat.saveCodes(gameID, revision, mPatchCheats.toArray(new PatchCheat[0]));
|
||||
mPatchCheatsNeedSaving = false;
|
||||
}
|
||||
|
||||
if (mARCheatsNeedSaving)
|
||||
{
|
||||
ARCheat.saveCodes(gameID, revision, mARCheats);
|
||||
ARCheat.saveCodes(gameID, revision, mARCheats.toArray(new ARCheat[0]));
|
||||
mARCheatsNeedSaving = false;
|
||||
}
|
||||
|
||||
if (mGeckoCheatsNeedSaving)
|
||||
{
|
||||
GeckoCheat.saveCodes(gameID, revision, mGeckoCheats);
|
||||
GeckoCheat.saveCodes(gameID, revision, mGeckoCheats.toArray(new GeckoCheat[0]));
|
||||
mGeckoCheatsNeedSaving = false;
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +93,56 @@ public class CheatsViewModel extends ViewModel
|
|||
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;
|
||||
|
@ -93,6 +151,27 @@ public class CheatsViewModel extends ViewModel
|
|||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,17 +211,17 @@ public class CheatsViewModel extends ViewModel
|
|||
mOpenDetailsViewEvent.setValue(false);
|
||||
}
|
||||
|
||||
public Cheat[] getPatchCheats()
|
||||
public ArrayList<PatchCheat> getPatchCheats()
|
||||
{
|
||||
return mPatchCheats;
|
||||
}
|
||||
|
||||
public ARCheat[] getARCheats()
|
||||
public ArrayList<ARCheat> getARCheats()
|
||||
{
|
||||
return mARCheats;
|
||||
}
|
||||
|
||||
public Cheat[] getGeckoCheats()
|
||||
public ArrayList<GeckoCheat> getGeckoCheats()
|
||||
{
|
||||
return mGeckoCheats;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,11 @@ public class GeckoCheat extends AbstractCheat
|
|||
@Keep
|
||||
private final long mPointer;
|
||||
|
||||
public GeckoCheat()
|
||||
{
|
||||
mPointer = createNew();
|
||||
}
|
||||
|
||||
@Keep
|
||||
private GeckoCheat(long pointer)
|
||||
{
|
||||
|
@ -19,6 +24,8 @@ public class GeckoCheat extends AbstractCheat
|
|||
@Override
|
||||
public native void finalize();
|
||||
|
||||
private native long createNew();
|
||||
|
||||
public boolean supportsCreator()
|
||||
{
|
||||
return true;
|
||||
|
|
|
@ -10,6 +10,11 @@ public class PatchCheat extends AbstractCheat
|
|||
@Keep
|
||||
private final long mPointer;
|
||||
|
||||
public PatchCheat()
|
||||
{
|
||||
mPointer = createNew();
|
||||
}
|
||||
|
||||
@Keep
|
||||
private PatchCheat(long pointer)
|
||||
{
|
||||
|
@ -19,6 +24,8 @@ public class PatchCheat extends AbstractCheat
|
|||
@Override
|
||||
public native void finalize();
|
||||
|
||||
private native long createNew();
|
||||
|
||||
public boolean supportsCreator()
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// 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.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 CheatsViewModel mViewModel;
|
||||
private int mString;
|
||||
private int mPosition;
|
||||
|
||||
public ActionViewHolder(@NonNull View itemView)
|
||||
{
|
||||
super(itemView);
|
||||
|
||||
mName = itemView.findViewById(R.id.text_setting_name);
|
||||
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public void bind(CheatsViewModel viewModel, CheatItem item, int position)
|
||||
{
|
||||
mViewModel = viewModel;
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -94,8 +94,16 @@ public class CheatDetailsFragment extends Fragment
|
|||
switch (result)
|
||||
{
|
||||
case Cheat.TRY_SET_SUCCESS:
|
||||
mViewModel.notifySelectedCheatChanged();
|
||||
mViewModel.setIsEditing(false);
|
||||
if (mViewModel.getIsAdding().getValue())
|
||||
{
|
||||
mViewModel.finishAddingCheat();
|
||||
onSelectedCheatUpdated(mCheat);
|
||||
}
|
||||
else
|
||||
{
|
||||
mViewModel.notifySelectedCheatChanged();
|
||||
mViewModel.setIsEditing(false);
|
||||
}
|
||||
break;
|
||||
case Cheat.TRY_SET_FAIL_NO_NAME:
|
||||
mEditName.setError(getString(R.string.cheats_error_no_name));
|
||||
|
|
|
@ -11,6 +11,7 @@ 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;
|
||||
|
|
|
@ -11,8 +11,12 @@ import androidx.lifecycle.LifecycleOwner;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.features.cheats.model.Cheat;
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
|
||||
{
|
||||
|
@ -22,6 +26,12 @@ public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
|
|||
{
|
||||
mViewModel = viewModel;
|
||||
|
||||
mViewModel.getCheatAddedEvent().observe(owner, (position) ->
|
||||
{
|
||||
if (position != null)
|
||||
notifyItemInserted(position);
|
||||
});
|
||||
|
||||
mViewModel.getCheatChangedEvent().observe(owner, (position) ->
|
||||
{
|
||||
if (position != null)
|
||||
|
@ -43,6 +53,9 @@ public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
|
|||
case CheatItem.TYPE_HEADER:
|
||||
View headerView = inflater.inflate(R.layout.list_item_header, parent, false);
|
||||
return new HeaderViewHolder(headerView);
|
||||
case CheatItem.TYPE_ACTION:
|
||||
View actionView = inflater.inflate(R.layout.list_item_submenu, parent, false);
|
||||
return new ActionViewHolder(actionView);
|
||||
default:
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -57,8 +70,8 @@ public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
|
|||
@Override
|
||||
public int getItemCount()
|
||||
{
|
||||
return mViewModel.getARCheats().length + mViewModel.getGeckoCheats().length +
|
||||
mViewModel.getPatchCheats().length + 3;
|
||||
return mViewModel.getARCheats().size() + mViewModel.getGeckoCheats().size() +
|
||||
mViewModel.getPatchCheats().size() + 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,32 +82,50 @@ public class CheatsAdapter extends RecyclerView.Adapter<CheatItemViewHolder>
|
|||
|
||||
private CheatItem getItemAt(int position)
|
||||
{
|
||||
// Patches
|
||||
|
||||
if (position == 0)
|
||||
return new CheatItem(CheatItem.TYPE_HEADER, R.string.cheats_header_patch);
|
||||
position -= 1;
|
||||
|
||||
Cheat[] patchCheats = mViewModel.getPatchCheats();
|
||||
if (position < patchCheats.length)
|
||||
return new CheatItem(patchCheats[position]);
|
||||
position -= patchCheats.length;
|
||||
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;
|
||||
|
||||
Cheat[] arCheats = mViewModel.getARCheats();
|
||||
if (position < arCheats.length)
|
||||
return new CheatItem(arCheats[position]);
|
||||
position -= arCheats.length;
|
||||
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;
|
||||
|
||||
Cheat[] geckoCheats = mViewModel.getGeckoCheats();
|
||||
if (position < geckoCheats.length)
|
||||
return new CheatItem(geckoCheats[position]);
|
||||
position -= geckoCheats.length;
|
||||
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;
|
||||
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
|||
return new SliderViewHolder(view, this, mContext);
|
||||
|
||||
case SettingsItem.TYPE_SUBMENU:
|
||||
view = inflater.inflate(R.layout.list_item_setting_submenu, parent, false);
|
||||
view = inflater.inflate(R.layout.list_item_submenu, parent, false);
|
||||
return new SubmenuViewHolder(view, this);
|
||||
|
||||
case SettingsItem.TYPE_INPUT_BINDING:
|
||||
|
|
|
@ -392,6 +392,9 @@
|
|||
<string name="cheats_header_ar">AR Codes</string>
|
||||
<string name="cheats_header_gecko">Gecko Codes</string>
|
||||
<string name="cheats_header_patch">Patches</string>
|
||||
<string name="cheats_add_ar">Add New AR Code</string>
|
||||
<string name="cheats_add_gecko">Add New Gecko Code</string>
|
||||
<string name="cheats_add_patch">Add New Patch</string>
|
||||
<string name="cheats_name">Name</string>
|
||||
<string name="cheats_creator">Creator</string>
|
||||
<string name="cheats_notes">Notes</string>
|
||||
|
|
|
@ -36,6 +36,14 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_finalize(JNIEnv* en
|
|||
delete GetPointer(env, obj);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_createNew(JNIEnv* env, jobject obj)
|
||||
{
|
||||
auto* code = new ActionReplay::ARCode;
|
||||
code->user_defined = true;
|
||||
return reinterpret_cast<jlong>(code);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_ARCheat_getName(JNIEnv* env, jobject obj)
|
||||
{
|
||||
|
|
|
@ -35,6 +35,14 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_finalize(JNIEnv*
|
|||
delete GetPointer(env, obj);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_createNew(JNIEnv* env, jobject obj)
|
||||
{
|
||||
auto* code = new Gecko::GeckoCode;
|
||||
code->user_defined = true;
|
||||
return reinterpret_cast<jlong>(code);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_GeckoCheat_getName(JNIEnv* env, jobject obj)
|
||||
{
|
||||
|
|
|
@ -34,6 +34,14 @@ Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_finalize(JNIEnv*
|
|||
delete GetPointer(env, obj);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_createNew(JNIEnv* env, jobject obj)
|
||||
{
|
||||
auto* patch = new PatchEngine::Patch;
|
||||
patch->user_defined = true;
|
||||
return reinterpret_cast<jlong>(patch);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_dolphinemu_dolphinemu_features_cheats_model_PatchCheat_getName(JNIEnv* env, jobject obj)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue