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 diff --git a/Source/Android/app/src/main/res/layout-ldrtl/list_item_cheat.xml b/Source/Android/app/src/main/res/layout-ldrtl/list_item_cheat.xml new file mode 100644 index 0000000000..7f1c4d524c --- /dev/null +++ b/Source/Android/app/src/main/res/layout-ldrtl/list_item_cheat.xml @@ -0,0 +1,37 @@ + + + + + + + + diff --git a/Source/Android/app/src/main/res/layout/list_item_cheat.xml b/Source/Android/app/src/main/res/layout/list_item_cheat.xml index 5d79260b68..1b663780f7 100644 --- a/Source/Android/app/src/main/res/layout/list_item_cheat.xml +++ b/Source/Android/app/src/main/res/layout/list_item_cheat.xml @@ -6,7 +6,8 @@ android:id="@+id/root" android:layout_width="match_parent" android:layout_height="wrap_content" - android:focusable="true"> + android:focusable="true" + android:nextFocusRight="@id/checkbox"> + android:focusable="true" + android:nextFocusLeft="@id/root" />