From ee3a5a4a81f56aeeda8c2aa1c0800847ab652bed Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 22 Jun 2021 13:43:56 +0200 Subject: Android: Create CheatsActivity --- .../dolphinemu/dialogs/GamePropertiesDialog.java | 4 ++++ .../features/cheats/ui/CheatsActivity.java | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java index e550e30b2d..d35151e7e2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java @@ -12,6 +12,7 @@ import androidx.fragment.app.DialogFragment; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.ConvertActivity; +import org.dolphinemu.dolphinemu.features.cheats.ui.CheatsActivity; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; @@ -91,6 +92,9 @@ public class GamePropertiesDialog extends DialogFragment itemsBuilder.add(R.string.properties_edit_game_settings, (dialog, i) -> SettingsActivity.launch(getContext(), MenuTag.SETTINGS, gameId, revision, isWii)); + itemsBuilder.add(R.string.properties_edit_cheats, (dialog, i) -> + CheatsActivity.launch(getContext(), gameId, revision)); + itemsBuilder.add(R.string.properties_clear_game_settings, (dialog, i) -> clearGameSettings(gameId)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java new file mode 100644 index 0000000000..4090507408 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.cheats.ui; + +import android.content.Context; +import android.content.Intent; + +import androidx.appcompat.app.AppCompatActivity; + +public class CheatsActivity extends AppCompatActivity +{ + private static final String ARG_GAME_ID = "game_id"; + private static final String ARG_REVISION = "revision"; + + public static void launch(Context context, String gameId, int revision) + { + Intent intent = new Intent(context, CheatsActivity.class); + intent.putExtra(ARG_GAME_ID, gameId); + intent.putExtra(ARG_REVISION, revision); + context.startActivity(intent); + } +} -- cgit v1.2.3 From 4d609c769f7b8eba12da931c0936c5950509e165 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 17 Jul 2021 19:47:11 +0200 Subject: Android: Implement basic read-only cheats list --- .../dolphinemu/features/cheats/model/ARCheat.java | 27 ++++++++ .../dolphinemu/features/cheats/model/Cheat.java | 11 ++++ .../features/cheats/model/CheatsViewModel.java | 41 ++++++++++++ .../features/cheats/model/GeckoCheat.java | 27 ++++++++ .../features/cheats/model/PatchCheat.java | 27 ++++++++ .../features/cheats/ui/CheatListFragment.java | 43 +++++++++++++ .../features/cheats/ui/CheatViewHolder.java | 29 +++++++++ .../features/cheats/ui/CheatsActivity.java | 25 ++++++++ .../features/cheats/ui/CheatsAdapter.java | 72 ++++++++++++++++++++++ 9 files changed, 302 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java new file mode 100644 index 0000000000..f17ef89859 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java @@ -0,0 +1,27 @@ +// 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 implements Cheat +{ + @Keep + private final long mPointer; + + @Keep + private ARCheat(long pointer) + { + mPointer = pointer; + } + + @Override + public native void finalize(); + + @NonNull + public native String getName(); + + @NonNull + public static native ARCheat[] loadCodes(String gameId, int revision); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java new file mode 100644 index 0000000000..3c50325edf --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.cheats.model; + +import androidx.annotation.NonNull; + +public interface Cheat +{ + @NonNull + String getName(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java new file mode 100644 index 0000000000..24eb4eff7c --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.cheats.model; + +import androidx.lifecycle.ViewModel; + +public class CheatsViewModel extends ViewModel +{ + private boolean mLoaded = false; + + private PatchCheat[] mPatchCheats; + private ARCheat[] mARCheats; + private GeckoCheat[] mGeckoCheats; + + public void load(String gameID, int revision) + { + if (mLoaded) + return; + + mPatchCheats = PatchCheat.loadCodes(gameID, revision); + mARCheats = ARCheat.loadCodes(gameID, revision); + mGeckoCheats = GeckoCheat.loadCodes(gameID, revision); + + mLoaded = true; + } + + public Cheat[] getPatchCheats() + { + return mPatchCheats; + } + + public ARCheat[] getARCheats() + { + return mARCheats; + } + + public Cheat[] getGeckoCheats() + { + return mGeckoCheats; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java new file mode 100644 index 0000000000..5f9c7029ec --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -0,0 +1,27 @@ +// 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 GeckoCheat implements Cheat +{ + @Keep + private final long mPointer; + + @Keep + private GeckoCheat(long pointer) + { + mPointer = pointer; + } + + @Override + public native void finalize(); + + @NonNull + public native String getName(); + + @NonNull + public static native GeckoCheat[] loadCodes(String gameId, int revision); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java new file mode 100644 index 0000000000..5b2027807e --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java @@ -0,0 +1,27 @@ +// 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 implements Cheat +{ + @Keep + private final long mPointer; + + @Keep + private PatchCheat(long pointer) + { + mPointer = pointer; + } + + @Override + public native void finalize(); + + @NonNull + public native String getName(); + + @NonNull + public static native PatchCheat[] loadCodes(String gameId, int revision); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java new file mode 100644 index 0000000000..82c5b549bb --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java @@ -0,0 +1,43 @@ +// 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 androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; +import org.dolphinemu.dolphinemu.ui.DividerItemDecoration; + +public class CheatListFragment extends Fragment +{ + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) + { + return inflater.inflate(R.layout.fragment_cheat_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + RecyclerView recyclerView = view.findViewById(R.id.cheat_list); + + CheatsActivity activity = (CheatsActivity) requireActivity(); + CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); + + recyclerView.setAdapter(new CheatsAdapter(viewModel)); + recyclerView.setLayoutManager(new LinearLayoutManager(activity)); + recyclerView.addItemDecoration(new DividerItemDecoration(activity, null)); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java new file mode 100644 index 0000000000..e99f75f6b2 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -0,0 +1,29 @@ +// 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.recyclerview.widget.RecyclerView.ViewHolder; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; + +public class CheatViewHolder extends ViewHolder +{ + private TextView mName; + + public CheatViewHolder(@NonNull View itemView) + { + super(itemView); + + mName = itemView.findViewById(R.id.text_name); + } + + public void bind(Cheat item) + { + mName.setText(item.getName()); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index 4090507408..80a875e026 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -4,8 +4,14 @@ package org.dolphinemu.dolphinemu.features.cheats.ui; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.ViewModelProvider; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; +import org.dolphinemu.dolphinemu.ui.main.MainPresenter; public class CheatsActivity extends AppCompatActivity { @@ -19,4 +25,23 @@ public class CheatsActivity extends AppCompatActivity intent.putExtra(ARG_REVISION, revision); context.startActivity(intent); } + + @Override + protected void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + MainPresenter.skipRescanningLibrary(); + + Intent intent = getIntent(); + String gameId = intent.getStringExtra(ARG_GAME_ID); + int revision = intent.getIntExtra(ARG_REVISION, 0); + + setTitle(getString(R.string.cheats_with_game_id, gameId)); + + CheatsViewModel viewModel = new ViewModelProvider(this).get(CheatsViewModel.class); + viewModel.load(gameId, revision); + + setContentView(R.layout.activity_cheats); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java new file mode 100644 index 0000000000..6193b6c52f --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -0,0 +1,72 @@ +// 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.features.cheats.model.Cheat; +import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; + +public class CheatsAdapter extends RecyclerView.Adapter +{ + private final CheatsViewModel mViewModel; + + public CheatsAdapter(CheatsViewModel viewModel) + { + mViewModel = viewModel; + } + + @NonNull + @Override + public CheatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) + { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View view = inflater.inflate(R.layout.list_item_cheat, parent, false); + return new CheatViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull CheatViewHolder holder, int position) + { + holder.bind(getItemAt(position)); + } + + @Override + public int getItemCount() + { + return mViewModel.getARCheats().length + mViewModel.getGeckoCheats().length + + mViewModel.getPatchCheats().length; + } + + private Cheat getItemAt(int position) + { + Cheat[] patchCheats = mViewModel.getPatchCheats(); + if (position < patchCheats.length) + { + return patchCheats[position]; + } + position -= patchCheats.length; + + Cheat[] arCheats = mViewModel.getARCheats(); + if (position < arCheats.length) + { + return arCheats[position]; + } + position -= arCheats.length; + + Cheat[] geckoCheats = mViewModel.getGeckoCheats(); + if (position < geckoCheats.length) + { + return geckoCheats[position]; + } + position -= geckoCheats.length; + + throw new IndexOutOfBoundsException(); + } +} -- cgit v1.2.3 From 67a8855d9a5ec6e6c23617d72beb29db002424c7 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 17 Jul 2021 22:15:26 +0200 Subject: Android: Add "Enable Cheats" setting to GUI --- .../dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java | 2 ++ .../dolphinemu/features/settings/ui/SettingsFragmentPresenter.java | 2 ++ 2 files changed, 4 insertions(+) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java index bd71683c1f..480f69937f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/BooleanSetting.java @@ -14,6 +14,7 @@ public enum BooleanSetting implements AbstractBooleanSetting MAIN_FASTMEM(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "Fastmem", true), MAIN_CPU_THREAD(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "CPUThread", true), MAIN_SYNC_ON_SKIP_IDLE(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "SyncOnSkipIdle", true), + MAIN_ENABLE_CHEATS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "EnableCheats", false), MAIN_OVERRIDE_REGION_SETTINGS(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "OverrideRegionSettings", false), MAIN_AUDIO_STRETCH(Settings.FILE_DOLPHIN, Settings.SECTION_INI_CORE, "AudioStretch", false), @@ -198,6 +199,7 @@ public enum BooleanSetting implements AbstractBooleanSetting private static final BooleanSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new BooleanSetting[]{ MAIN_DSP_HLE, MAIN_CPU_THREAD, + MAIN_ENABLE_CHEATS, MAIN_OVERRIDE_REGION_SETTINGS, MAIN_WII_SD_CARD, // Can actually be changed, but specific code is required MAIN_DSP_JIT diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 0450a7cedf..d91faa775e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -258,6 +258,8 @@ public final class SettingsFragmentPresenter { sl.add(new CheckBoxSetting(mContext, BooleanSetting.MAIN_CPU_THREAD, R.string.dual_core, R.string.dual_core_description)); + sl.add(new CheckBoxSetting(mContext, BooleanSetting.MAIN_ENABLE_CHEATS, R.string.enable_cheats, + 0)); sl.add(new CheckBoxSetting(mContext, BooleanSetting.MAIN_OVERRIDE_REGION_SETTINGS, R.string.override_region_settings, 0)); sl.add(new CheckBoxSetting(mContext, BooleanSetting.MAIN_AUTO_DISC_CHANGE, -- cgit v1.2.3 From 93a12713864b147c003cfb757b42b47ecb69676f Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 18 Jul 2021 18:54:01 +0200 Subject: Android: Add checkboxes for toggling cheats enabled --- .../dolphinemu/features/cheats/model/ARCheat.java | 9 ++++- .../features/cheats/model/AbstractCheat.java | 29 +++++++++++++++++ .../dolphinemu/features/cheats/model/Cheat.java | 4 +++ .../features/cheats/model/CheatsViewModel.java | 38 ++++++++++++++++++++++ .../features/cheats/model/GeckoCheat.java | 9 ++++- .../features/cheats/model/PatchCheat.java | 9 ++++- .../features/cheats/ui/CheatViewHolder.java | 22 +++++++++++-- .../features/cheats/ui/CheatsActivity.java | 22 ++++++++++--- 8 files changed, 132 insertions(+), 10 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java index f17ef89859..ebb4ee9416 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java @@ -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); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java new file mode 100644 index 0000000000..8f6121808a --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java @@ -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); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java index 3c50325edf..97bd57d885 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java @@ -8,4 +8,8 @@ public interface Cheat { @NonNull String getName(); + + boolean getEnabled(); + + void setEnabled(boolean enabled); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index 24eb4eff7c..c2cadaf8c0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -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; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java index 5f9c7029ec..f6ea493ac3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -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); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java index 5b2027807e..411df3cdc0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java @@ -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); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java index e99f75f6b2..d3fa67905b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -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); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index 80a875e026..21fd9a04e9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -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); + } } -- cgit v1.2.3 From 95879c2e76a68526079445b427d1f3740416c978 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 4 Aug 2021 22:38:14 +0200 Subject: Android: Add details view for cheats The details view only contains the name of the cheat for now. --- .../features/cheats/model/CheatsViewModel.java | 14 +++++ .../features/cheats/ui/CheatDetailsFragment.java | 62 ++++++++++++++++++++++ .../features/cheats/ui/CheatViewHolder.java | 16 +++++- .../features/cheats/ui/CheatsAdapter.java | 2 +- 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index c2cadaf8c0..d05da7d978 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -2,12 +2,16 @@ package org.dolphinemu.dolphinemu.features.cheats.model; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; public class CheatsViewModel extends ViewModel { private boolean mLoaded = false; + private final MutableLiveData mSelectedCheat = new MutableLiveData<>(null); + private PatchCheat[] mPatchCheats; private ARCheat[] mARCheats; private GeckoCheat[] mGeckoCheats; @@ -62,6 +66,16 @@ public class CheatsViewModel extends ViewModel } } + public LiveData getSelectedCheat() + { + return mSelectedCheat; + } + + public void setSelectedCheat(Cheat cheat) + { + mSelectedCheat.setValue(cheat); + } + public Cheat[] getPatchCheats() { return mPatchCheats; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java new file mode 100644 index 0000000000..90356b63eb --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -0,0 +1,62 @@ +// 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 android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.ViewModelProvider; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; +import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; + +public class CheatDetailsFragment extends Fragment +{ + private View mRoot; + private EditText mEditName; + + private CheatsViewModel mViewModel; + private Cheat mCheat; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) + { + return inflater.inflate(R.layout.fragment_cheat_details, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + mRoot = view.findViewById(R.id.root); + mEditName = view.findViewById(R.id.edit_name); + + CheatsActivity activity = (CheatsActivity) requireActivity(); + mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); + + LiveData selectedCheat = mViewModel.getSelectedCheat(); + selectedCheat.observe(getViewLifecycleOwner(), this::populateFields); + populateFields(selectedCheat.getValue()); + } + + private void populateFields(@Nullable Cheat cheat) + { + mRoot.setVisibility(cheat == null ? View.GONE : View.VISIBLE); + + if (cheat != null && cheat != mCheat) + { + mEditName.setText(cheat.getName()); + } + + mCheat = cheat; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java index d3fa67905b..adaa5949a6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -12,34 +12,46 @@ import androidx.recyclerview.widget.RecyclerView.ViewHolder; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; +import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; -public class CheatViewHolder extends ViewHolder implements CompoundButton.OnCheckedChangeListener +public class CheatViewHolder extends ViewHolder + implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { + private final View mRoot; private final TextView mName; private final CheckBox mCheckbox; + private CheatsViewModel mViewModel; private Cheat mCheat; public CheatViewHolder(@NonNull View itemView) { super(itemView); + mRoot = itemView.findViewById(R.id.root); mName = itemView.findViewById(R.id.text_name); mCheckbox = itemView.findViewById(R.id.checkbox); } - public void bind(Cheat item) + public void bind(CheatsViewModel viewModel, Cheat item) { mCheckbox.setOnCheckedChangeListener(null); mName.setText(item.getName()); mCheckbox.setChecked(item.getEnabled()); + mViewModel = viewModel; mCheat = item; + mRoot.setOnClickListener(this); mCheckbox.setOnCheckedChangeListener(this); } + public void onClick(View root) + { + mViewModel.setSelectedCheat(mCheat); + } + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mCheat.setEnabled(isChecked); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index 6193b6c52f..3638ab383f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -34,7 +34,7 @@ public class CheatsAdapter extends RecyclerView.Adapter @Override public void onBindViewHolder(@NonNull CheatViewHolder holder, int position) { - holder.bind(getItemAt(position)); + holder.bind(mViewModel, getItemAt(position)); } @Override -- cgit v1.2.3 From a303b4bc98af237ab708221b88c51df22d3f2ebd Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 5 Aug 2021 16:23:13 +0200 Subject: Android: Programmatically open/close cheat details Only has an effect when using a narrow screen. --- .../features/cheats/model/CheatsViewModel.java | 13 ++++++ .../features/cheats/ui/CheatViewHolder.java | 1 + .../features/cheats/ui/CheatsActivity.java | 32 +++++++++++++++ .../ui/TwoPaneOnBackPressedCallback.java | 46 ++++++++++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index d05da7d978..aac6c02ed0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -12,6 +12,8 @@ public class CheatsViewModel extends ViewModel private final MutableLiveData mSelectedCheat = new MutableLiveData<>(null); + private final MutableLiveData mOpenDetailsViewEvent = new MutableLiveData<>(false); + private PatchCheat[] mPatchCheats; private ARCheat[] mARCheats; private GeckoCheat[] mGeckoCheats; @@ -76,6 +78,17 @@ public class CheatsViewModel extends ViewModel mSelectedCheat.setValue(cheat); } + public LiveData getOpenDetailsViewEvent() + { + return mOpenDetailsViewEvent; + } + + public void openDetailsView() + { + mOpenDetailsViewEvent.setValue(true); + mOpenDetailsViewEvent.setValue(false); + } + public Cheat[] getPatchCheats() { return mPatchCheats; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java index adaa5949a6..c48c7c27be 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -50,6 +50,7 @@ public class CheatViewHolder extends ViewHolder public void onClick(View root) { mViewModel.setSelectedCheat(mCheat); + mViewModel.openDetailsView(); } public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index 21fd9a04e9..911c54ab0e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -8,9 +8,12 @@ import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; +import androidx.slidingpanelayout.widget.SlidingPaneLayout; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; +import org.dolphinemu.dolphinemu.ui.TwoPaneOnBackPressedCallback; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; public class CheatsActivity extends AppCompatActivity @@ -22,6 +25,8 @@ public class CheatsActivity extends AppCompatActivity private int mRevision; private CheatsViewModel mViewModel; + private SlidingPaneLayout mSlidingPaneLayout; + public static void launch(Context context, String gameId, int revision) { Intent intent = new Intent(context, CheatsActivity.class); @@ -47,6 +52,16 @@ public class CheatsActivity extends AppCompatActivity mViewModel.load(mGameId, mRevision); setContentView(R.layout.activity_cheats); + + mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout); + + getOnBackPressedDispatcher().addCallback(this, + new TwoPaneOnBackPressedCallback(mSlidingPaneLayout)); + + mViewModel.getSelectedCheat().observe(this, this::onSelectedCheatChanged); + onSelectedCheatChanged(mViewModel.getSelectedCheat().getValue()); + + mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView); } @Override @@ -56,4 +71,21 @@ public class CheatsActivity extends AppCompatActivity mViewModel.saveIfNeeded(mGameId, mRevision); } + + private void onSelectedCheatChanged(Cheat selectedCheat) + { + boolean cheatSelected = selectedCheat != null; + + if (!cheatSelected && mSlidingPaneLayout.isOpen()) + mSlidingPaneLayout.close(); + + mSlidingPaneLayout.setLockMode(cheatSelected ? + SlidingPaneLayout.LOCK_MODE_UNLOCKED : SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED); + } + + private void openDetailsView(boolean open) + { + if (open) + mSlidingPaneLayout.open(); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java new file mode 100644 index 0000000000..a6a0da5f97 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/TwoPaneOnBackPressedCallback.java @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.ui; + +import android.view.View; + +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) + { + setEnabled(false); + } +} + -- cgit v1.2.3 From 43dcbf33adc72e935c518c3a7a0e621fe5694b69 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 7 Aug 2021 16:08:07 +0200 Subject: Android: Add edit button for cheats --- .../dolphinemu/features/cheats/model/ARCheat.java | 5 ++ .../features/cheats/model/AbstractCheat.java | 16 ++++++ .../dolphinemu/features/cheats/model/Cheat.java | 10 ++++ .../features/cheats/model/CheatsViewModel.java | 14 +++++ .../features/cheats/model/GeckoCheat.java | 5 ++ .../features/cheats/model/PatchCheat.java | 5 ++ .../features/cheats/ui/CheatDetailsFragment.java | 64 ++++++++++++++++++++-- 7 files changed, 114 insertions(+), 5 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java index ebb4ee9416..bed39c2dbd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java @@ -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); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java index 8f6121808a..60391895c7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java @@ -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); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java index 97bd57d885..baf8ebb5be 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java @@ -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); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index aac6c02ed0..e4e5298104 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -11,6 +11,7 @@ public class CheatsViewModel extends ViewModel private boolean mLoaded = false; private final MutableLiveData mSelectedCheat = new MutableLiveData<>(null); + private final MutableLiveData mIsEditing = new MutableLiveData<>(false); private final MutableLiveData 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 getIsEditing() + { + return mIsEditing; + } + + public void setIsEditing(boolean isEditing) + { + mIsEditing.setValue(isEditing); + } + public LiveData getOpenDetailsViewEvent() { return mOpenDetailsViewEvent; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java index f6ea493ac3..06815d1fe9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -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); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java index 411df3cdc0..b4e0ee3e53 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java @@ -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); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index 90356b63eb..0b7ce49785 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -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 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 clearEditErrors() + { + mEditName.setError(null); } - private void populateFields(@Nullable Cheat cheat) + 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); + } } -- cgit v1.2.3 From fc6c31c3db77dcb04473378f27e24283131b6052 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sat, 7 Aug 2021 19:40:45 +0200 Subject: Android: Update cheat list when a cheat is edited The way I'm implementing events using LiveData feels rather unorthodox, but I'm not aware of anything in the Android framework that would let me do it in a better way... One option I did consider was wrapping the cheat lists in LiveData and observing those, but then CheatsAdapter wouldn't know which cheat had changed, only that there was some kind of change to the list, necessitating the use of the not recommended notifyDataSetChanged. --- .../features/cheats/model/CheatsViewModel.java | 31 +++++++++++++++++++++- .../features/cheats/ui/CheatDetailsFragment.java | 1 + .../features/cheats/ui/CheatListFragment.java | 2 +- .../features/cheats/ui/CheatViewHolder.java | 7 +++-- .../features/cheats/ui/CheatsAdapter.java | 11 ++++++-- 5 files changed, 46 insertions(+), 6 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index e4e5298104..cdd68221c6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -10,9 +10,11 @@ public class CheatsViewModel extends ViewModel { private boolean mLoaded = false; + private int mSelectedCheatPosition = -1; private final MutableLiveData mSelectedCheat = new MutableLiveData<>(null); private final MutableLiveData mIsEditing = new MutableLiveData<>(false); + private final MutableLiveData mCheatChangedEvent = new MutableLiveData<>(null); private final MutableLiveData mOpenDetailsViewEvent = new MutableLiveData<>(false); private PatchCheat[] mPatchCheats; @@ -74,12 +76,13 @@ public class CheatsViewModel extends ViewModel return mSelectedCheat; } - public void setSelectedCheat(Cheat cheat) + public void setSelectedCheat(Cheat cheat, int position) { if (mIsEditing.getValue()) setIsEditing(false); mSelectedCheat.setValue(cheat); + mSelectedCheatPosition = position; } public LiveData getIsEditing() @@ -92,6 +95,32 @@ public class CheatsViewModel extends ViewModel mIsEditing.setValue(isEditing); } + /** + * 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 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); + } + public LiveData getOpenDetailsViewEvent() { return mOpenDetailsViewEvent; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index 0b7ce49785..3a61b2e4b7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -76,6 +76,7 @@ public class CheatDetailsFragment extends Fragment switch (result) { case Cheat.TRY_SET_SUCCESS: + mViewModel.notifySelectedCheatChanged(); mViewModel.setIsEditing(false); break; case Cheat.TRY_SET_FAIL_NO_NAME: diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java index 82c5b549bb..03f87f0c0c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java @@ -36,7 +36,7 @@ public class CheatListFragment extends Fragment CheatsActivity activity = (CheatsActivity) requireActivity(); CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); - recyclerView.setAdapter(new CheatsAdapter(viewModel)); + recyclerView.setAdapter(new CheatsAdapter(getViewLifecycleOwner(), viewModel)); recyclerView.setLayoutManager(new LinearLayoutManager(activity)); recyclerView.addItemDecoration(new DividerItemDecoration(activity, null)); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java index c48c7c27be..1ffcafafd5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -23,6 +23,7 @@ public class CheatViewHolder extends ViewHolder private CheatsViewModel mViewModel; private Cheat mCheat; + private int mPosition; public CheatViewHolder(@NonNull View itemView) { @@ -33,7 +34,7 @@ public class CheatViewHolder extends ViewHolder mCheckbox = itemView.findViewById(R.id.checkbox); } - public void bind(CheatsViewModel viewModel, Cheat item) + public void bind(CheatsViewModel viewModel, Cheat item, int position) { mCheckbox.setOnCheckedChangeListener(null); @@ -42,6 +43,7 @@ public class CheatViewHolder extends ViewHolder mViewModel = viewModel; mCheat = item; + mPosition = position; mRoot.setOnClickListener(this); mCheckbox.setOnCheckedChangeListener(this); @@ -49,12 +51,13 @@ public class CheatViewHolder extends ViewHolder public void onClick(View root) { - mViewModel.setSelectedCheat(mCheat); + mViewModel.setSelectedCheat(mCheat, mPosition); mViewModel.openDetailsView(); } public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mCheat.setEnabled(isChecked); + mViewModel.notifyCheatChanged(mPosition); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index 3638ab383f..623f89c507 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -7,6 +7,7 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; +import androidx.lifecycle.LifecycleOwner; import androidx.recyclerview.widget.RecyclerView; import org.dolphinemu.dolphinemu.R; @@ -17,9 +18,15 @@ public class CheatsAdapter extends RecyclerView.Adapter { private final CheatsViewModel mViewModel; - public CheatsAdapter(CheatsViewModel viewModel) + public CheatsAdapter(LifecycleOwner owner, CheatsViewModel viewModel) { mViewModel = viewModel; + + mViewModel.getCheatChangedEvent().observe(owner, (position) -> + { + if (position != null) + notifyItemChanged(position); + }); } @NonNull @@ -34,7 +41,7 @@ public class CheatsAdapter extends RecyclerView.Adapter @Override public void onBindViewHolder(@NonNull CheatViewHolder holder, int position) { - holder.bind(mViewModel, getItemAt(position)); + holder.bind(mViewModel, getItemAt(position), position); } @Override -- cgit v1.2.3 From 883a9f8a9900db8456350123e48618497ffb961c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 9 Aug 2021 15:24:32 +0200 Subject: Android: Allow viewing/editing the actual codes --- .../dolphinemu/features/cheats/model/ARCheat.java | 5 ++++- .../features/cheats/model/AbstractCheat.java | 6 +++--- .../dolphinemu/features/cheats/model/Cheat.java | 8 +++++++- .../dolphinemu/features/cheats/model/GeckoCheat.java | 5 ++++- .../dolphinemu/features/cheats/model/PatchCheat.java | 5 ++++- .../features/cheats/ui/CheatDetailsFragment.java | 18 ++++++++++++++++-- 6 files changed, 38 insertions(+), 9 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java index bed39c2dbd..432da43ce1 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java @@ -22,12 +22,15 @@ public class ARCheat extends AbstractCheat @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); + protected native int trySetImpl(@NonNull String name, @NonNull String code); @Override protected native void setEnabledImpl(boolean enabled); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java index 60391895c7..cca4b041d9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java @@ -9,12 +9,12 @@ public abstract class AbstractCheat implements Cheat { private Runnable mChangedCallback = null; - public int trySet(@NonNull String name) + public int trySet(@NonNull String name, @NonNull String code) { if (name.isEmpty()) return TRY_SET_FAIL_NO_NAME; - int result = trySetImpl(name); + int result = trySetImpl(name, code); if (result == TRY_SET_SUCCESS) onChanged(); @@ -39,7 +39,7 @@ public abstract class AbstractCheat implements Cheat mChangedCallback.run(); } - protected abstract int trySetImpl(@NonNull String name); + protected abstract int trySetImpl(@NonNull String name, @NonNull String code); protected abstract void setEnabledImpl(boolean enabled); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java index baf8ebb5be..505bd5c026 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java @@ -7,13 +7,19 @@ 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) @NonNull String getName(); - int trySet(@NonNull String name); + @NonNull + String getCode(); + + int trySet(@NonNull String name, @NonNull String code); boolean getUserDefined(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java index 06815d1fe9..6ef6a9431e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -22,12 +22,15 @@ public class GeckoCheat extends AbstractCheat @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); + protected native int trySetImpl(@NonNull String name, @NonNull String code); @Override protected native void setEnabledImpl(boolean enabled); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java index b4e0ee3e53..8514ae00cc 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java @@ -22,12 +22,15 @@ public class PatchCheat extends AbstractCheat @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); + protected native int trySetImpl(@NonNull String name, @NonNull String code); @Override protected native void setEnabledImpl(boolean enabled); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index 3a61b2e4b7..ffe86fc702 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -23,6 +23,7 @@ public class CheatDetailsFragment extends Fragment { private View mRoot; private EditText mEditName; + private EditText mEditCode; private Button mButtonEdit; private Button mButtonCancel; private Button mButtonOk; @@ -43,6 +44,7 @@ public class CheatDetailsFragment extends Fragment { mRoot = view.findViewById(R.id.root); mEditName = view.findViewById(R.id.edit_name); + mEditCode = view.findViewById(R.id.edit_code); mButtonEdit = view.findViewById(R.id.button_edit); mButtonCancel = view.findViewById(R.id.button_cancel); mButtonOk = view.findViewById(R.id.button_ok); @@ -65,13 +67,14 @@ public class CheatDetailsFragment extends Fragment private void clearEditErrors() { mEditName.setError(null); + mEditCode.setError(null); } private void onOkClicked(View view) { clearEditErrors(); - int result = mCheat.trySet(mEditName.getText().toString()); + int result = mCheat.trySet(mEditName.getText().toString(), mEditCode.getText().toString()); switch (result) { @@ -80,7 +83,16 @@ public class CheatDetailsFragment extends Fragment mViewModel.setIsEditing(false); break; case Cheat.TRY_SET_FAIL_NO_NAME: - mEditName.setError(getText(R.string.cheats_error_no_name)); + mEditName.setError(getString(R.string.cheats_error_no_name)); + break; + case Cheat.TRY_SET_FAIL_NO_CODE_LINES: + mEditCode.setError(getString(R.string.cheats_error_no_code_lines)); + break; + case Cheat.TRY_SET_FAIL_CODE_MIXED_ENCRYPTION: + mEditCode.setError(getString(R.string.cheats_error_mixed_encryption)); + break; + default: + mEditCode.setError(getString(R.string.cheats_error_on_line, result)); break; } } @@ -101,6 +113,7 @@ public class CheatDetailsFragment extends Fragment if (!isEditing && cheat != null) { mEditName.setText(cheat.getName()); + mEditCode.setText(cheat.getCode()); } mCheat = cheat; @@ -109,6 +122,7 @@ public class CheatDetailsFragment extends Fragment private void onIsEditingUpdated(boolean isEditing) { mEditName.setEnabled(isEditing); + mEditCode.setEnabled(isEditing); mButtonEdit.setVisibility(isEditing ? View.GONE : View.VISIBLE); mButtonCancel.setVisibility(isEditing ? View.VISIBLE : View.GONE); -- cgit v1.2.3 From e299be1d857001e8eaea7674e4c763b5d37e0336 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Mon, 9 Aug 2021 19:54:52 +0200 Subject: Android: Add creator and notes fields for Gecko codes --- .../dolphinemu/features/cheats/model/ARCheat.java | 13 +++++++++++- .../features/cheats/model/AbstractCheat.java | 8 +++++--- .../dolphinemu/features/cheats/model/Cheat.java | 19 +++++++++++++++++- .../features/cheats/model/GeckoCheat.java | 19 +++++++++++++++++- .../features/cheats/model/PatchCheat.java | 13 +++++++++++- .../features/cheats/ui/CheatDetailsFragment.java | 23 +++++++++++++++++++++- 6 files changed, 87 insertions(+), 8 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java index 432da43ce1..24b74b489a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java @@ -19,6 +19,16 @@ public class ARCheat extends AbstractCheat @Override public native void finalize(); + public boolean supportsCreator() + { + return false; + } + + public boolean supportsNotes() + { + return false; + } + @NonNull public native String getName(); @@ -30,7 +40,8 @@ public class ARCheat extends AbstractCheat public native boolean getEnabled(); @Override - protected native int trySetImpl(@NonNull String name, @NonNull String code); + protected native int trySetImpl(@NonNull String name, @NonNull String creator, + @NonNull String notes, @NonNull String code); @Override protected native void setEnabledImpl(boolean enabled); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java index cca4b041d9..5dc5e80b6f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java @@ -9,12 +9,13 @@ public abstract class AbstractCheat implements Cheat { private Runnable mChangedCallback = null; - public int trySet(@NonNull String name, @NonNull String code) + public int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes, + @NonNull String code) { if (name.isEmpty()) return TRY_SET_FAIL_NO_NAME; - int result = trySetImpl(name, code); + int result = trySetImpl(name, creator, notes, code); if (result == TRY_SET_SUCCESS) onChanged(); @@ -39,7 +40,8 @@ public abstract class AbstractCheat implements Cheat mChangedCallback.run(); } - protected abstract int trySetImpl(@NonNull String name, @NonNull String code); + protected abstract int trySetImpl(@NonNull String name, @NonNull String creator, + @NonNull String notes, @NonNull String code); protected abstract void setEnabledImpl(boolean enabled); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java index 505bd5c026..75a3befc6f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/Cheat.java @@ -13,13 +13,30 @@ public interface Cheat int TRY_SET_SUCCESS = 0; // Result codes greater than 0 represent an error on the corresponding code line (one-indexed) + boolean supportsCreator(); + + boolean supportsNotes(); + @NonNull String getName(); + @NonNull + default String getCreator() + { + return ""; + } + + @NonNull + default String getNotes() + { + return ""; + } + @NonNull String getCode(); - int trySet(@NonNull String name, @NonNull String code); + int trySet(@NonNull String name, @NonNull String creator, @NonNull String notes, + @NonNull String code); boolean getUserDefined(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java index 6ef6a9431e..1847daa40d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -19,9 +19,25 @@ public class GeckoCheat extends AbstractCheat @Override public native void finalize(); + 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(); @@ -30,7 +46,8 @@ public class GeckoCheat extends AbstractCheat public native boolean getEnabled(); @Override - protected native int trySetImpl(@NonNull String name, @NonNull String code); + protected native int trySetImpl(@NonNull String name, @NonNull String creator, + @NonNull String notes, @NonNull String code); @Override protected native void setEnabledImpl(boolean enabled); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java index 8514ae00cc..52d88413dd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java @@ -19,6 +19,16 @@ public class PatchCheat extends AbstractCheat @Override public native void finalize(); + public boolean supportsCreator() + { + return false; + } + + public boolean supportsNotes() + { + return false; + } + @NonNull public native String getName(); @@ -30,7 +40,8 @@ public class PatchCheat extends AbstractCheat public native boolean getEnabled(); @Override - protected native int trySetImpl(@NonNull String name, @NonNull String code); + protected native int trySetImpl(@NonNull String name, @NonNull String creator, + @NonNull String notes, @NonNull String code); @Override protected native void setEnabledImpl(boolean enabled); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index ffe86fc702..db625928f9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -8,6 +8,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -23,6 +24,10 @@ public class CheatDetailsFragment extends Fragment { private View mRoot; private EditText mEditName; + private TextView mLabelCreator; + private EditText mEditCreator; + private TextView mLabelNotes; + private EditText mEditNotes; private EditText mEditCode; private Button mButtonEdit; private Button mButtonCancel; @@ -44,6 +49,10 @@ public class CheatDetailsFragment extends Fragment { mRoot = view.findViewById(R.id.root); mEditName = view.findViewById(R.id.edit_name); + mLabelCreator = view.findViewById(R.id.label_creator); + mEditCreator = view.findViewById(R.id.edit_creator); + mLabelNotes = view.findViewById(R.id.label_notes); + mEditNotes = view.findViewById(R.id.edit_notes); mEditCode = view.findViewById(R.id.edit_code); mButtonEdit = view.findViewById(R.id.button_edit); mButtonCancel = view.findViewById(R.id.button_cancel); @@ -74,7 +83,8 @@ public class CheatDetailsFragment extends Fragment { clearEditErrors(); - int result = mCheat.trySet(mEditName.getText().toString(), mEditCode.getText().toString()); + int result = mCheat.trySet(mEditName.getText().toString(), mEditCreator.getText().toString(), + mEditNotes.getText().toString(), mEditCode.getText().toString()); switch (result) { @@ -103,6 +113,13 @@ public class CheatDetailsFragment extends Fragment mRoot.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; + mLabelCreator.setVisibility(creatorVisibility); + mEditCreator.setVisibility(creatorVisibility); + mLabelNotes.setVisibility(notesVisibility); + mEditNotes.setVisibility(notesVisibility); + boolean userDefined = cheat != null && cheat.getUserDefined(); mButtonEdit.setEnabled(userDefined); @@ -113,6 +130,8 @@ public class CheatDetailsFragment extends Fragment if (!isEditing && cheat != null) { mEditName.setText(cheat.getName()); + mEditCreator.setText(cheat.getCreator()); + mEditNotes.setText(cheat.getNotes()); mEditCode.setText(cheat.getCode()); } @@ -122,6 +141,8 @@ public class CheatDetailsFragment extends Fragment private void onIsEditingUpdated(boolean isEditing) { mEditName.setEnabled(isEditing); + mEditCreator.setEnabled(isEditing); + mEditNotes.setEnabled(isEditing); mEditCode.setEnabled(isEditing); mButtonEdit.setVisibility(isEditing ? View.GONE : View.VISIBLE); -- cgit v1.2.3 From 109aef4b8109dd83f713a66fb64558df72d1aafd Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 10 Aug 2021 15:12:05 +0200 Subject: Android: Scroll to field with error when error occurs --- .../dolphinemu/features/cheats/ui/CheatDetailsFragment.java | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index db625928f9..2a2896251a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -8,6 +8,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; +import android.widget.ScrollView; import android.widget.TextView; import androidx.annotation.NonNull; @@ -23,6 +24,8 @@ import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; public class CheatDetailsFragment extends Fragment { private View mRoot; + private ScrollView mScrollView; + private TextView mLabelName; private EditText mEditName; private TextView mLabelCreator; private EditText mEditCreator; @@ -48,6 +51,8 @@ public class CheatDetailsFragment extends Fragment public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { mRoot = view.findViewById(R.id.root); + mScrollView = view.findViewById(R.id.scroll_view); + mLabelName = view.findViewById(R.id.label_name); mEditName = view.findViewById(R.id.edit_name); mLabelCreator = view.findViewById(R.id.label_creator); mEditCreator = view.findViewById(R.id.edit_creator); @@ -94,15 +99,19 @@ public class CheatDetailsFragment extends Fragment break; case Cheat.TRY_SET_FAIL_NO_NAME: mEditName.setError(getString(R.string.cheats_error_no_name)); + mScrollView.smoothScrollTo(0, mLabelName.getTop()); break; case Cheat.TRY_SET_FAIL_NO_CODE_LINES: mEditCode.setError(getString(R.string.cheats_error_no_code_lines)); + mScrollView.smoothScrollTo(0, mEditCode.getBottom()); break; case Cheat.TRY_SET_FAIL_CODE_MIXED_ENCRYPTION: mEditCode.setError(getString(R.string.cheats_error_mixed_encryption)); + mScrollView.smoothScrollTo(0, mEditCode.getBottom()); break; default: mEditCode.setError(getString(R.string.cheats_error_on_line, result)); + mScrollView.smoothScrollTo(0, mEditCode.getBottom()); break; } } -- cgit v1.2.3 From 6934b9a21dea3615950affc8bfacf7e72dbc8fd9 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 10 Aug 2021 12:58:19 +0200 Subject: Android: Add headers to cheat list --- .../dolphinemu/features/cheats/ui/CheatItem.java | 48 +++++++++++++++++++ .../features/cheats/ui/CheatItemViewHolder.java | 20 ++++++++ .../features/cheats/ui/CheatViewHolder.java | 12 ++--- .../features/cheats/ui/CheatsAdapter.java | 54 +++++++++++++++------- .../features/cheats/ui/HeaderViewHolder.java | 28 +++++++++++ .../features/settings/ui/SettingsAdapter.java | 8 +--- 6 files changed, 141 insertions(+), 29 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java new file mode 100644 index 0000000000..dd3ee76ab0 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java @@ -0,0 +1,48 @@ +// 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; + + private final @Nullable Cheat mCheat; + private final int mString; + private final int mType; + + public CheatItem(@NonNull Cheat cheat) + { + mCheat = cheat; + mString = 0; + mType = TYPE_CHEAT; + } + + public CheatItem(int type, int string) + { + mCheat = null; + mString = string; + mType = type; + } + + @Nullable + public Cheat getCheat() + { + return mCheat; + } + + public int getString() + { + return mString; + } + + public int getType() + { + return mType; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java new file mode 100644 index 0000000000..ca07d3a871 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java @@ -0,0 +1,20 @@ +// 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; + +import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; + +public abstract class CheatItemViewHolder extends RecyclerView.ViewHolder +{ + public CheatItemViewHolder(@NonNull View itemView) + { + super(itemView); + } + + public abstract void bind(CheatsViewModel viewModel, CheatItem item, int position); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java index 1ffcafafd5..3f56f480cd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -14,7 +14,7 @@ import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; -public class CheatViewHolder extends ViewHolder +public class CheatViewHolder extends CheatItemViewHolder implements View.OnClickListener, CompoundButton.OnCheckedChangeListener { private final View mRoot; @@ -34,17 +34,17 @@ public class CheatViewHolder extends ViewHolder mCheckbox = itemView.findViewById(R.id.checkbox); } - public void bind(CheatsViewModel viewModel, Cheat item, int position) + public void bind(CheatsViewModel viewModel, CheatItem item, int position) { mCheckbox.setOnCheckedChangeListener(null); - mName.setText(item.getName()); - mCheckbox.setChecked(item.getEnabled()); - mViewModel = viewModel; - mCheat = item; + mCheat = item.getCheat(); mPosition = position; + mName.setText(mCheat.getName()); + mCheckbox.setChecked(mCheat.getEnabled()); + mRoot.setOnClickListener(this); mCheckbox.setOnCheckedChangeListener(this); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index 623f89c507..f0431c29a6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -14,7 +14,7 @@ import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; -public class CheatsAdapter extends RecyclerView.Adapter +public class CheatsAdapter extends RecyclerView.Adapter { private final CheatsViewModel mViewModel; @@ -31,15 +31,25 @@ public class CheatsAdapter extends RecyclerView.Adapter @NonNull @Override - public CheatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) + public CheatItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - View view = inflater.inflate(R.layout.list_item_cheat, parent, false); - return new CheatViewHolder(view); + + switch (viewType) + { + case CheatItem.TYPE_CHEAT: + View cheatView = inflater.inflate(R.layout.list_item_cheat, parent, false); + return new CheatViewHolder(cheatView); + case CheatItem.TYPE_HEADER: + View headerView = inflater.inflate(R.layout.list_item_header, parent, false); + return new HeaderViewHolder(headerView); + default: + throw new UnsupportedOperationException(); + } } @Override - public void onBindViewHolder(@NonNull CheatViewHolder holder, int position) + public void onBindViewHolder(@NonNull CheatItemViewHolder holder, int position) { holder.bind(mViewModel, getItemAt(position), position); } @@ -48,30 +58,42 @@ public class CheatsAdapter extends RecyclerView.Adapter public int getItemCount() { return mViewModel.getARCheats().length + mViewModel.getGeckoCheats().length + - mViewModel.getPatchCheats().length; + mViewModel.getPatchCheats().length + 3; + } + + @Override + public int getItemViewType(int position) + { + return getItemAt(position).getType(); } - private Cheat getItemAt(int position) + private CheatItem getItemAt(int position) { + 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 patchCheats[position]; - } + return new CheatItem(patchCheats[position]); position -= patchCheats.length; + 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 arCheats[position]; - } + return new CheatItem(arCheats[position]); position -= arCheats.length; + 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 geckoCheats[position]; - } + return new CheatItem(geckoCheats[position]); position -= geckoCheats.length; throw new IndexOutOfBoundsException(); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java new file mode 100644 index 0000000000..29da69c9da --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java @@ -0,0 +1,28 @@ +// 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.CheatsViewModel; + +public class HeaderViewHolder extends CheatItemViewHolder +{ + private TextView mHeaderName; + + public HeaderViewHolder(@NonNull View itemView) + { + super(itemView); + + mHeaderName = itemView.findViewById(R.id.text_header_name); + } + + public void bind(CheatsViewModel viewModel, CheatItem item, int position) + { + mHeaderName.setText(item.getString()); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index 85e26ef6ca..e216330280 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -18,9 +18,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog; -import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.features.settings.model.StringSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.CheckBoxSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; @@ -43,7 +41,6 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHold import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHolder; -import org.dolphinemu.dolphinemu.ui.main.MainPresenter; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.Log; @@ -51,10 +48,7 @@ import org.dolphinemu.dolphinemu.utils.Log; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.security.InvalidParameterException; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Map; public final class SettingsAdapter extends RecyclerView.Adapter implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener @@ -87,7 +81,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter Date: Tue, 10 Aug 2021 13:51:32 +0200 Subject: Android: Add the ability to add cheats --- .../dolphinemu/features/cheats/model/ARCheat.java | 7 ++ .../features/cheats/model/CheatsViewModel.java | 103 ++++++++++++++++++--- .../features/cheats/model/GeckoCheat.java | 7 ++ .../features/cheats/model/PatchCheat.java | 7 ++ .../features/cheats/ui/ActionViewHolder.java | 60 ++++++++++++ .../features/cheats/ui/CheatDetailsFragment.java | 12 ++- .../dolphinemu/features/cheats/ui/CheatItem.java | 1 + .../features/cheats/ui/CheatsAdapter.java | 61 +++++++++--- .../features/settings/ui/SettingsAdapter.java | 2 +- 9 files changed, 230 insertions(+), 30 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java index 24b74b489a..8a168e8bc5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/ARCheat.java @@ -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; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index cdd68221c6..f405c427a5 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -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 mSelectedCheat = new MutableLiveData<>(null); + private final MutableLiveData mIsAdding = new MutableLiveData<>(false); private final MutableLiveData mIsEditing = new MutableLiveData<>(false); + private final MutableLiveData mCheatAddedEvent = new MutableLiveData<>(null); private final MutableLiveData mCheatChangedEvent = new MutableLiveData<>(null); private final MutableLiveData mOpenDetailsViewEvent = new MutableLiveData<>(false); - private PatchCheat[] mPatchCheats; - private ARCheat[] mARCheats; - private GeckoCheat[] mGeckoCheats; + private ArrayList mPatchCheats; + private ArrayList mARCheats; + private ArrayList 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 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 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 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 getPatchCheats() { return mPatchCheats; } - public ARCheat[] getARCheats() + public ArrayList getARCheats() { return mARCheats; } - public Cheat[] getGeckoCheats() + public ArrayList getGeckoCheats() { return mGeckoCheats; } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java index 1847daa40d..19573723b6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -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; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java index 52d88413dd..9dcb9f8111 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/PatchCheat.java @@ -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; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java new file mode 100644 index 0000000000..72380f8ab1 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java @@ -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(); + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index 2a2896251a..3de19df37a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -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)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java index dd3ee76ab0..b293cd84d1 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItem.java @@ -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; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index f0431c29a6..3406e1b692 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -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 { @@ -22,6 +26,12 @@ public class CheatsAdapter extends RecyclerView.Adapter { 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 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 @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 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 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 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 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(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index e216330280..753dddf522 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -99,7 +99,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter Date: Tue, 10 Aug 2021 15:42:35 +0200 Subject: Android: Add the ability to delete cheats --- .../features/cheats/model/CheatsViewModel.java | 36 ++++++++++++++++++++++ .../features/cheats/ui/CheatDetailsFragment.java | 16 ++++++++++ .../features/cheats/ui/CheatsAdapter.java | 6 ++++ 3 files changed, 58 insertions(+) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index f405c427a5..750c921c94 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -20,6 +20,7 @@ public class CheatsViewModel extends ViewModel private final MutableLiveData mCheatAddedEvent = new MutableLiveData<>(null); private final MutableLiveData mCheatChangedEvent = new MutableLiveData<>(null); + private final MutableLiveData mCheatDeletedEvent = new MutableLiveData<>(null); private final MutableLiveData mOpenDetailsViewEvent = new MutableLiveData<>(false); private ArrayList mPatchCheats; @@ -200,6 +201,41 @@ public class CheatsViewModel extends ViewModel 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 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); + } + public LiveData getOpenDetailsViewEvent() { return mOpenDetailsViewEvent; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index 3de19df37a..421b0aa094 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -13,6 +13,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModelProvider; @@ -32,6 +33,7 @@ public class CheatDetailsFragment extends Fragment private TextView mLabelNotes; private EditText mEditNotes; private EditText mEditCode; + private Button mButtonDelete; private Button mButtonEdit; private Button mButtonCancel; private Button mButtonOk; @@ -59,6 +61,7 @@ public class CheatDetailsFragment extends Fragment mLabelNotes = view.findViewById(R.id.label_notes); mEditNotes = view.findViewById(R.id.edit_notes); mEditCode = view.findViewById(R.id.edit_code); + mButtonDelete = view.findViewById(R.id.button_delete); mButtonEdit = view.findViewById(R.id.button_edit); mButtonCancel = view.findViewById(R.id.button_cancel); mButtonOk = view.findViewById(R.id.button_ok); @@ -69,6 +72,7 @@ public class CheatDetailsFragment extends Fragment mViewModel.getSelectedCheat().observe(getViewLifecycleOwner(), this::onSelectedCheatUpdated); mViewModel.getIsEditing().observe(getViewLifecycleOwner(), this::onIsEditingUpdated); + mButtonDelete.setOnClickListener(this::onDeleteClicked); mButtonEdit.setOnClickListener((v) -> mViewModel.setIsEditing(true)); mButtonCancel.setOnClickListener((v) -> { @@ -84,6 +88,16 @@ public class CheatDetailsFragment extends Fragment mEditCode.setError(null); } + private void onDeleteClicked(View view) + { + AlertDialog.Builder builder = + new AlertDialog.Builder(requireContext(), R.style.DolphinDialogBase); + builder.setMessage(getString(R.string.cheats_delete_confirmation, mCheat.getName())); + builder.setPositiveButton(R.string.yes, (dialog, i) -> mViewModel.deleteSelectedCheat()); + builder.setNegativeButton(R.string.no, null); + builder.show(); + } + private void onOkClicked(View view) { clearEditErrors(); @@ -138,6 +152,7 @@ public class CheatDetailsFragment extends Fragment mEditNotes.setVisibility(notesVisibility); boolean userDefined = cheat != null && cheat.getUserDefined(); + mButtonDelete.setEnabled(userDefined); mButtonEdit.setEnabled(userDefined); // If the fragment was recreated while editing a cheat, it's vital that we @@ -162,6 +177,7 @@ public class CheatDetailsFragment extends Fragment mEditNotes.setEnabled(isEditing); mEditCode.setEnabled(isEditing); + mButtonDelete.setVisibility(isEditing ? View.GONE : View.VISIBLE); mButtonEdit.setVisibility(isEditing ? View.GONE : View.VISIBLE); mButtonCancel.setVisibility(isEditing ? View.VISIBLE : View.GONE); mButtonOk.setVisibility(isEditing ? View.VISIBLE : View.GONE); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index 3406e1b692..c12cd58c9f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -37,6 +37,12 @@ public class CheatsAdapter extends RecyclerView.Adapter if (position != null) notifyItemChanged(position); }); + + mViewModel.getCheatDeletedEvent().observe(owner, (position) -> + { + if (position != null) + notifyItemRemoved(position); + }); } @NonNull -- cgit v1.2.3 From 215492152c6b5a20c600e727afcbef9ceace666a Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 11 Aug 2021 13:40:12 +0200 Subject: Android: Show warning when Enable Cheats is off --- .../dolphinemu/dialogs/GamePropertiesDialog.java | 2 +- .../features/cheats/ui/CheatWarningFragment.java | 59 ++++++++++++++++++++++ .../features/cheats/ui/CheatsActivity.java | 14 ++++- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java index d35151e7e2..15e6c8bc31 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java @@ -93,7 +93,7 @@ public class GamePropertiesDialog extends DialogFragment SettingsActivity.launch(getContext(), MenuTag.SETTINGS, gameId, revision, isWii)); itemsBuilder.add(R.string.properties_edit_cheats, (dialog, i) -> - CheatsActivity.launch(getContext(), gameId, revision)); + CheatsActivity.launch(getContext(), gameId, revision, isWii)); itemsBuilder.add(R.string.properties_clear_game_settings, (dialog, i) -> clearGameSettings(gameId)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java new file mode 100644 index 0000000000..aba0bb1602 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java @@ -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 android.widget.Button; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; + +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; +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 class CheatWarningFragment extends Fragment implements View.OnClickListener +{ + private View mView; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) + { + return inflater.inflate(R.layout.fragment_cheat_warning, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) + { + mView = view; + + Button settingsButton = view.findViewById(R.id.button_settings); + settingsButton.setOnClickListener(this); + } + + @Override + public void onResume() + { + super.onResume(); + + CheatsActivity activity = (CheatsActivity) requireActivity(); + try (Settings settings = activity.loadGameSpecificSettings()) + { + boolean cheatsEnabled = BooleanSetting.MAIN_ENABLE_CHEATS.getBoolean(settings); + mView.setVisibility(cheatsEnabled ? View.GONE : View.VISIBLE); + } + } + + public void onClick(View view) + { + SettingsActivity.launch(requireContext(), MenuTag.CONFIG_GENERAL); + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index 911c54ab0e..4143457006 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -13,6 +13,7 @@ import androidx.slidingpanelayout.widget.SlidingPaneLayout; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.features.cheats.model.Cheat; import org.dolphinemu.dolphinemu.features.cheats.model.CheatsViewModel; +import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.ui.TwoPaneOnBackPressedCallback; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; @@ -20,18 +21,21 @@ public class CheatsActivity extends AppCompatActivity { private static final String ARG_GAME_ID = "game_id"; private static final String ARG_REVISION = "revision"; + private static final String ARG_IS_WII = "is_wii"; private String mGameId; private int mRevision; + private boolean mIsWii; private CheatsViewModel mViewModel; private SlidingPaneLayout mSlidingPaneLayout; - public static void launch(Context context, String gameId, int revision) + public static void launch(Context context, String gameId, int revision, boolean isWii) { Intent intent = new Intent(context, CheatsActivity.class); intent.putExtra(ARG_GAME_ID, gameId); intent.putExtra(ARG_REVISION, revision); + intent.putExtra(ARG_IS_WII, isWii); context.startActivity(intent); } @@ -45,6 +49,7 @@ public class CheatsActivity extends AppCompatActivity Intent intent = getIntent(); mGameId = intent.getStringExtra(ARG_GAME_ID); mRevision = intent.getIntExtra(ARG_REVISION, 0); + mIsWii = intent.getBooleanExtra(ARG_IS_WII, true); setTitle(getString(R.string.cheats_with_game_id, mGameId)); @@ -88,4 +93,11 @@ public class CheatsActivity extends AppCompatActivity if (open) mSlidingPaneLayout.open(); } + + public Settings loadGameSpecificSettings() + { + Settings settings = new Settings(); + settings.loadSettings(null, mGameId, mRevision, mIsWii); + return settings; + } } -- cgit v1.2.3 From 47efd3317d2cc6e317121a9ca10fb25808f85981 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 11 Aug 2021 14:49:20 +0200 Subject: Android: Fix CheatsActivity d-pad navigation Special shoutout to Android for not having RTL compatible variants of nextFocusRight and nextFocusLeft. Ideally we would have some way to block the user from using the d-pad to switch between the two panes when in portrait mode, or make the list pane act as if it's to the left of the details pane rather than the right when the details pane is open, but I don't know of a good way to do this. SlidingPaneLayout doesn't really seem to have been implemented with d-pad navigation in mind. Thankfully, landscape is the most important use case for gamepads. --- .../features/cheats/ui/CheatDetailsFragment.java | 25 +++++-- .../features/cheats/ui/CheatListFragment.java | 2 +- .../features/cheats/ui/CheatWarningFragment.java | 4 ++ .../features/cheats/ui/CheatsActivity.java | 76 ++++++++++++++++++++++ .../features/cheats/ui/CheatsAdapter.java | 19 ++++-- 5 files changed, 115 insertions(+), 11 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java index 421b0aa094..2ab6bb31b0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatDetailsFragment.java @@ -73,13 +73,12 @@ public class CheatDetailsFragment extends Fragment mViewModel.getIsEditing().observe(getViewLifecycleOwner(), this::onIsEditingUpdated); mButtonDelete.setOnClickListener(this::onDeleteClicked); - mButtonEdit.setOnClickListener((v) -> mViewModel.setIsEditing(true)); - mButtonCancel.setOnClickListener((v) -> - { - mViewModel.setIsEditing(false); - onSelectedCheatUpdated(mCheat); - }); + mButtonEdit.setOnClickListener(this::onEditClicked); + mButtonCancel.setOnClickListener(this::onCancelClicked); mButtonOk.setOnClickListener(this::onOkClicked); + + CheatsActivity.setOnFocusChangeListenerRecursively(view, + (v, hasFocus) -> activity.onDetailsViewFocusChange(hasFocus)); } private void clearEditErrors() @@ -98,6 +97,19 @@ public class CheatDetailsFragment extends Fragment builder.show(); } + private void onEditClicked(View view) + { + mViewModel.setIsEditing(true); + mButtonOk.requestFocus(); + } + + private void onCancelClicked(View view) + { + mViewModel.setIsEditing(false); + onSelectedCheatUpdated(mCheat); + mButtonDelete.requestFocus(); + } + private void onOkClicked(View view) { clearEditErrors(); @@ -118,6 +130,7 @@ public class CheatDetailsFragment extends Fragment mViewModel.notifySelectedCheatChanged(); mViewModel.setIsEditing(false); } + mButtonEdit.requestFocus(); break; case Cheat.TRY_SET_FAIL_NO_NAME: mEditName.setError(getString(R.string.cheats_error_no_name)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java index 03f87f0c0c..efc446d50e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatListFragment.java @@ -36,7 +36,7 @@ public class CheatListFragment extends Fragment CheatsActivity activity = (CheatsActivity) requireActivity(); CheatsViewModel viewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); - recyclerView.setAdapter(new CheatsAdapter(getViewLifecycleOwner(), viewModel)); + recyclerView.setAdapter(new CheatsAdapter(activity, viewModel)); recyclerView.setLayoutManager(new LinearLayoutManager(activity)); recyclerView.addItemDecoration(new DividerItemDecoration(activity, null)); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java index aba0bb1602..df6d9d94b0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatWarningFragment.java @@ -37,6 +37,10 @@ public class CheatWarningFragment extends Fragment implements View.OnClickListen Button settingsButton = view.findViewById(R.id.button_settings); settingsButton.setOnClickListener(this); + + CheatsActivity activity = (CheatsActivity) requireActivity(); + CheatsActivity.setOnFocusChangeListenerRecursively(view, + (v, hasFocus) -> activity.onListViewFocusChange(hasFocus)); } @Override diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index 4143457006..42300e2b8d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -5,8 +5,12 @@ package org.dolphinemu.dolphinemu.features.cheats.ui; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.ViewCompat; import androidx.lifecycle.ViewModelProvider; import androidx.slidingpanelayout.widget.SlidingPaneLayout; @@ -18,6 +22,7 @@ import org.dolphinemu.dolphinemu.ui.TwoPaneOnBackPressedCallback; import org.dolphinemu.dolphinemu.ui.main.MainPresenter; public class CheatsActivity extends AppCompatActivity + implements SlidingPaneLayout.PanelSlideListener { private static final String ARG_GAME_ID = "game_id"; private static final String ARG_REVISION = "revision"; @@ -29,6 +34,11 @@ public class CheatsActivity extends AppCompatActivity private CheatsViewModel mViewModel; private SlidingPaneLayout mSlidingPaneLayout; + private View mCheatList; + private View mCheatDetails; + + private View mCheatListLastFocus; + private View mCheatDetailsLastFocus; public static void launch(Context context, String gameId, int revision, boolean isWii) { @@ -59,6 +69,13 @@ public class CheatsActivity extends AppCompatActivity setContentView(R.layout.activity_cheats); mSlidingPaneLayout = findViewById(R.id.sliding_pane_layout); + mCheatList = findViewById(R.id.cheat_list); + mCheatDetails = findViewById(R.id.cheat_details); + + mCheatListLastFocus = mCheatList; + mCheatDetailsLastFocus = mCheatDetails; + + mSlidingPaneLayout.addPanelSlideListener(this); getOnBackPressedDispatcher().addCallback(this, new TwoPaneOnBackPressedCallback(mSlidingPaneLayout)); @@ -77,6 +94,25 @@ public class CheatsActivity extends AppCompatActivity 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; @@ -88,6 +124,30 @@ public class CheatsActivity extends AppCompatActivity SlidingPaneLayout.LOCK_MODE_UNLOCKED : SlidingPaneLayout.LOCK_MODE_LOCKED_CLOSED); } + public void onListViewFocusChange(boolean hasFocus) + { + if (hasFocus) + { + mCheatListLastFocus = mCheatList.findFocus(); + if (mCheatListLastFocus == null) + throw new NullPointerException(); + + mSlidingPaneLayout.close(); + } + } + + public void onDetailsViewFocusChange(boolean hasFocus) + { + if (hasFocus) + { + mCheatDetailsLastFocus = mCheatDetails.findFocus(); + if (mCheatDetailsLastFocus == null) + throw new NullPointerException(); + + mSlidingPaneLayout.open(); + } + } + private void openDetailsView(boolean open) { if (open) @@ -100,4 +160,20 @@ public class CheatsActivity extends AppCompatActivity settings.loadSettings(null, mGameId, mRevision, mIsWii); return settings; } + + 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); + } + } + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index c12cd58c9f..ed230f0513 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -20,25 +20,27 @@ import java.util.ArrayList; public class CheatsAdapter extends RecyclerView.Adapter { + private final CheatsActivity mActivity; private final CheatsViewModel mViewModel; - public CheatsAdapter(LifecycleOwner owner, CheatsViewModel viewModel) + public CheatsAdapter(CheatsActivity activity, CheatsViewModel viewModel) { + mActivity = activity; mViewModel = viewModel; - mViewModel.getCheatAddedEvent().observe(owner, (position) -> + mViewModel.getCheatAddedEvent().observe(activity, (position) -> { if (position != null) notifyItemInserted(position); }); - mViewModel.getCheatChangedEvent().observe(owner, (position) -> + mViewModel.getCheatChangedEvent().observe(activity, (position) -> { if (position != null) notifyItemChanged(position); }); - mViewModel.getCheatDeletedEvent().observe(owner, (position) -> + mViewModel.getCheatDeletedEvent().observe(activity, (position) -> { if (position != null) notifyItemRemoved(position); @@ -55,12 +57,15 @@ public class CheatsAdapter extends RecyclerView.Adapter { case CheatItem.TYPE_CHEAT: View cheatView = inflater.inflate(R.layout.list_item_cheat, parent, false); + addViewListeners(cheatView); return new CheatViewHolder(cheatView); case CheatItem.TYPE_HEADER: View headerView = inflater.inflate(R.layout.list_item_header, parent, false); + addViewListeners(headerView); return new HeaderViewHolder(headerView); case CheatItem.TYPE_ACTION: View actionView = inflater.inflate(R.layout.list_item_submenu, parent, false); + addViewListeners(actionView); return new ActionViewHolder(actionView); default: throw new UnsupportedOperationException(); @@ -86,6 +91,12 @@ public class CheatsAdapter extends RecyclerView.Adapter return getItemAt(position).getType(); } + private void addViewListeners(View view) + { + CheatsActivity.setOnFocusChangeListenerRecursively(view, + (v, hasFocus) -> mActivity.onListViewFocusChange(hasFocus)); + } + private CheatItem getItemAt(int position) { // Patches -- cgit v1.2.3 From 53ae1a07252d628f33543e1a19427d4848cc7393 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Wed, 11 Aug 2021 17:17:30 +0200 Subject: Android: Add Gecko code downloading --- .../dolphinemu/dialogs/GamePropertiesDialog.java | 11 +++-- .../features/cheats/model/CheatsViewModel.java | 33 ++++++++++++++ .../features/cheats/model/GeckoCheat.java | 12 +++++ .../features/cheats/ui/ActionViewHolder.java | 11 ++++- .../features/cheats/ui/CheatItemViewHolder.java | 2 +- .../features/cheats/ui/CheatViewHolder.java | 5 ++- .../features/cheats/ui/CheatsActivity.java | 52 +++++++++++++++++++++- .../features/cheats/ui/CheatsAdapter.java | 17 ++++++- .../features/cheats/ui/HeaderViewHolder.java | 2 +- 9 files changed, 132 insertions(+), 13 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java index 15e6c8bc31..8a92ffba28 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GamePropertiesDialog.java @@ -29,7 +29,8 @@ public class GamePropertiesDialog extends DialogFragment { public static final String TAG = "GamePropertiesDialog"; private static final String ARG_PATH = "path"; - private static final String ARG_GAMEID = "game_id"; + private static final String ARG_GAME_ID = "game_id"; + private static final String ARG_GAMETDB_ID = "gametdb_id"; public static final String ARG_REVISION = "revision"; private static final String ARG_PLATFORM = "platform"; private static final String ARG_SHOULD_ALLOW_CONVERSION = "should_allow_conversion"; @@ -40,7 +41,8 @@ public class GamePropertiesDialog extends DialogFragment Bundle arguments = new Bundle(); arguments.putString(ARG_PATH, gameFile.getPath()); - arguments.putString(ARG_GAMEID, gameFile.getGameId()); + arguments.putString(ARG_GAME_ID, gameFile.getGameId()); + arguments.putString(ARG_GAMETDB_ID, gameFile.getGameTdbId()); arguments.putInt(ARG_REVISION, gameFile.getRevision()); arguments.putInt(ARG_PLATFORM, gameFile.getPlatform()); arguments.putBoolean(ARG_SHOULD_ALLOW_CONVERSION, gameFile.shouldAllowConversion()); @@ -54,7 +56,8 @@ public class GamePropertiesDialog extends DialogFragment public Dialog onCreateDialog(Bundle savedInstanceState) { final String path = requireArguments().getString(ARG_PATH); - final String gameId = requireArguments().getString(ARG_GAMEID); + final String gameId = requireArguments().getString(ARG_GAME_ID); + final String gameTdbId = requireArguments().getString(ARG_GAMETDB_ID); final int revision = requireArguments().getInt(ARG_REVISION); final int platform = requireArguments().getInt(ARG_PLATFORM); final boolean shouldAllowConversion = @@ -93,7 +96,7 @@ public class GamePropertiesDialog extends DialogFragment SettingsActivity.launch(getContext(), MenuTag.SETTINGS, gameId, revision, isWii)); itemsBuilder.add(R.string.properties_edit_cheats, (dialog, i) -> - CheatsActivity.launch(getContext(), gameId, revision, isWii)); + CheatsActivity.launch(getContext(), gameId, gameTdbId, revision, isWii)); itemsBuilder.add(R.string.properties_clear_game_settings, (dialog, i) -> clearGameSettings(gameId)); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java index 750c921c94..0238628319 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/CheatsViewModel.java @@ -21,6 +21,7 @@ public class CheatsViewModel extends ViewModel private final MutableLiveData mCheatAddedEvent = new MutableLiveData<>(null); private final MutableLiveData mCheatChangedEvent = new MutableLiveData<>(null); private final MutableLiveData mCheatDeletedEvent = new MutableLiveData<>(null); + private final MutableLiveData mGeckoCheatsDownloadedEvent = new MutableLiveData<>(null); private final MutableLiveData mOpenDetailsViewEvent = new MutableLiveData<>(false); private ArrayList mPatchCheats; @@ -236,6 +237,38 @@ public class CheatsViewModel extends ViewModel 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 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 getOpenDetailsViewEvent() { return mOpenDetailsViewEvent; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java index 19573723b6..4397a95d81 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/GeckoCheat.java @@ -4,6 +4,7 @@ package org.dolphinemu.dolphinemu.features.cheats.model; import androidx.annotation.Keep; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; public class GeckoCheat extends AbstractCheat { @@ -26,6 +27,12 @@ public class GeckoCheat extends AbstractCheat 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; @@ -52,6 +59,8 @@ public class GeckoCheat extends AbstractCheat 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); @@ -63,4 +72,7 @@ public class GeckoCheat extends AbstractCheat public static native GeckoCheat[] loadCodes(String gameId, int revision); public static native void saveCodes(String gameId, int revision, GeckoCheat[] codes); + + @Nullable + public static native GeckoCheat[] downloadCodes(String gameTdbId); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java index 72380f8ab1..3e99384fdd 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/ActionViewHolder.java @@ -6,6 +6,7 @@ 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.features.cheats.model.ARCheat; @@ -17,6 +18,7 @@ public class ActionViewHolder extends CheatItemViewHolder implements View.OnClic { private final TextView mName; + private CheatsActivity mActivity; private CheatsViewModel mViewModel; private int mString; private int mPosition; @@ -30,9 +32,10 @@ public class ActionViewHolder extends CheatItemViewHolder implements View.OnClic itemView.setOnClickListener(this); } - public void bind(CheatsViewModel viewModel, CheatItem item, int position) + public void bind(CheatsActivity activity, CheatItem item, int position) { - mViewModel = viewModel; + mActivity = activity; + mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); mString = item.getString(); mPosition = position; @@ -56,5 +59,9 @@ public class ActionViewHolder extends CheatItemViewHolder implements View.OnClic mViewModel.startAddingCheat(new PatchCheat(), mPosition); mViewModel.openDetailsView(); } + else if (mString == R.string.cheats_download_gecko) + { + mActivity.downloadGeckoCodes(); + } } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java index ca07d3a871..b4e38f4c1b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatItemViewHolder.java @@ -16,5 +16,5 @@ public abstract class CheatItemViewHolder extends RecyclerView.ViewHolder super(itemView); } - public abstract void bind(CheatsViewModel viewModel, CheatItem item, int position); + public abstract void bind(CheatsActivity activity, CheatItem item, int position); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java index 3f56f480cd..58e391fb01 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatViewHolder.java @@ -8,6 +8,7 @@ import android.widget.CompoundButton; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.RecyclerView.ViewHolder; import org.dolphinemu.dolphinemu.R; @@ -34,11 +35,11 @@ public class CheatViewHolder extends CheatItemViewHolder mCheckbox = itemView.findViewById(R.id.checkbox); } - public void bind(CheatsViewModel viewModel, CheatItem item, int position) + public void bind(CheatsActivity activity, CheatItem item, int position) { mCheckbox.setOnCheckedChangeListener(null); - mViewModel = viewModel; + mViewModel = new ViewModelProvider(activity).get(CheatsViewModel.class); mCheat = item.getCheat(); mPosition = position; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index 42300e2b8d..e1210a86f6 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -9,6 +9,7 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.view.ViewCompat; import androidx.lifecycle.ViewModelProvider; @@ -17,6 +18,7 @@ import androidx.slidingpanelayout.widget.SlidingPaneLayout; import org.dolphinemu.dolphinemu.R; 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; @@ -25,10 +27,12 @@ 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; @@ -40,10 +44,12 @@ public class CheatsActivity extends AppCompatActivity private View mCheatListLastFocus; private View mCheatDetailsLastFocus; - public static void launch(Context context, String gameId, int revision, boolean isWii) + 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); @@ -58,6 +64,7 @@ public class CheatsActivity extends AppCompatActivity 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); @@ -161,6 +168,49 @@ public class CheatsActivity extends AppCompatActivity return settings; } + public void downloadGeckoCodes() + { + AlertDialog progressDialog = new AlertDialog.Builder(this, R.style.DolphinDialogBase).create(); + progressDialog.setTitle(R.string.cheats_downloading); + progressDialog.setCancelable(false); + progressDialog.show(); + + new Thread(() -> + { + GeckoCheat[] codes = GeckoCheat.downloadCodes(mGameTdbId); + + runOnUiThread(() -> + { + progressDialog.dismiss(); + + if (codes == null) + { + new AlertDialog.Builder(this, R.style.DolphinDialogBase) + .setMessage(getString(R.string.cheats_download_failed)) + .setPositiveButton(R.string.ok, null) + .show(); + } + else if (codes.length == 0) + { + new AlertDialog.Builder(this, R.style.DolphinDialogBase) + .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 AlertDialog.Builder(this, R.style.DolphinDialogBase) + .setMessage(message) + .setPositiveButton(R.string.ok, null) + .show(); + } + }); + }).start(); + } + public static void setOnFocusChangeListenerRecursively(@NonNull View view, View.OnFocusChangeListener listener) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java index ed230f0513..5994feab2a 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsAdapter.java @@ -45,6 +45,16 @@ public class CheatsAdapter extends RecyclerView.Adapter 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 @@ -75,14 +85,14 @@ public class CheatsAdapter extends RecyclerView.Adapter @Override public void onBindViewHolder(@NonNull CheatItemViewHolder holder, int position) { - holder.bind(mViewModel, getItemAt(position), position); + holder.bind(mActivity, getItemAt(position), position); } @Override public int getItemCount() { return mViewModel.getARCheats().size() + mViewModel.getGeckoCheats().size() + - mViewModel.getPatchCheats().size() + 6; + mViewModel.getPatchCheats().size() + 7; } @Override @@ -144,6 +154,9 @@ public class CheatsAdapter extends RecyclerView.Adapter return new CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_add_gecko); position -= 1; + if (position == 0) + return new CheatItem(CheatItem.TYPE_ACTION, R.string.cheats_download_gecko); + throw new IndexOutOfBoundsException(); } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java index 29da69c9da..57c0edebb0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/HeaderViewHolder.java @@ -21,7 +21,7 @@ public class HeaderViewHolder extends CheatItemViewHolder mHeaderName = itemView.findViewById(R.id.text_header_name); } - public void bind(CheatsViewModel viewModel, CheatItem item, int position) + public void bind(CheatsActivity activity, CheatItem item, int position) { mHeaderName.setText(item.getString()); } -- cgit v1.2.3 From ffa25c4cf55d7ec4b3133e0597569072ea0f1b6a Mon Sep 17 00:00:00 2001 From: JosJuice Date: Fri, 13 Aug 2021 19:45:08 +0200 Subject: Android: Allow $ line when entering cheat code Port of cda442d. --- .../dolphinemu/features/cheats/model/AbstractCheat.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java index 5dc5e80b6f..9a86d58533 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/model/AbstractCheat.java @@ -12,6 +12,21 @@ public abstract class AbstractCheat implements Cheat 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; -- cgit v1.2.3 From 0ca4c6dbd4a19607b2fc82f82171226c679f58b4 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 9 Sep 2021 19:27:44 +0200 Subject: Android: Add save button to cheats activity By request. Matches the settings activity. --- .../features/cheats/ui/CheatsActivity.java | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java index e1210a86f6..9eff4d1c9e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/cheats/ui/CheatsActivity.java @@ -5,6 +5,9 @@ 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.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -93,6 +96,27 @@ public class CheatsActivity extends AppCompatActivity mViewModel.getOpenDetailsViewEvent().observe(this, this::openDetailsView); } + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_settings, menu); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + if (item.getItemId() == R.id.menu_save_exit) + { + finish(); + return true; + } + + return false; + } + @Override protected void onStop() { -- cgit v1.2.3