From afe29d5b1dcd69e353e48e6f791ad0b4eb630c1f Mon Sep 17 00:00:00 2001 From: SSimco <37044560+SSimco@users.noreply.github.com> Date: Fri, 25 Oct 2024 16:50:15 +0300 Subject: [PATCH] Refactored code --- .github/workflows/android_build.yml | 10 +- src/android/app/build.gradle | 2 + src/android/app/src/main/AndroidManifest.xml | 3 +- .../app/src/main/cpp/NativeEmulation.cpp | 10 +- .../app/src/main/cpp/NativeGraphicPacks.cpp | 4 +- .../java/info/cemu/Cemu/CemuApplication.java | 3 +- ...DrawableExtensions.java => Drawables.java} | 8 +- .../Cemu/emulation/EmulationActivity.java | 42 ++- .../cemu/Cemu/emulation/EmulationData.java | 6 - .../cemu/Cemu/emulation/EmulationError.java | 4 - .../Cemu/emulation/EmulationFragment.java | 82 ++--- .../emulation/EmulationTextInputEditText.java | 15 +- .../Cemu/emulation/EmulationViewModel.java | 19 -- .../cemu/Cemu/features/DocumentsProvider.java | 68 +++-- .../info/cemu/Cemu/gameview/GameAdapter.java | 4 +- .../Cemu/gameview/GameDetailsFragment.java | 21 +- .../cemu/Cemu/gameview/GameListViewModel.java | 11 +- .../gameview/GameProfileEditFragment.java | 9 +- .../cemu/Cemu/gameview/GamesFragment.java | 43 ++- .../BaseSelectionAdapter.java | 3 +- .../ButtonRecyclerViewItem.java | 3 +- .../CheckboxRecyclerViewItem.java | 4 +- .../FilterableRecyclerViewAdapter.java | 6 +- .../GenericRecyclerViewAdapter.java | 4 +- .../guibasecomponents/SelectionAdapter.java | 7 +- .../SimpleButtonRecyclerViewItem.java | 3 +- .../SingleSelectionRecyclerViewItem.java | 6 +- .../SliderRecyclerViewItem.java | 33 +- .../StringSelectionAdapter.java | 7 +- .../ToggleRecyclerViewItem.java | 4 +- .../info/cemu/Cemu/input/InputManager.java | 12 +- .../info/cemu/Cemu/input/SensorManager.java | 19 +- .../info/cemu/Cemu/inputoverlay/Button.java | 4 +- .../cemu/Cemu/inputoverlay/DPadInput.java | 32 +- .../InputOverlaySettingsProvider.java | 18 +- .../inputoverlay/InputOverlaySurfaceView.java | 20 +- .../info/cemu/Cemu/inputoverlay/Joystick.java | 4 +- .../main/java/info/cemu/Cemu/io/Files.java | 18 ++ .../Cemu/nativeinterface/FileCallbacks.java | 28 +- .../Cemu/nativeinterface/NativeEmulation.java | 2 +- .../nativeinterface/NativeGraphicPacks.java | 19 +- .../Cemu/nativeinterface/NativeInput.java | 1 + .../cemu/Cemu/settings/SettingsActivity.java | 23 +- .../settings/gamespath/GamePathsFragment.java | 4 +- .../graphicpacks/GraphicPackDataNode.java | 39 +++ .../GraphicPackItemRecyclerViewItem.java | 63 ---- .../GraphicPackListItemRecyclerViewItem.java | 88 ++++++ .../graphicpacks/GraphicPackNode.java | 14 + .../GraphicPackRecyclerViewItem.java | 14 +- .../graphicpacks/GraphicPackSectionNode.java | 87 ++++++ .../graphicpacks/GraphicPackViewModel.java | 18 ++ .../graphicpacks/GraphicPacksFragment.java | 31 +- .../GraphicPacksRecyclerViewAdapter.java | 63 ++-- .../GraphicPacksRootFragment.java | 283 +++++++----------- .../graphics/GraphicsSettingsFragment.java | 6 +- .../input/ControllerInputsDataProvider.java | 12 +- .../input/ControllerInputsFragment.java | 48 +-- .../input/EmulatedControllerTypeAdapter.java | 8 +- .../Cemu/settings/input/MotionDialog.java | 32 -- .../overlay/OverlaySettingsFragment.java | 24 +- .../app/src/main/res/drawable/button_a.xml | 20 +- .../app/src/main/res/drawable/button_b.xml | 20 +- .../app/src/main/res/drawable/button_c.xml | 20 +- .../app/src/main/res/drawable/button_home.xml | 20 +- .../app/src/main/res/drawable/button_l.xml | 18 +- .../src/main/res/drawable/button_minus.xml | 20 +- .../app/src/main/res/drawable/button_one.xml | 20 +- .../app/src/main/res/drawable/button_plus.xml | 20 +- .../app/src/main/res/drawable/button_r.xml | 18 +- .../src/main/res/drawable/button_stick.xml | 32 +- .../app/src/main/res/drawable/button_two.xml | 20 +- .../app/src/main/res/drawable/button_x.xml | 20 +- .../app/src/main/res/drawable/button_y.xml | 20 +- .../app/src/main/res/drawable/button_z.xml | 20 +- .../app/src/main/res/drawable/button_zl.xml | 18 +- .../app/src/main/res/drawable/button_zr.xml | 18 +- .../src/main/res/drawable/dpad_background.xml | 12 +- .../app/src/main/res/drawable/dpad_button.xml | 10 +- .../app/src/main/res/drawable/ic_check.xml | 10 + .../app/src/main/res/drawable/ic_download.xml | 6 +- .../app/src/main/res/drawable/ic_favorite.xml | 2 +- .../app/src/main/res/drawable/ic_move.xml | 2 +- .../app/src/main/res/drawable/ic_resize.xml | 2 +- .../app/src/main/res/drawable/ic_search.xml | 6 +- .../app/src/main/res/drawable/ic_settings.xml | 6 +- .../main/res/drawable/stick_background.xml | 20 +- .../app/src/main/res/drawable/stick_inner.xml | 12 +- .../app/src/main/res/layout/activity_main.xml | 17 +- .../src/main/res/layout/activity_settings.xml | 22 +- .../res/layout/layout_graphic_pack_item.xml | 59 +++- .../layout_graphic_pack_search_item.xml | 52 +++- src/android/app/src/main/res/menu/game.xml | 3 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 4 +- .../res/navigation/nav_settings_graph.xml | 2 +- .../app/src/main/res/values-night/themes.xml | 7 +- .../app/src/main/res/values-v23/themes.xml | 9 - .../app/src/main/res/values/strings.xml | 4 + .../app/src/main/res/values/themes.xml | 15 +- 98 files changed, 1176 insertions(+), 908 deletions(-) rename src/android/app/src/main/java/info/cemu/Cemu/drawable/{DrawableExtensions.java => Drawables.java} (79%) delete mode 100644 src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationData.java delete mode 100644 src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationError.java delete mode 100644 src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationViewModel.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/io/Files.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackDataNode.java delete mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackItemRecyclerViewItem.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackListItemRecyclerViewItem.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackNode.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackSectionNode.java create mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackViewModel.java delete mode 100644 src/android/app/src/main/java/info/cemu/Cemu/settings/input/MotionDialog.java create mode 100644 src/android/app/src/main/res/drawable/ic_check.xml delete mode 100644 src/android/app/src/main/res/values-v23/themes.xml diff --git a/.github/workflows/android_build.yml b/.github/workflows/android_build.yml index 401aa6e2..18854bc9 100644 --- a/.github/workflows/android_build.yml +++ b/.github/workflows/android_build.yml @@ -32,6 +32,7 @@ jobs: ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} if: "${{ env.ANDROID_STORE_FILE_BASE64 != '' }}" run: | + echo "BUILD_TYPE=Release" >> "$GITHUB_ENV" cd ./src/android base64 --decode <<< "${ANDROID_STORE_FILE_BASE64}" > store.jks ANDROID_STORE_FILE=$(pwd)/store.jks @@ -39,13 +40,16 @@ jobs: - name: Build Cemu for Android run: | + if [[ -z "$BUILD_TYPE" ]]; then + BUILD_TYPE=Debug + fi cd ./src/android - ./gradlew assembleRelease + ./gradlew assemble$BUILD_TYPE - name: Upload artifact uses: actions/upload-artifact@v4 with: name: cemu-android path: | - ./src/android/app/build/outputs/apk/release/*.apk - ./src/android/app/build/outputs/apk/release/*.aab + ./src/android/app/build/outputs/apk/*/*.apk + ./src/android/app/build/outputs/apk/*/*.aab diff --git a/src/android/app/build.gradle b/src/android/app/build.gradle index 423a930c..1e37b744 100644 --- a/src/android/app/build.gradle +++ b/src/android/app/build.gradle @@ -19,6 +19,7 @@ android { versionName "1.0" ndk { // abiFilters("x86_64", "arm64-v8a") + //noinspection ChromeOsAbiSupport abiFilters("arm64-v8a") } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -41,6 +42,7 @@ android { if (releaseStoreFile != null) { signingConfig signingConfigs.release } else { + resValue("string", "app_name", "Cemu debug") signingConfig signingConfigs.debug } } diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 82a6da67..d9786abe 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -57,8 +57,7 @@ android:parentActivityName=".MainActivity" /> + android:exported="true"> diff --git a/src/android/app/src/main/cpp/NativeEmulation.cpp b/src/android/app/src/main/cpp/NativeEmulation.cpp index 8ae26200..ec89504d 100644 --- a/src/android/app/src/main/cpp/NativeEmulation.cpp +++ b/src/android/app/src/main/cpp/NativeEmulation.cpp @@ -181,13 +181,15 @@ Java_info_cemu_Cemu_nativeinterface_NativeEmulation_initializeEmulation([[maybe_ } extern "C" [[maybe_unused]] JNIEXPORT void JNICALL -Java_info_cemu_Cemu_nativeinterface_NativeEmulation_initializerRenderer(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject testSurface) +Java_info_cemu_Cemu_nativeinterface_NativeEmulation_initializeRenderer(JNIEnv* env, [[maybe_unused]] jclass clazz, jobject j_testSurface) { JNIUtils::handleNativeException(env, [&]() { - cemu_assert_debug(testSurface != nullptr); - // TODO: cleanup surface - GuiSystem::getWindowInfo().window_main.surface = ANativeWindow_fromSurface(env, testSurface); + cemu_assert_debug(j_testSurface != nullptr); + ANativeWindow* testSurface = ANativeWindow_fromSurface(env, j_testSurface); + GuiSystem::getWindowInfo().window_main.surface = testSurface; g_renderer = std::make_unique(); + GuiSystem::getWindowInfo().window_main.surface = nullptr; + ANativeWindow_release(testSurface); }); } diff --git a/src/android/app/src/main/cpp/NativeGraphicPacks.cpp b/src/android/app/src/main/cpp/NativeGraphicPacks.cpp index 6190f8eb..9e733169 100644 --- a/src/android/app/src/main/cpp/NativeGraphicPacks.cpp +++ b/src/android/app/src/main/cpp/NativeGraphicPacks.cpp @@ -100,7 +100,7 @@ extern "C" [[maybe_unused]] JNIEXPORT jobject JNICALL Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_getGraphicPackBasicInfos(JNIEnv* env, [[maybe_unused]] jclass clazz) { auto graphicPackInfoClass = env->FindClass("info/cemu/Cemu/nativeinterface/NativeGraphicPacks$GraphicPackBasicInfo"); - auto graphicPackInfoCtorId = env->GetMethodID(graphicPackInfoClass, "", "(JLjava/lang/String;Ljava/util/ArrayList;)V"); + auto graphicPackInfoCtorId = env->GetMethodID(graphicPackInfoClass, "", "(JLjava/lang/String;ZLjava/util/ArrayList;)V"); std::vector graphicPackInfoJObjects; for (auto&& graphicPack : NativeGraphicPacks::s_graphicPacks) @@ -108,7 +108,7 @@ Java_info_cemu_Cemu_nativeinterface_NativeGraphicPacks_getGraphicPackBasicInfos( jstring virtualPath = env->NewStringUTF(graphicPack.second->GetVirtualPath().c_str()); jlong id = graphicPack.first; jobject titleIds = JNIUtils::createJavaLongArrayList(env, graphicPack.second->GetTitleIds()); - jobject jGraphicPack = env->NewObject(graphicPackInfoClass, graphicPackInfoCtorId, id, virtualPath, titleIds); + jobject jGraphicPack = env->NewObject(graphicPackInfoClass, graphicPackInfoCtorId, id, virtualPath, graphicPack.second->IsEnabled(), titleIds); graphicPackInfoJObjects.push_back(jGraphicPack); } return JNIUtils::createArrayList(env, graphicPackInfoJObjects); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java b/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java index be3e2661..3601e155 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/CemuApplication.java @@ -26,8 +26,9 @@ public class CemuApplication extends Application { public File getInternalFolder() { var externalFilesDir = getExternalFilesDir(null); - if (externalFilesDir != null) + if (externalFilesDir != null) { return externalFilesDir; + } return getFilesDir(); } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/drawable/DrawableExtensions.java b/src/android/app/src/main/java/info/cemu/Cemu/drawable/Drawables.java similarity index 79% rename from src/android/app/src/main/java/info/cemu/Cemu/drawable/DrawableExtensions.java rename to src/android/app/src/main/java/info/cemu/Cemu/drawable/Drawables.java index 4d66c19b..7fbd1292 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/drawable/DrawableExtensions.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/drawable/Drawables.java @@ -7,7 +7,7 @@ import android.graphics.drawable.Drawable; import java.util.Objects; -public class DrawableExtensions { +public class Drawables { private static final ColorMatrix INVERTED_COLOR_MATRIX = new ColorMatrix( new float[]{ -1, 0, 0, 0, 255, @@ -18,14 +18,12 @@ public class DrawableExtensions { ); public static Drawable getInvertedDrawable(Drawable drawable, Resources resources) { - var newDrawable = Objects.requireNonNull(drawable.getConstantState()) - .newDrawable(resources); + var newDrawable = Objects.requireNonNull(drawable.getConstantState()).newDrawable(resources); return applyInvertedColorTransform(newDrawable); } public static Drawable applyInvertedColorTransform(Drawable drawable) { - drawable = drawable.mutate(); - drawable.setColorFilter(new ColorMatrixColorFilter(INVERTED_COLOR_MATRIX)); + drawable.mutate().setColorFilter(new ColorMatrixColorFilter(INVERTED_COLOR_MATRIX)); return drawable; } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationActivity.java index 1dc1c7e7..8d23a9fd 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationActivity.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationActivity.java @@ -14,8 +14,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsControllerCompat; -import androidx.lifecycle.Observer; -import androidx.lifecycle.ViewModelProvider; import com.google.android.material.dialog.MaterialAlertDialogBuilder; @@ -27,7 +25,7 @@ import info.cemu.Cemu.databinding.ActivityEmulationBinding; import info.cemu.Cemu.input.InputManager; import info.cemu.Cemu.nativeinterface.NativeSwkbd; -public class EmulationActivity extends AppCompatActivity implements Observer { +public class EmulationActivity extends AppCompatActivity { private boolean hasEmulationError; public static final String EXTRA_LAUNCH_PATH = BuildConfig.APPLICATION_ID + ".LaunchPath"; private final InputManager inputManager = new InputManager(); @@ -39,8 +37,9 @@ public class EmulationActivity extends AppCompatActivity implements Observer { var inputEditTextLayout = emulationActivityInstance.getLayoutInflater().inflate(R.layout.layout_emulation_input, null); @@ -55,8 +54,9 @@ public class EmulationActivity extends AppCompatActivity implements Observer inputEditText.onFinishedEdit()); inputEditText.setOnTextChangedListener(s -> doneButton.setEnabled(s.length() > 0)); - if (maxLength > 0) + if (maxLength > 0) { inputEditText.appendFilter(new InputFilter.LengthFilter(maxLength)); + } emulationActivityInstance.emulationTextInputDialog = dialog; }); } @@ -66,8 +66,9 @@ public class EmulationActivity extends AppCompatActivity implements Observer quit()) .setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel()) - .create() .show(); } - private void onEmulationError(EmulationError emulationError) { + private void onEmulationError(String emulationError) { + if (hasEmulationError) { + return; + } + hasEmulationError = true; MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); builder.setTitle(R.string.error) - .setMessage(emulationError.errorMessage()) + .setMessage(emulationError) .setNeutralButton(R.string.quit, (dialog, which) -> dialog.dismiss()) .setOnDismissListener(dialog -> quit()) - .create() .show(); } @@ -158,12 +162,4 @@ public class EmulationActivity extends AppCompatActivity implements Observer emulationError) { -} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationError.java b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationError.java deleted file mode 100644 index d7c5f14f..00000000 --- a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationError.java +++ /dev/null @@ -1,4 +0,0 @@ -package info.cemu.Cemu.emulation; - -public record EmulationError(String errorMessage) { -} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java index 9b94aa26..a4dd4ebd 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationFragment.java @@ -20,14 +20,13 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.widget.PopupMenu; import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProvider; -import info.cemu.Cemu.nativeinterface.NativeEmulation; import info.cemu.Cemu.R; import info.cemu.Cemu.databinding.FragmentEmulationBinding; import info.cemu.Cemu.input.SensorManager; import info.cemu.Cemu.inputoverlay.InputOverlaySettingsProvider; import info.cemu.Cemu.inputoverlay.InputOverlaySurfaceView; +import info.cemu.Cemu.nativeinterface.NativeEmulation; import info.cemu.Cemu.nativeinterface.NativeException; import info.cemu.Cemu.nativeinterface.NativeInput; @@ -102,43 +101,48 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC } } + public interface OnEmulationErrorCallback { + void onEmulationError(String errorMessage); + } + private final String launchPath; private boolean isGameRunning; private SurfaceView padCanvas; - private SurfaceTexture testSurfaceTexture; - private Surface testSurface; private Toast toast; private FragmentEmulationBinding binding; private boolean isMotionEnabled; private PopupMenu settingsMenu; private InputOverlaySurfaceView inputOverlaySurfaceView; private SensorManager sensorManager; - private EmulationViewModel viewModel; + private OnEmulationErrorCallback onEmulationErrorCallback; private boolean hasEmulationError; + private InputOverlaySettingsProvider.OverlaySettings overlaySettings; public EmulationFragment(String launchPath) { this.launchPath = launchPath; } - InputOverlaySettingsProvider.OverlaySettings overlaySettings; + public void setOnEmulationErrorCallback(OnEmulationErrorCallback onEmulationErrorCallback) { + this.onEmulationErrorCallback = onEmulationErrorCallback; + } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); var inputOverlaySettingsProvider = new InputOverlaySettingsProvider(requireContext()); - if (sensorManager == null) + if (sensorManager == null) { sensorManager = new SensorManager(requireContext()); + } sensorManager.setIsLandscape(getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); overlaySettings = inputOverlaySettingsProvider.getOverlaySettings(); - testSurfaceTexture = new SurfaceTexture(0); - testSurface = new Surface(testSurfaceTexture); } @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (sensorManager != null) + if (sensorManager != null) { sensorManager.setIsLandscape(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE); + } } @Override @@ -150,21 +154,23 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC @Override public void onResume() { super.onResume(); - if (isMotionEnabled) + if (isMotionEnabled) { sensorManager.startListening(); + } } @Override public void onDestroy() { super.onDestroy(); - if (sensorManager != null) + if (sensorManager != null) { sensorManager.pauseListening(); - if (testSurface != null) testSurface.release(); - if (testSurfaceTexture != null) testSurfaceTexture.release(); + } } private void createPadCanvas() { - if (padCanvas != null) return; + if (padCanvas != null) { + return; + } padCanvas = new SurfaceView(requireContext()); binding.canvasesLayout.addView( padCanvas, @@ -183,7 +189,9 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC } private void destroyPadCanvas() { - if (padCanvas == null) return; + if (padCanvas == null) { + return; + } binding.canvasesLayout.removeView(padCanvas); padCanvas = null; } @@ -223,10 +231,11 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC } if (itemId == R.id.enable_motion) { isMotionEnabled = !item.isChecked(); - if (isMotionEnabled) + if (isMotionEnabled) { sensorManager.startListening(); - else + } else { sensorManager.pauseListening(); + } item.setChecked(isMotionEnabled); return true; } @@ -248,22 +257,22 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - viewModel = new ViewModelProvider(requireActivity()).get(EmulationViewModel.class); - binding = FragmentEmulationBinding.inflate(inflater, container, false); inputOverlaySurfaceView = binding.inputOverlay; binding.moveInputsButton.setOnClickListener(v -> { - if (inputOverlaySurfaceView.getInputMode() == InputOverlaySurfaceView.InputMode.EDIT_POSITION) + if (inputOverlaySurfaceView.getInputMode() == InputOverlaySurfaceView.InputMode.EDIT_POSITION) { return; + } binding.resizeInputsButton.setAlpha(0.5f); binding.moveInputsButton.setAlpha(1.0f); toastMessage(R.string.input_mode_edit_position); inputOverlaySurfaceView.setInputMode(InputOverlaySurfaceView.InputMode.EDIT_POSITION); }); binding.resizeInputsButton.setOnClickListener(v -> { - if (inputOverlaySurfaceView.getInputMode() == InputOverlaySurfaceView.InputMode.EDIT_SIZE) + if (inputOverlaySurfaceView.getInputMode() == InputOverlaySurfaceView.InputMode.EDIT_SIZE) { return; + } binding.moveInputsButton.setAlpha(0.5f); binding.resizeInputsButton.setAlpha(1.0f); toastMessage(R.string.input_mode_edit_size); @@ -288,7 +297,11 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC } SurfaceView mainCanvas = binding.mainCanvas; try { - NativeEmulation.initializerRenderer(testSurface); + SurfaceTexture testSurfaceTexture = new SurfaceTexture(0); + Surface testSurface = new Surface(testSurfaceTexture); + NativeEmulation.initializeRenderer(testSurface); + testSurface.release(); + testSurfaceTexture.release(); } catch (NativeException exception) { onEmulationError(getString(R.string.failed_initialize_renderer_error, exception.getMessage())); return binding.getRoot(); @@ -303,8 +316,9 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { - if (hasEmulationError) + if (hasEmulationError) { return; + } if (!isGameRunning) { isGameRunning = true; startGame(); @@ -321,22 +335,22 @@ public class EmulationFragment extends Fragment implements PopupMenu.OnMenuItemC private void startGame() { int result = NativeEmulation.startGame(launchPath); - if (result == NativeEmulation.START_GAME_SUCCESSFUL) + if (result == NativeEmulation.START_GAME_SUCCESSFUL) { return; - int errorMessageId = switch (result) { - case NativeEmulation.START_GAME_ERROR_GAME_BASE_FILES_NOT_FOUND -> - R.string.game_not_found; - case NativeEmulation.START_GAME_ERROR_NO_DISC_KEY -> R.string.no_disk_key; - case NativeEmulation.START_GAME_ERROR_NO_TITLE_TIK -> R.string.no_title_tik; - default -> R.string.game_files_unknown_error; + } + String errorMessage = switch (result) { + case NativeEmulation.START_GAME_ERROR_GAME_BASE_FILES_NOT_FOUND -> getString(R.string.game_not_found); + case NativeEmulation.START_GAME_ERROR_NO_DISC_KEY -> getString(R.string.no_disk_key); + case NativeEmulation.START_GAME_ERROR_NO_TITLE_TIK -> getString(R.string.no_title_tik); + default -> getString(R.string.game_files_unknown_error, launchPath); }; onEmulationError(getString(errorMessageId)); } private void onEmulationError(String errorMessage) { hasEmulationError = true; - if (viewModel == null) - return; - viewModel.setEmulationError(new EmulationError(errorMessage)); + if (onEmulationErrorCallback != null) { + onEmulationErrorCallback.onEmulationError(errorMessage); + } } } \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationTextInputEditText.java b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationTextInputEditText.java index a0c20380..a66eabf3 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationTextInputEditText.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationTextInputEditText.java @@ -35,18 +35,21 @@ public class EmulationTextInputEditText extends TextInputEditText { public void updateText(String text) { boolean hasFocus = hasFocus(); - if (hasFocus) + if (hasFocus) { clearFocus(); + } setText(text); - if (hasFocus) + if (hasFocus) { requestFocus(); + } } public EmulationTextInputEditText(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); appendFilter((source, start, end, dest, dstart, dend) -> { - if (INPUT_PATTERN.matcher(source).matches()) + if (INPUT_PATTERN.matcher(source).matches()) { return null; + } return ""; }); setInputType(EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_NORMAL); @@ -57,10 +60,12 @@ public class EmulationTextInputEditText extends TextInputEditText { @Override public void onTextChanged(CharSequence text, int start, int before, int count) { - if (!hasFocus()) + if (!hasFocus()) { return; - if (onTextChangedListener != null) + } + if (onTextChangedListener != null) { onTextChangedListener.onTextChanged(text); + } NativeSwkbd.onTextChanged(text.toString()); } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationViewModel.java b/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationViewModel.java deleted file mode 100644 index 686532cd..00000000 --- a/src/android/app/src/main/java/info/cemu/Cemu/emulation/EmulationViewModel.java +++ /dev/null @@ -1,19 +0,0 @@ -package info.cemu.Cemu.emulation; - -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; - -import java.util.Optional; - -public class EmulationViewModel extends ViewModel { - private final MutableLiveData emulationData = new MutableLiveData<>(new EmulationData(Optional.empty())); - - public void setEmulationError(EmulationError emulationError) { - emulationData.setValue(new EmulationData(Optional.of(emulationError))); - } - - public LiveData getEmulationData() { - return emulationData; - } -} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/features/DocumentsProvider.java b/src/android/app/src/main/java/info/cemu/Cemu/features/DocumentsProvider.java index e3530ebd..e24beec2 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/features/DocumentsProvider.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/features/DocumentsProvider.java @@ -85,7 +85,9 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { @Override public boolean isChildDocument(String parentDocumentId, String documentId) { - if (parentDocumentId == null || documentId == null) return false; + if (parentDocumentId == null || documentId == null) { + return false; + } return documentId.startsWith(parentDocumentId); } @@ -95,12 +97,14 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { var newFile = resolveWithoutConflict(parentFile, displayName); if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) { - if (!newFile.mkdir()) + if (!newFile.mkdir()) { throw new FileNotFoundException("Failed to create directory"); + } } else { try { - if (!newFile.createNewFile()) + if (!newFile.createNewFile()) { throw new FileNotFoundException("Failed to create file"); + } } catch (IOException e) { throw new RuntimeException(e); } @@ -116,25 +120,31 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { deleteFolder(file); return; } - if (!file.delete()) + if (!file.delete()) { throw new FileNotFoundException("Couldn't delete document with ID " + documentId); + } } private void deleteFolder(File dirFile) throws FileNotFoundException { - if (!dirFile.isDirectory()) + if (!dirFile.isDirectory()) { return; + } var files = dirFile.listFiles(); - if (files == null) return; + if (files == null) { + return; + } for (var file : files) { if (file.isDirectory()) { deleteFolder(file); continue; } - if (!file.delete()) + if (!file.delete()) { throw new FileNotFoundException("Couldn't delete file " + file.getPath()); + } } - if (!dirFile.delete()) + if (!dirFile.delete()) { throw new FileNotFoundException("Couldn't delete file " + dirFile.getPath()); + } } @Override @@ -142,30 +152,35 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { var parent = getFile(parentDocumentId); var file = getFile(documentId); - if (!(parent.equals(file) || file.getParentFile() == null || file.getParentFile().equals(parent))) + if (!(parent.equals(file) || file.getParentFile() == null || file.getParentFile().equals(parent))) { throw new FileNotFoundException("Couldn't delete document with ID " + documentId); + } if (file.isDirectory()) { deleteFolder(file); return; } - if (!file.delete()) + if (!file.delete()) { throw new FileNotFoundException("Couldn't delete document with ID " + documentId); + } } @Override public String renameDocument(String documentId, String displayName) throws FileNotFoundException { - if (displayName == null) + if (displayName == null) { throw new FileNotFoundException("Couldn't rename document " + documentId + " as the new name is null"); + } var sourceFile = getFile(documentId); var sourceParentFile = sourceFile.getParentFile(); - if (sourceParentFile == null) + if (sourceParentFile == null) { throw new FileNotFoundException("Couldn't rename document '" + documentId + "' as it has no parent"); + } var destFile = resolve(sourceParentFile, displayName); try { - if (!sourceFile.renameTo(destFile)) + if (!sourceFile.renameTo(destFile)) { throw new FileNotFoundException("Couldn't rename document from '" + sourceFile.getName() + "' to '" + destFile.getName() + "'"); + } } catch (Exception exception) { throw new FileNotFoundException("Couldn't rename document from '" + sourceFile.getName() + "' to '" + destFile.getName() + "':" + exception.getMessage()); } @@ -180,8 +195,9 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { var newFile = resolveWithoutConflict(parent, oldFile.getName()); try { - if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true))) + if (!(newFile.createNewFile() && newFile.setWritable(true) && newFile.setReadable(true))) { throw new IOException("Couldn't create new file"); + } try (var inputStream = new FileInputStream(oldFile); var outputStream = new FileOutputStream(newFile)) { byte[] b = new byte[1024]; int len; @@ -211,10 +227,12 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { var cursor = new MatrixCursor(projection == null ? DEFAULT_DOCUMENT_PROJECTION : projection); var parent = getFile(parentDocumentId); var files = parent.listFiles(); - if (files == null) + if (files == null) { return cursor; - for (var file : files) + } + for (var file : files) { includeFile(cursor, null, file); + } return cursor; } @@ -230,15 +248,17 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { } private String copyDocument(String sourceDocumentId, String sourceParentDocumentId, String targetParentDocumentId) throws FileNotFoundException { - if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) + if (!isChildDocument(sourceParentDocumentId, sourceDocumentId)) { throw new FileNotFoundException("Couldn't copy document '" + sourceDocumentId + "' as its parent is not '" + sourceParentDocumentId + "'"); + } return copyDocument(sourceDocumentId, targetParentDocumentId); } private File resolveWithoutConflict(File originalFile, String name) { var file = resolve(originalFile, name); - if (!file.exists()) + if (!file.exists()) { return file; + } // Makes sure two files don't have the same name by adding a number to the end var noConflictId = 1; @@ -276,13 +296,15 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { .add(DocumentsContract.Document.COLUMN_MIME_TYPE, getTypeForFile(localFile)) .add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, localFile.lastModified()) .add(DocumentsContract.Document.COLUMN_FLAGS, flags); - if (localFile.equals(baseDirectory)) + if (localFile.equals(baseDirectory)) { curorRowBuilder.add(DocumentsContract.Root.COLUMN_ICON, R.mipmap.ic_launcher); + } } private String getTypeForFile(File file) { - if (file.isDirectory()) + if (file.isDirectory()) { return DocumentsContract.Document.MIME_TYPE_DIR; + } return getTypeForName(file.getName()); } @@ -291,8 +313,9 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { if (lastDot >= 0) { var extension = name.substring(lastDot + 1); var mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - if (mime != null) + if (mime != null) { return mime; + } } return "application/octect-stream"; } @@ -301,8 +324,9 @@ public class DocumentsProvider extends android.provider.DocumentsProvider { Objects.requireNonNull(documentId); if (documentId.startsWith(ROOT_ID)) { var file = resolve(baseDirectory, documentId.substring(ROOT_ID.length() + 1)); - if (!file.exists()) + if (!file.exists()) { throw new FileNotFoundException(file.getAbsolutePath() + " " + documentId + " not found"); + } return file; } else { throw new FileNotFoundException(documentId + " is not in any known root"); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameAdapter.java index 8ffa9074..d37aa8ca 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameAdapter.java @@ -79,7 +79,9 @@ public class GameAdapter extends ListAdapter { @Override public void onBindViewHolder(@NonNull GameAdapter.ViewHolder holder, int position) { Game game = getItem(position); - if (game == null) return; + if (game == null) { + return; + } holder.icon.setImageBitmap(game.icon()); holder.favoriteIcon.setVisibility(game.isFavorite() ? View.VISIBLE : View.GONE); holder.text.setText(game.name()); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameDetailsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameDetailsFragment.java index 7edd028e..e882cc9e 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameDetailsFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameDetailsFragment.java @@ -23,7 +23,7 @@ import info.cemu.Cemu.nativeinterface.NativeGameTitles; import info.cemu.Cemu.nativeinterface.NativeGameTitles.Game; public class GameDetailsFragment extends Fragment { - private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -31,10 +31,12 @@ public class GameDetailsFragment extends Fragment { var game = new ViewModelProvider(requireActivity()).get(GameViewModel.class).getGame(); binding.gameTitleName.setText(game.name()); binding.titleVersion.setText(String.valueOf(game.version())); - if (game.icon() != null) + if (game.icon() != null) { binding.titleIcon.setImageBitmap(game.icon()); - if (game.dlc() != 0) + } + if (game.dlc() != 0) { binding.titleDlc.setText(String.valueOf(game.dlc())); + } binding.titleTimePlayed.setText(getTimePlayed(game)); binding.titleLastPlayed.setText(getLastPlayedDate(game)); binding.titleId.setText(String.format("%016x", game.titleId())); @@ -44,15 +46,20 @@ public class GameDetailsFragment extends Fragment { } private String getLastPlayedDate(Game game) { - if (game.lastPlayedYear() == 0) return getString(R.string.never_played); + if (game.lastPlayedYear() == 0) { + return getString(R.string.never_played); + } LocalDate lastPlayedDate = LocalDate.of(game.lastPlayedYear(), game.lastPlayedMonth(), game.lastPlayedDay()); - return dateFormatter.format(lastPlayedDate); + return DATE_FORMATTER.format(lastPlayedDate); } private String getTimePlayed(Game game) { - if (game.minutesPlayed() == 0) return getString(R.string.never_played); - if (game.minutesPlayed() < 60) + if (game.minutesPlayed() == 0) { + return getString(R.string.never_played); + } + if (game.minutesPlayed() < 60) { return getString(R.string.minutes_played, game.minutesPlayed()); + } return getString(R.string.hours_minutes_played, game.minutesPlayed() / 60, game.minutesPlayed() % 60); } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameListViewModel.java b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameListViewModel.java index 61c400d7..a9847816 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameListViewModel.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameListViewModel.java @@ -23,6 +23,9 @@ public class GameListViewModel extends ViewModel { public GameListViewModel() { this.gamesData = new MutableLiveData<>(); NativeGameTitles.setGameTitleLoadedCallback(game -> { + if (!isGameValid(game)) { + return; + } synchronized (GameListViewModel.this) { games.add(game); gamesData.postValue(new ArrayList<>(games)); @@ -30,9 +33,15 @@ public class GameListViewModel extends ViewModel { }); } + private boolean isGameValid(Game game) { + return game.path() != null && game.name() != null; + } + public void setGameTitleFavorite(Game game, boolean isFavorite) { synchronized (this) { - if (!games.contains(game)) return; + if (!games.contains(game)) { + return; + } NativeGameTitles.setGameTitleFavorite(game.titleId(), isFavorite); games.remove(game); Game newGame = new Game( diff --git a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameProfileEditFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameProfileEditFragment.java index c748a5e6..380f2cde 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameProfileEditFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GameProfileEditFragment.java @@ -28,12 +28,9 @@ public class GameProfileEditFragment extends Fragment { private String cpuModeToString(int cpuMode) { int resourceId = switch (cpuMode) { - case NativeGameTitles.CPU_MODE_SINGLECOREINTERPRETER -> - R.string.cpu_mode_single_core_interpreter; - case NativeGameTitles.CPU_MODE_SINGLECORERECOMPILER -> - R.string.cpu_mode_single_core_recompiler; - case NativeGameTitles.CPU_MODE_MULTICORERECOMPILER -> - R.string.cpu_mode_multi_core_recompiler; + case NativeGameTitles.CPU_MODE_SINGLECOREINTERPRETER -> R.string.cpu_mode_single_core_interpreter; + case NativeGameTitles.CPU_MODE_SINGLECORERECOMPILER -> R.string.cpu_mode_single_core_recompiler; + case NativeGameTitles.CPU_MODE_MULTICORERECOMPILER -> R.string.cpu_mode_multi_core_recompiler; default -> R.string.cpu_mode_auto; }; return getString(resourceId); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GamesFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GamesFragment.java index 75a11c39..71f16715 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/gameview/GamesFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/gameview/GamesFragment.java @@ -1,7 +1,11 @@ package info.cemu.Cemu.gameview; +import android.app.PendingIntent; import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.graphics.Color; +import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -21,6 +25,7 @@ import androidx.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.color.MaterialColors; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import java.util.HashSet; @@ -47,7 +52,7 @@ public class GamesFragment extends Fragment { super.onCreate(savedInstanceState); currentGamePaths = new HashSet<>(NativeSettings.getGamesPaths()); gameAdapter = new GameAdapter(game -> { - Intent intent = new Intent(getContext(), EmulationActivity.class); + Intent intent = new Intent(requireContext(), EmulationActivity.class); intent.putExtra(EmulationActivity.EXTRA_LAUNCH_PATH, game.path()); startActivity(intent); }); @@ -74,8 +79,9 @@ public class GamesFragment extends Fragment { MenuInflater inflater = requireActivity().getMenuInflater(); inflater.inflate(R.menu.game, menu); Game selectedGame = gameAdapter.getSelectedGame(); - if (selectedGame == null) + if (selectedGame == null) { return; + } menu.findItem(R.id.favorite).setChecked(selectedGame.isFavorite()); menu.findItem(R.id.remove_shader_caches).setEnabled(NativeGameTitles.titleHasShaderCacheFiles(selectedGame.titleId())); } @@ -105,9 +111,36 @@ public class GamesFragment extends Fragment { NavHostFragment.findNavController(this).navigate(R.id.action_games_fragment_to_game_details_fragment); return true; } + if (itemId == R.id.create_shortcut) { + createShortcutForGame(game); + return true; + } return super.onContextItemSelected(item); } + private void createShortcutForGame(Game game) { + var context = requireContext(); + ShortcutManager shortcutManager = context.getSystemService(ShortcutManager.class); + if (!shortcutManager.isRequestPinShortcutSupported()) { + Toast.makeText(context, R.string.shortcut_not_supported, Toast.LENGTH_SHORT).show(); + return; + } + if (game.icon() == null) { + return; + } + Intent intent = new Intent(requireContext(), EmulationActivity.class); + intent.setAction(Intent.ACTION_VIEW); + intent.putExtra(EmulationActivity.EXTRA_LAUNCH_PATH, game.path()); + ShortcutInfo pinShortcutInfo = new ShortcutInfo.Builder(context, String.valueOf(game.titleId())) + .setShortLabel(game.name()) + .setIntent(intent) + .setIcon(Icon.createWithBitmap(game.icon())) + .build(); + Intent pinnedShortcutCallbackIntent = shortcutManager.createShortcutResultIntent(pinShortcutInfo); + PendingIntent successCallback = PendingIntent.getBroadcast(context, 0, pinnedShortcutCallbackIntent, PendingIntent.FLAG_IMMUTABLE); + shortcutManager.requestPinShortcut(pinShortcutInfo, successCallback.getIntentSender()); + } + private void removeShaderCachesForGame(Game game) { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext()); builder.setTitle(R.string.remove_shader_caches) @@ -153,7 +186,9 @@ public class GamesFragment extends Fragment { }); binding.gamesSwipeRefresh.setOnRefreshListener(() -> { - if (refreshing) return; + if (refreshing) { + return; + } refreshing = true; handler.postDelayed(() -> { binding.gamesSwipeRefresh.setRefreshing(false); @@ -161,6 +196,8 @@ public class GamesFragment extends Fragment { }, 1000); gameListViewModel.refreshGames(); }); + binding.gamesSwipeRefresh.setColorSchemeColors(MaterialColors.getColor(requireContext(), com.google.android.material.R.attr.colorOnSurfaceVariant, Color.BLACK)); + binding.gamesSwipeRefresh.setProgressBackgroundColorSchemeColor(MaterialColors.getColor(requireContext(), com.google.android.material.R.attr.colorSurfaceVariant, Color.WHITE)); recyclerView.setAdapter(gameAdapter); return binding.getRoot(); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/BaseSelectionAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/BaseSelectionAdapter.java index c922b42c..46752fb9 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/BaseSelectionAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/BaseSelectionAdapter.java @@ -34,8 +34,9 @@ public abstract class BaseSelectionAdapter extends BaseAdapter { @Override public View getView(int position, View view, ViewGroup viewGroup) { - if (view == null) + if (view == null) { view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.layout_single_selection_item, viewGroup, false); + } MaterialRadioButton radioButton = view.findViewById(R.id.single_selection_item_radio_button); radioButton.setEnabled(isEnabled(position)); setRadioButtonText(radioButton, position); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ButtonRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ButtonRecyclerViewItem.java index 5edbb15f..bee4e93a 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ButtonRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ButtonRecyclerViewItem.java @@ -46,8 +46,9 @@ public class ButtonRecyclerViewItem implements RecyclerViewItem { buttonViewHolder.text.setText(text); buttonViewHolder.description.setText(description); buttonViewHolder.itemView.setOnClickListener(view -> { - if (onButtonClickListener != null) + if (onButtonClickListener != null) { onButtonClickListener.onButtonClick(); + } }); } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/CheckboxRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/CheckboxRecyclerViewItem.java index 688f53cb..33557a35 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/CheckboxRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/CheckboxRecyclerViewItem.java @@ -55,7 +55,9 @@ public class CheckboxRecyclerViewItem implements RecyclerViewItem { checkBoxViewHolder.itemView.setOnClickListener(view -> { checked = !checkBoxViewHolder.checkBox.isChecked(); checkBoxViewHolder.checkBox.setChecked(checked); - if (onCheckedChangeListener != null) onCheckedChangeListener.onCheckChanged(checked); + if (onCheckedChangeListener != null) { + onCheckedChangeListener.onCheckChanged(checked); + } }); } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/FilterableRecyclerViewAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/FilterableRecyclerViewAdapter.java index 984831b3..c287b4d5 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/FilterableRecyclerViewAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/FilterableRecyclerViewAdapter.java @@ -28,15 +28,17 @@ public class FilterableRecyclerViewAdapter extends R this.predicate = predicate; int oldSize = filteredRecyclerViewItems.size(); filteredRecyclerViewItems = recyclerViewItems.stream().filter(predicate).collect(Collectors.toList()); - if (oldSize != filteredRecyclerViewItems.size()) + if (oldSize != filteredRecyclerViewItems.size()) { notifyDataSetChanged(); + } } public void clearPredicate() { int oldSize = filteredRecyclerViewItems.size(); filteredRecyclerViewItems = new ArrayList<>(recyclerViewItems); - if (oldSize != filteredRecyclerViewItems.size()) + if (oldSize != filteredRecyclerViewItems.size()) { notifyDataSetChanged(); + } } @NonNull diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/GenericRecyclerViewAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/GenericRecyclerViewAdapter.java index 3a1bbd64..52f153aa 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/GenericRecyclerViewAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/GenericRecyclerViewAdapter.java @@ -19,7 +19,9 @@ public class GenericRecyclerViewAdapter extends RecyclerView.Adapter extends BaseSelectionAdapter { @Override public int getPositionOf(T value) { OptionalInt optionalInt = IntStream.range(0, choiceItems.size()).filter(position -> choiceItems.get(position).choiceValue.equals(value)).findFirst(); - if (!optionalInt.isPresent()) - throw new IllegalArgumentException("value " + value + " was not found in the list of choiceItems"); - return optionalInt.getAsInt(); + if (optionalInt.isPresent()) { + return optionalInt.getAsInt(); + } + throw new IllegalArgumentException("value " + value + " was not found in the list of choiceItems"); } @Override diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SimpleButtonRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SimpleButtonRecyclerViewItem.java index b0256be3..db730a80 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SimpleButtonRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SimpleButtonRecyclerViewItem.java @@ -41,8 +41,9 @@ public class SimpleButtonRecyclerViewItem implements RecyclerViewItem { SimpleButtonRecyclerViewItem.SimpleButtonViewHolder buttonViewHolder = (SimpleButtonRecyclerViewItem.SimpleButtonViewHolder) viewHolder; buttonViewHolder.text.setText(text); buttonViewHolder.itemView.setOnClickListener(view -> { - if (onButtonClickListener != null) + if (onButtonClickListener != null) { onButtonClickListener.onButtonClick(); + } }); } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SingleSelectionRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SingleSelectionRecyclerViewItem.java index 2a1d4d3a..5ffee4b0 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SingleSelectionRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SingleSelectionRecyclerViewItem.java @@ -84,12 +84,14 @@ public class SingleSelectionRecyclerViewItem implements RecyclerViewItem { if (selectAlertDialog == null) { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(viewHolder.itemView.getContext()); selectAlertDialog = builder.setTitle(label).setAdapter(selectionAdapter, (dialogInterface, position) -> { - if (!selectionAdapter.isEnabled(position)) + if (!selectionAdapter.isEnabled(position)) { return; + } T selectedValue = selectionAdapter.getItem(position); selectionAdapter.setSelectedValue(selectedValue); - if (onItemSelectedListener != null) + if (onItemSelectedListener != null) { onItemSelectedListener.onItemSelected(selectedValue, SingleSelectionRecyclerViewItem.this); + } }).setNegativeButton(R.string.cancel, (dialogInterface, i) -> dialogInterface.dismiss()).show(); return; diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SliderRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SliderRecyclerViewItem.java index 2049a666..1eca219d 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SliderRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/SliderRecyclerViewItem.java @@ -28,22 +28,14 @@ public class SliderRecyclerViewItem implements RecyclerViewItem { } } + private final static float DEFAULT_STEP_SIZE = 1.0f; private final String label; private final float valueFrom; private final float valueTo; private float currentValue; private final OnChangeListener onChangeListener; - private LabelFormatter labelFormatter; - private float stepSize = 1.0f; - - public SliderRecyclerViewItem(String label, float valueFrom, float valueTo, float currentValue, OnChangeListener onChangeListener, LabelFormatter labelFormatter) { - this.label = label; - this.valueFrom = valueFrom; - this.valueTo = valueTo; - this.currentValue = currentValue; - this.onChangeListener = onChangeListener; - this.labelFormatter = labelFormatter; - } + private final LabelFormatter labelFormatter; + private final float stepSize; public SliderRecyclerViewItem(String label, float valueFrom, float valueTo, float currentValue, float stepSize, OnChangeListener onChangeListener, LabelFormatter labelFormatter) { this.label = label; @@ -55,12 +47,13 @@ public class SliderRecyclerViewItem implements RecyclerViewItem { this.labelFormatter = labelFormatter; } + public SliderRecyclerViewItem(String label, float valueFrom, float valueTo, float currentValue, OnChangeListener onChangeListener, LabelFormatter labelFormatter) { + this(label, valueFrom, valueTo, currentValue, DEFAULT_STEP_SIZE, onChangeListener, labelFormatter); + + } + public SliderRecyclerViewItem(String label, float valueFrom, float valueTo, float currentValue, OnChangeListener onChangeListener) { - this.label = label; - this.valueFrom = valueFrom; - this.valueTo = valueTo; - this.currentValue = currentValue; - this.onChangeListener = onChangeListener; + this(label, valueFrom, valueTo, currentValue, DEFAULT_STEP_SIZE, onChangeListener, null); } @Override @@ -72,14 +65,18 @@ public class SliderRecyclerViewItem implements RecyclerViewItem { public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, RecyclerView.Adapter adapter) { SliderViewHolder sliderViewHolder = (SliderViewHolder) viewHolder; sliderViewHolder.label.setText(label); - if (labelFormatter != null) sliderViewHolder.slider.setLabelFormatter(labelFormatter); + if (labelFormatter != null) { + sliderViewHolder.slider.setLabelFormatter(labelFormatter); + } sliderViewHolder.slider.setValueFrom(valueFrom); sliderViewHolder.slider.setValueTo(valueTo); sliderViewHolder.slider.setStepSize(stepSize); sliderViewHolder.slider.setValue(currentValue); sliderViewHolder.slider.addOnChangeListener((slider, value, fromUser) -> { currentValue = value; - if (onChangeListener != null) onChangeListener.onChange(currentValue); + if (onChangeListener != null) { + onChangeListener.onChange(currentValue); + } }); } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/StringSelectionAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/StringSelectionAdapter.java index 65a1c5a5..7a511827 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/StringSelectionAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/StringSelectionAdapter.java @@ -23,9 +23,10 @@ public class StringSelectionAdapter extends BaseSelectionAdapter { @Override public int getPositionOf(String value) { OptionalInt optionalInt = IntStream.range(0, choices.size()).filter(position -> choices.get(position).equals(value)).findFirst(); - if (!optionalInt.isPresent()) - throw new IllegalArgumentException("value " + value + " was not found in the list of choices"); - return optionalInt.getAsInt(); + if (optionalInt.isPresent()) { + return optionalInt.getAsInt(); + } + throw new IllegalArgumentException("value " + value + " was not found in the list of choices"); } @Override diff --git a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ToggleRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ToggleRecyclerViewItem.java index 7ba7aab8..eaaacf74 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ToggleRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/guibasecomponents/ToggleRecyclerViewItem.java @@ -55,7 +55,9 @@ public class ToggleRecyclerViewItem implements RecyclerViewItem { toggleViewHolder.itemView.setOnClickListener(view -> { checked = !toggleViewHolder.toggle.isChecked(); toggleViewHolder.toggle.setChecked(checked); - if (onCheckedChangeListener != null) onCheckedChangeListener.onCheckChanged(checked); + if (onCheckedChangeListener != null) { + onCheckedChangeListener.onCheckChanged(checked); + } }); } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/input/InputManager.java b/src/android/app/src/main/java/info/cemu/Cemu/input/InputManager.java index 29cb72b7..6af03671 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/input/InputManager.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/input/InputManager.java @@ -91,20 +91,24 @@ public class InputManager { } public boolean onKeyEvent(KeyEvent event) { - if (isSpecialKey(event)) + if (isSpecialKey(event)) { return false; - if (event.getDeviceId() < 0) + } + if (event.getDeviceId() < 0) { return false; + } InputDevice device = event.getDevice(); - if (!isController(device)) + if (!isController(device)) { return false; + } NativeInput.onNativeKey(device.getDescriptor(), device.getName(), event.getKeyCode(), event.getAction() == KeyEvent.ACTION_DOWN); return true; } public boolean onMotionEvent(MotionEvent event) { - if (!isMotionEventFromJoystickOrGamepad(event)) + if (!isMotionEventFromJoystickOrGamepad(event)) { return false; + } InputDevice device = event.getDevice(); int actionPointerIndex = event.getActionIndex(); for (InputDevice.MotionRange motionRange : device.getMotionRanges()) { diff --git a/src/android/app/src/main/java/info/cemu/Cemu/input/SensorManager.java b/src/android/app/src/main/java/info/cemu/Cemu/input/SensorManager.java index 6861f2b2..ed4d122c 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/input/SensorManager.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/input/SensorManager.java @@ -12,9 +12,7 @@ public class SensorManager implements SensorEventListener { private final Sensor accelerometer; private final Sensor gyroscope; private final boolean hasMotionData; - private float gyroX, gyroY, gyroZ; private float accelX, accelY, accelZ; - private boolean hasAccelData, hasGyroData; private boolean isLandscape = true; public SensorManager(Context context) { @@ -51,19 +49,14 @@ public class SensorManager implements SensorEventListener { accelX = event.values[0]; accelY = event.values[1]; accelZ = event.values[2]; - hasAccelData = true; - } - if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { - gyroX = event.values[0]; - gyroY = event.values[1]; - gyroZ = event.values[2]; - hasGyroData = true; - } - if (!hasAccelData || !hasGyroData) { return; } - hasAccelData = false; - hasGyroData = false; + if (event.sensor.getType() != Sensor.TYPE_GYROSCOPE) { + return; + } + float gyroX = event.values[0]; + float gyroY = event.values[1]; + float gyroZ = event.values[2]; if (isLandscape) { NativeInput.onMotion(event.timestamp, gyroY, gyroZ, gyroX, accelY, accelZ, accelX); return; diff --git a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Button.java b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Button.java index 5804f7c1..70e75501 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Button.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Button.java @@ -10,7 +10,7 @@ import androidx.core.content.res.ResourcesCompat; import java.util.Objects; -import info.cemu.Cemu.drawable.DrawableExtensions; +import info.cemu.Cemu.drawable.Drawables; public abstract class Button extends Input { protected Drawable iconPressed; @@ -23,7 +23,7 @@ public abstract class Button extends Input { public Button(Resources resources, @DrawableRes int buttonId, ButtonStateChangeListener buttonStateChangeListener, InputOverlaySurfaceView.OverlayButton button, InputOverlaySettingsProvider.InputOverlaySettings settings) { super(settings); iconNotPressed = Objects.requireNonNull(ResourcesCompat.getDrawable(resources, buttonId, null)); - iconPressed = DrawableExtensions.getInvertedDrawable(iconNotPressed, resources); + iconPressed = Drawables.getInvertedDrawable(iconNotPressed, resources); iconPressed.setAlpha(settings.getAlpha()); iconNotPressed.setAlpha(settings.getAlpha()); icon = iconNotPressed; diff --git a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/DPadInput.java b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/DPadInput.java index 38cbee0e..2f9b3409 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/DPadInput.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/DPadInput.java @@ -11,7 +11,7 @@ import androidx.core.content.res.ResourcesCompat; import java.util.Objects; import java.util.function.Supplier; -import info.cemu.Cemu.drawable.DrawableExtensions; +import info.cemu.Cemu.drawable.Drawables; public class DPadInput extends Input { Drawable iconDpadUp; @@ -105,7 +105,7 @@ public class DPadInput extends Input { background = Objects.requireNonNull(ResourcesCompat.getDrawable(resources, backgroundId, null)); Supplier getNotPressedButtonIcon = () -> Objects.requireNonNull(ResourcesCompat.getDrawable(resources, buttonId, null)); - Supplier getPressedButtonIcon = () -> DrawableExtensions.applyInvertedColorTransform(ResourcesCompat.getDrawable(resources, buttonId, null)); + Supplier getPressedButtonIcon = () -> Drawables.applyInvertedColorTransform(ResourcesCompat.getDrawable(resources, buttonId, null)); iconDpadUpPressed = getPressedButtonIcon.get(); iconDpadUpNotPressed = getNotPressedButtonIcon.get(); @@ -270,13 +270,27 @@ public class DPadInput extends Input { } private DpadState getStateByAngle(double angle) { - if (-0.125 * Math.PI <= angle && angle < 0.125 * Math.PI) return DpadState.RIGHT; - if (0.125 * Math.PI <= angle && angle < 0.375 * Math.PI) return DpadState.DOWN_RIGHT; - if (0.375 * Math.PI <= angle && angle < 0.625 * Math.PI) return DpadState.DOWN; - if (0.625 * Math.PI <= angle && angle < 0.875 * Math.PI) return DpadState.DOWN_LEFT; - if (-0.875 * Math.PI <= angle && angle < -0.625 * Math.PI) return DpadState.UP_LEFT; - if (-0.625 * Math.PI <= angle && angle < -0.375 * Math.PI) return DpadState.UP; - if (-0.375 * Math.PI <= angle && angle < -0.125 * Math.PI) return DpadState.UP_RIGHT; + if (-0.125 * Math.PI <= angle && angle < 0.125 * Math.PI) { + return DpadState.RIGHT; + } + if (0.125 * Math.PI <= angle && angle < 0.375 * Math.PI) { + return DpadState.DOWN_RIGHT; + } + if (0.375 * Math.PI <= angle && angle < 0.625 * Math.PI) { + return DpadState.DOWN; + } + if (0.625 * Math.PI <= angle && angle < 0.875 * Math.PI) { + return DpadState.DOWN_LEFT; + } + if (-0.875 * Math.PI <= angle && angle < -0.625 * Math.PI) { + return DpadState.UP_LEFT; + } + if (-0.625 * Math.PI <= angle && angle < -0.375 * Math.PI) { + return DpadState.UP; + } + if (-0.375 * Math.PI <= angle && angle < -0.125 * Math.PI) { + return DpadState.UP_RIGHT; + } return DpadState.LEFT; } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySettingsProvider.java b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySettingsProvider.java index d9ade8ef..0b84397f 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySettingsProvider.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySettingsProvider.java @@ -185,16 +185,14 @@ public class InputOverlaySettingsProvider { joystickCentreY - joystickRadius, rightJoystickCentreX + joystickRadius, joystickCentreY + joystickRadius); - case L_STICK -> - new Rect((int) (leftJoystickCentreX + joystickRadius * 1.5f - joystickClickRadius), - (int) (joystickCentreY + joystickRadius * 1.5f - joystickClickRadius), - (int) (leftJoystickCentreX + joystickRadius * 1.5f + joystickClickRadius), - (int) (joystickCentreY + joystickRadius * 1.5f + joystickClickRadius)); - case R_STICK -> - new Rect((int) (rightJoystickCentreX - joystickRadius * 1.5f - joystickClickRadius), - (int) (joystickCentreY + joystickRadius * 1.5f - joystickClickRadius), - (int) (rightJoystickCentreX - joystickRadius * 1.5f + joystickClickRadius), - (int) (joystickCentreY + joystickRadius * 1.5f + joystickClickRadius)); + case L_STICK -> new Rect((int) (leftJoystickCentreX + joystickRadius * 1.5f - joystickClickRadius), + (int) (joystickCentreY + joystickRadius * 1.5f - joystickClickRadius), + (int) (leftJoystickCentreX + joystickRadius * 1.5f + joystickClickRadius), + (int) (joystickCentreY + joystickRadius * 1.5f + joystickClickRadius)); + case R_STICK -> new Rect((int) (rightJoystickCentreX - joystickRadius * 1.5f - joystickClickRadius), + (int) (joystickCentreY + joystickRadius * 1.5f - joystickClickRadius), + (int) (rightJoystickCentreX - joystickRadius * 1.5f + joystickClickRadius), + (int) (joystickCentreY + joystickRadius * 1.5f + joystickClickRadius)); }; } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySurfaceView.java b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySurfaceView.java index c590124e..37582171 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySurfaceView.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/InputOverlaySurfaceView.java @@ -4,6 +4,7 @@ import android.content.Context; import android.graphics.Canvas; import android.os.VibrationEffect; import android.os.Vibrator; +import android.os.VibratorManager; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; @@ -26,7 +27,9 @@ public class InputOverlaySurfaceView extends SurfaceView implements View.OnTouch private InputMode inputMode = InputMode.DEFAULT; public void resetInputs() { - if (inputs == null) return; + if (inputs == null) { + return; + } int width = getWidth(); int height = getHeight(); for (var input : InputOverlaySettingsProvider.Input.values()) { @@ -99,11 +102,18 @@ public class InputOverlaySurfaceView extends SurfaceView implements View.OnTouch private static final int INPUTS_MIN_WIDTH_HEIGHT_DP = 40; private final int inputsMinWidthHeight; + private Vibrator getVibrator(Context context) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + VibratorManager vibratorManager = (VibratorManager) context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE); + return vibratorManager.getDefaultVibrator(); + } + return (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + } + public InputOverlaySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); - context.getSystemService(Context.VIBRATOR_SERVICE); inputsMinWidthHeight = Math.round(INPUTS_MIN_WIDTH_HEIGHT_DP * ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT)); - vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); + vibrator = getVibrator(context); setOnTouchListener(this); settingsProvider = new InputOverlaySettingsProvider(context); var overlaySettings = settingsProvider.getOverlaySettings(); @@ -549,7 +559,9 @@ public class InputOverlaySurfaceView extends SurfaceView implements View.OnTouch boolean touchEventProcessed = false; if (inputMode == InputMode.DEFAULT) { for (var input : inputs) { - if (input.onTouch(event)) touchEventProcessed = true; + if (input.onTouch(event)) { + touchEventProcessed = true; + } } } else if (inputMode == InputMode.EDIT_POSITION) { touchEventProcessed = onEditPosition(event); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Joystick.java b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Joystick.java index 4391fc0d..86660a48 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Joystick.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/inputoverlay/Joystick.java @@ -10,7 +10,7 @@ import androidx.core.content.res.ResourcesCompat; import java.util.Objects; -import info.cemu.Cemu.drawable.DrawableExtensions; +import info.cemu.Cemu.drawable.Drawables; public class Joystick extends Input { private final Drawable iconPressed; @@ -36,7 +36,7 @@ public class Joystick extends Input { super(settings); joystickBackground = Objects.requireNonNull(ResourcesCompat.getDrawable(resources, joystickBackgroundId, null)); iconNotPressed = Objects.requireNonNull(ResourcesCompat.getDrawable(resources, innerStickId, null)); - iconPressed = DrawableExtensions.getInvertedDrawable(iconNotPressed, resources); + iconPressed = Drawables.getInvertedDrawable(iconNotPressed, resources); icon = iconNotPressed; this.stickStateChangeListener = stickStateChangeListener; this.joystick = joystick; diff --git a/src/android/app/src/main/java/info/cemu/Cemu/io/Files.java b/src/android/app/src/main/java/info/cemu/Cemu/io/Files.java new file mode 100644 index 00000000..7a3411fb --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/io/Files.java @@ -0,0 +1,18 @@ +package info.cemu.Cemu.io; + +import java.io.File; +import java.io.IOException; + +public class Files { + public static void delete(File file) throws IOException { + var files = file.listFiles(); + if (files == null) { + var ignored = file.delete(); + return; + } + for (var childFile : files) { + delete(childFile); + } + var ignored = file.delete(); + } +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/FileCallbacks.java b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/FileCallbacks.java index 9132b688..c89c2b84 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/FileCallbacks.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/FileCallbacks.java @@ -7,11 +7,11 @@ import android.provider.DocumentsContract; import android.util.Log; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import info.cemu.Cemu.CemuApplication; +import info.cemu.Cemu.io.Files; public class FileCallbacks { private static final String PATH_SEPARATOR_ENCODED = "%2F"; @@ -22,15 +22,17 @@ public class FileCallbacks { private static String toCppPath(Uri uri) { String uriPath = uri.toString(); int delimiterPos = uriPath.lastIndexOf(COLON_ENCODED); - if (delimiterPos == -1) + if (delimiterPos == -1) { return uriPath; + } return uriPath.substring(0, delimiterPos) + uriPath.substring(delimiterPos).replace(PATH_SEPARATOR_ENCODED, PATH_SEPARATOR_DECODED); } private static Uri fromCppPath(String cppPath) { int delimiterPos = cppPath.lastIndexOf(COLON_ENCODED); - if (delimiterPos == -1) + if (delimiterPos == -1) { return Uri.parse(cppPath); + } return Uri.parse(cppPath.substring(0, delimiterPos) + cppPath.substring(delimiterPos).replace(PATH_SEPARATOR_DECODED, PATH_SEPARATOR_ENCODED)); } @@ -89,25 +91,7 @@ public class FileCallbacks { public static void delete(Path fileToDelete) { try { - if (!Files.exists(fileToDelete)) { - return; - } - if (Files.isRegularFile(fileToDelete)) { - Files.delete(fileToDelete); - return; - } - var files = Files.list(fileToDelete); - files.forEach(path -> { - if (Files.isDirectory(path)) { - delete(path); - } - try { - Files.delete(path); - } catch (IOException ignored) { - } - }); - files.close(); - Files.delete(fileToDelete); + Files.delete(fileToDelete.toFile()); } catch (IOException ignored) { } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeEmulation.java b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeEmulation.java index dd62b07c..1ee070b1 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeEmulation.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeEmulation.java @@ -15,7 +15,7 @@ public class NativeEmulation { public static native void setSurfaceSize(int width, int height, boolean isMainCanvas); - public static native void initializerRenderer(Surface surface); + public static native void initializeRenderer(Surface surface); public static final int START_GAME_SUCCESSFUL = 0; public static final int START_GAME_ERROR_GAME_BASE_FILES_NOT_FOUND = 1; diff --git a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeGraphicPacks.java b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeGraphicPacks.java index 485ef335..3d52adfc 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeGraphicPacks.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeGraphicPacks.java @@ -5,7 +5,10 @@ import java.util.List; import java.util.Objects; public class NativeGraphicPacks { - public record GraphicPackBasicInfo(long id, String virtualPath, ArrayList titleIds) { + public record GraphicPackBasicInfo(long id, + String virtualPath, + boolean enabled, + ArrayList titleIds) { } public static native ArrayList getGraphicPackBasicInfos(); @@ -23,10 +26,15 @@ public class NativeGraphicPacks { @Override public boolean equals(Object object) { - if (object == null) return false; - if (object == this) return true; - if (object instanceof GraphicPackPreset preset) + if (object == null) { + return false; + } + if (object == this) { + return true; + } + if (object instanceof GraphicPackPreset preset) { return this.hashCode() == preset.hashCode(); + } return false; } @@ -42,8 +50,9 @@ public class NativeGraphicPacks { } public void setActivePreset(String activePreset) { - if (presets.stream().noneMatch(s -> s.equals(activePreset))) + if (presets.stream().noneMatch(s -> s.equals(activePreset))) { throw new IllegalArgumentException("Trying to set an invalid preset: " + activePreset); + } setGraphicPackActivePreset(graphicPackId, category, activePreset); this.activePreset = activePreset; } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeInput.java b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeInput.java index a458b52e..88eb8eed 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeInput.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/nativeinterface/NativeInput.java @@ -163,6 +163,7 @@ public class NativeInput { public static native void onMotion(long timestamp, float gyroX, float gyroY, float gyroZ, float accelX, float accelY, float accelZ); public static native void setMotionEnabled(boolean motionEnabled); + public static native void onOverlayButton(int controllerIndex, int mappingId, boolean value); public static native void onOverlayAxis(int controllerIndex, int mappingId, float value); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/SettingsActivity.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/SettingsActivity.java index 0648ddfd..f74676d3 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/SettingsActivity.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/SettingsActivity.java @@ -4,31 +4,28 @@ import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.navigation.NavController; -import androidx.navigation.Navigation; +import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; +import java.util.Objects; + import info.cemu.Cemu.R; import info.cemu.Cemu.databinding.ActivitySettingsBinding; public class SettingsActivity extends AppCompatActivity { - private AppBarConfiguration appBarConfiguration; - private NavController navController; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - info.cemu.Cemu.databinding.ActivitySettingsBinding binding = ActivitySettingsBinding.inflate(getLayoutInflater()); + ActivitySettingsBinding binding = ActivitySettingsBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); setSupportActionBar(binding.toolbarSettings); - navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_settings); - appBarConfiguration = new AppBarConfiguration.Builder().build(); - NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); - } - - @Override - public boolean onSupportNavigateUp() { - return NavigationUI.navigateUp(navController, appBarConfiguration) - || super.onSupportNavigateUp(); + NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_content_settings); + NavController navController = Objects.requireNonNull(navHostFragment).getNavController(); + AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder() + .setFallbackOnNavigateUpListener(this::onSupportNavigateUp) + .build(); + NavigationUI.setupWithNavController(binding.toolbarSettings, navController, appBarConfiguration); } } \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/gamespath/GamePathsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/gamespath/GamePathsFragment.java index 39742fdb..cf8349d9 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/gamespath/GamePathsFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/gamespath/GamePathsFragment.java @@ -50,7 +50,9 @@ public class GamePathsFragment extends Fragment { Uri uri = Objects.requireNonNull(data.getData()); requireActivity().getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); DocumentFile documentFile = DocumentFile.fromTreeUri(requireContext(), uri); - if (documentFile == null) return; + if (documentFile == null) { + return; + } String gamesPath = documentFile.getUri().toString(); if (gamesPaths.stream().anyMatch(p -> p.equals(gamesPath))) { Toast.makeText(requireContext(), R.string.game_path_already_added, Toast.LENGTH_LONG).show(); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackDataNode.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackDataNode.java new file mode 100644 index 00000000..a387add8 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackDataNode.java @@ -0,0 +1,39 @@ +package info.cemu.Cemu.settings.graphicpacks; + +public class GraphicPackDataNode extends GraphicPackNode { + private final long id; + private final String path; + private final GraphicPackSectionNode parentNode; + private boolean enabled; + + public GraphicPackDataNode(long id, String name, String path, boolean enabled, boolean hasTitleIdInstalled, GraphicPackSectionNode parentNode) { + this.id = id; + this.name = name; + this.path = path; + this.titleIdInstalled = hasTitleIdInstalled; + this.parentNode = parentNode; + this.enabled = enabled; + } + + public long getId() { + return id; + } + + public String getPath() { + return path; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + if (this.enabled == enabled) { + return; + } + this.enabled = enabled; + if (parentNode != null) { + parentNode.updateEnabledCount(enabled); + } + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackItemRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackItemRecyclerViewItem.java deleted file mode 100644 index 6338d9c4..00000000 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackItemRecyclerViewItem.java +++ /dev/null @@ -1,63 +0,0 @@ -package info.cemu.Cemu.settings.graphicpacks; - -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.recyclerview.widget.RecyclerView; - -import info.cemu.Cemu.R; -import info.cemu.Cemu.guibasecomponents.RecyclerViewItem; - -public class GraphicPackItemRecyclerViewItem implements RecyclerViewItem { - - private static class HeaderViewHolder extends RecyclerView.ViewHolder { - TextView title; - ImageView icon; - - public HeaderViewHolder(View itemView) { - super(itemView); - title = itemView.findViewById(R.id.graphic_pack_text); - icon = itemView.findViewById(R.id.graphic_pack_icon); - } - } - - public interface OnClickCallback { - void onClick(); - } - - public enum GraphicPackItemType { - SECTION, - GRAPHIC_PACK, - } - - private final String text; - public final boolean hasInstalledTitleId; - private final GraphicPackItemType graphicPackItemType; - private final OnClickCallback onClickCallback; - - public GraphicPackItemRecyclerViewItem(String text, boolean hasInstalledTitleId, GraphicPackItemType graphicPackItemType, OnClickCallback onClickCallback) { - this.text = text; - this.hasInstalledTitleId = hasInstalledTitleId; - this.graphicPackItemType = graphicPackItemType; - this.onClickCallback = onClickCallback; - } - - @Override - public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) { - return new HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_graphic_pack_item, parent, false)); - } - - @Override - public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, RecyclerView.Adapter adapter) { - HeaderViewHolder headerViewHolder = (HeaderViewHolder) viewHolder; - headerViewHolder.itemView.setOnClickListener(v -> onClickCallback.onClick()); - headerViewHolder.title.setText(text); - if (graphicPackItemType == GraphicPackItemType.SECTION) - headerViewHolder.icon.setImageResource(R.drawable.ic_lists); - else if (graphicPackItemType == GraphicPackItemType.GRAPHIC_PACK) - headerViewHolder.icon.setImageResource(R.drawable.ic_package_2); - } -} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackListItemRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackListItemRecyclerViewItem.java new file mode 100644 index 00000000..be484b45 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackListItemRecyclerViewItem.java @@ -0,0 +1,88 @@ +package info.cemu.Cemu.settings.graphicpacks; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.card.MaterialCardView; +import com.google.android.material.textview.MaterialTextView; + +import java.util.Objects; + +import info.cemu.Cemu.R; +import info.cemu.Cemu.guibasecomponents.RecyclerViewItem; + +public class GraphicPackListItemRecyclerViewItem implements RecyclerViewItem { + + private static class ViewHolder extends RecyclerView.ViewHolder { + MaterialTextView title; + ImageView icon; + MaterialTextView enabledGraphicPacksCount; + ImageView enabledIcon; + MaterialCardView graphicPackExtraInfo; + + public ViewHolder(View itemView) { + super(itemView); + title = itemView.findViewById(R.id.graphic_pack_text); + icon = itemView.findViewById(R.id.graphic_pack_icon); + enabledGraphicPacksCount = itemView.findViewById(R.id.graphic_pack_enabled_count); + enabledIcon = itemView.findViewById(R.id.graphic_pack_enabled_icon); + graphicPackExtraInfo = itemView.findViewById(R.id.graphic_pack_extra_info); + } + } + + public interface OnClickCallback { + void onClick(); + } + + + private final String text; + public final boolean hasInstalledTitleId; + private final OnClickCallback onClickCallback; + private final GraphicPackNode graphicPackNode; + + public GraphicPackListItemRecyclerViewItem(GraphicPackNode node, OnClickCallback onClickCallback) { + this.text = node.getName(); + this.hasInstalledTitleId = node.hasTitleIdInstalled(); + graphicPackNode = Objects.requireNonNull(node); + this.onClickCallback = onClickCallback; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) { + return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_graphic_pack_item, parent, false)); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, RecyclerView.Adapter adapter) { + ViewHolder graphicPackViewHolder = (ViewHolder) viewHolder; + graphicPackViewHolder.itemView.setOnClickListener(v -> onClickCallback.onClick()); + graphicPackViewHolder.title.setText(text); + if (graphicPackNode instanceof GraphicPackSectionNode sectionNode) { + configureGraphicPackSection(graphicPackViewHolder, sectionNode); + } else if (graphicPackNode instanceof GraphicPackDataNode dataNode) { + configureGraphicPack(graphicPackViewHolder, dataNode); + } + } + + private void configureGraphicPackSection(ViewHolder viewHolder, GraphicPackSectionNode sectionNode) { + viewHolder.icon.setImageResource(R.drawable.ic_lists); + int enabledGraphicPacksCount = sectionNode.getEnabledGraphicPacksCount(); + if (enabledGraphicPacksCount == 0) { + return; + } + String graphicPacksCountText = enabledGraphicPacksCount >= 100 ? "99+" : String.valueOf(enabledGraphicPacksCount); + viewHolder.enabledGraphicPacksCount.setText(graphicPacksCountText); + viewHolder.enabledGraphicPacksCount.setVisibility(View.VISIBLE); + } + + private void configureGraphicPack(ViewHolder viewHolder, GraphicPackDataNode dataNode) { + viewHolder.icon.setImageResource(R.drawable.ic_package_2); + if (dataNode.isEnabled()) { + viewHolder.enabledIcon.setVisibility(View.VISIBLE); + } + } +} diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackNode.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackNode.java new file mode 100644 index 00000000..be47c8b1 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackNode.java @@ -0,0 +1,14 @@ +package info.cemu.Cemu.settings.graphicpacks; + +public abstract class GraphicPackNode { + protected boolean titleIdInstalled; + protected String name; + + public boolean hasTitleIdInstalled() { + return titleIdInstalled; + } + + public String getName() { + return name; + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackRecyclerViewItem.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackRecyclerViewItem.java index 7e3ad143..effe6605 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackRecyclerViewItem.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackRecyclerViewItem.java @@ -27,11 +27,16 @@ public class GraphicPackRecyclerViewItem implements RecyclerViewItem { } } + public interface OnCheckedChangeListener { + void onCheckedChange(boolean checked); + } private final NativeGraphicPacks.GraphicPack graphicPack; + private final OnCheckedChangeListener onCheckedChangeListener; - public GraphicPackRecyclerViewItem(NativeGraphicPacks.GraphicPack graphicPack) { + public GraphicPackRecyclerViewItem(NativeGraphicPacks.GraphicPack graphicPack, OnCheckedChangeListener onCheckedChangeListener) { this.graphicPack = graphicPack; + this.onCheckedChangeListener = onCheckedChangeListener; } @Override @@ -49,6 +54,11 @@ public class GraphicPackRecyclerViewItem implements RecyclerViewItem { graphicPackViewHolder.description.setText(R.string.graphic_pack_no_description); } graphicPackViewHolder.enableToggle.setChecked(graphicPack.isActive()); - graphicPackViewHolder.enableToggle.setOnCheckedChangeListener((materialCheckBox, isChecked) -> graphicPack.setActive(isChecked)); + graphicPackViewHolder.enableToggle.setOnCheckedChangeListener((materialCheckBox, isChecked) -> { + graphicPack.setActive(isChecked); + if (onCheckedChangeListener != null) { + onCheckedChangeListener.onCheckedChange(isChecked); + } + }); } } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackSectionNode.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackSectionNode.java new file mode 100644 index 00000000..da5526ce --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackSectionNode.java @@ -0,0 +1,87 @@ +package info.cemu.Cemu.settings.graphicpacks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import info.cemu.Cemu.nativeinterface.NativeGraphicPacks; + +public class GraphicPackSectionNode extends GraphicPackNode { + private int enabledGraphicPacksCount = 0; + private final GraphicPackSectionNode parent; + private final ArrayList children = new ArrayList<>(); + + List getChildren() { + return children; + } + + private GraphicPackSectionNode(String name, boolean hasTitleIdInstalled, GraphicPackSectionNode parent) { + this.name = name; + this.titleIdInstalled = hasTitleIdInstalled; + this.parent = parent; + } + + public GraphicPackSectionNode() { + this(null, false, null); + } + + void clear() { + children.clear(); + } + + public void addGraphicPackDataByTokens(NativeGraphicPacks.GraphicPackBasicInfo graphicPackBasicInfo, boolean hasTitleIdInstalled) { + var node = this; + var tokens = Arrays.asList(graphicPackBasicInfo.virtualPath().split("/")); + if (tokens.isEmpty()) { + return; + } + for (var token : tokens.subList(0, tokens.size() - 1)) { + node = getOrAddSectionByToken(node, token, node.children, hasTitleIdInstalled); + } + var dataNode = new GraphicPackDataNode( + graphicPackBasicInfo.id(), + tokens.get(tokens.size() - 1), + graphicPackBasicInfo.virtualPath(), + graphicPackBasicInfo.enabled(), + hasTitleIdInstalled, + node + ); + if (graphicPackBasicInfo.enabled()) { + node.updateEnabledCount(true); + } + node.children.add(dataNode); + } + + private GraphicPackSectionNode getOrAddSectionByToken(GraphicPackSectionNode parent, String token, ArrayList graphicPackNodes, boolean hasTitleIdInstalled) { + var sectionNodeOptional = graphicPackNodes.stream() + .filter(g -> (g instanceof GraphicPackSectionNode) && g.name.equals(token)) + .findFirst(); + if (sectionNodeOptional.isPresent()) { + return (GraphicPackSectionNode) sectionNodeOptional.get(); + } + var sectionNode = new GraphicPackSectionNode(token, hasTitleIdInstalled, parent); + graphicPackNodes.add(sectionNode); + return sectionNode; + } + + public void sort() { + children.forEach(node -> { + if (node instanceof GraphicPackSectionNode sectionNode) { + sectionNode.sort(); + } + }); + children.sort(Comparator.comparing(o -> o.name)); + } + + public void updateEnabledCount(boolean enabled) { + enabledGraphicPacksCount = Math.max(0, enabledGraphicPacksCount + (enabled ? 1 : -1)); + if (parent != null) { + parent.updateEnabledCount(enabled); + } + } + + public int getEnabledGraphicPacksCount() { + return enabledGraphicPacksCount; + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackViewModel.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackViewModel.java new file mode 100644 index 00000000..7a64b7cd --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPackViewModel.java @@ -0,0 +1,18 @@ +package info.cemu.Cemu.settings.graphicpacks; + +import androidx.lifecycle.ViewModel; + +public class GraphicPackViewModel extends ViewModel { + private GraphicPackNode graphicPackNode; + + public GraphicPackViewModel() { + } + + public GraphicPackNode getGraphicPackNode() { + return graphicPackNode; + } + + public void setGraphicPackNode(GraphicPackNode graphicPackNode) { + this.graphicPackNode = graphicPackNode; + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksFragment.java index 7d2723da..9931ef7c 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksFragment.java @@ -25,17 +25,17 @@ import info.cemu.Cemu.nativeinterface.NativeGraphicPacks; public class GraphicPacksFragment extends Fragment { private final GenericRecyclerViewAdapter genericRecyclerViewAdapter = new GenericRecyclerViewAdapter(); - private GraphicPacksRootFragment.GraphicPackViewModel graphicPackPreviousViewModel; - private GraphicPacksRootFragment.GraphicPackViewModel graphicPackCurrentViewModel; + private GraphicPackViewModel graphicPackPreviousViewModel; + private GraphicPackViewModel graphicPackCurrentViewModel; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); NavController navController = NavHostFragment.findNavController(this); graphicPackPreviousViewModel = new ViewModelProvider(Objects.requireNonNull(navController.getPreviousBackStackEntry())) - .get(GraphicPacksRootFragment.GraphicPackViewModel.class); + .get(GraphicPackViewModel.class); graphicPackCurrentViewModel = new ViewModelProvider(Objects.requireNonNull(navController.getCurrentBackStackEntry())) - .get(GraphicPacksRootFragment.GraphicPackViewModel.class); + .get(GraphicPackViewModel.class); fillData(); } @@ -50,21 +50,16 @@ public class GraphicPacksFragment extends Fragment { private void fillData() { var graphicNode = graphicPackPreviousViewModel.getGraphicPackNode(); - if (graphicNode instanceof GraphicPacksRootFragment.GraphicPackSectionNode sectionNode) { + if (graphicNode instanceof GraphicPackSectionNode sectionNode) { fillData(sectionNode); - } else if (graphicNode instanceof GraphicPacksRootFragment.GraphicPackDataNode dataNode) { + } else if (graphicNode instanceof GraphicPackDataNode dataNode) { fillData(dataNode); } } - private void fillData(GraphicPacksRootFragment.GraphicPackSectionNode graphicPackSectionNode) { - graphicPackSectionNode.children.forEach(node -> { - var graphicPackItemType = node instanceof GraphicPacksRootFragment.GraphicPackSectionNode - ? GraphicPackItemRecyclerViewItem.GraphicPackItemType.SECTION - : GraphicPackItemRecyclerViewItem.GraphicPackItemType.GRAPHIC_PACK; - GraphicPackItemRecyclerViewItem graphicPackItemRecyclerViewItem = new GraphicPackItemRecyclerViewItem(node.name, - node.hasTitleIdInstalled(), - graphicPackItemType, + private void fillData(GraphicPackSectionNode graphicPackSectionNode) { + graphicPackSectionNode.getChildren().forEach(node -> { + GraphicPackListItemRecyclerViewItem graphicPackItemRecyclerViewItem = new GraphicPackListItemRecyclerViewItem(node, () -> { graphicPackCurrentViewModel.setGraphicPackNode(node); Bundle bundle = new Bundle(); @@ -76,10 +71,10 @@ public class GraphicPacksFragment extends Fragment { }); } - private void fillData(GraphicPacksRootFragment.GraphicPackDataNode graphicPackDataNode) { + private void fillData(GraphicPackDataNode graphicPackDataNode) { genericRecyclerViewAdapter.clearRecyclerViewItems(); var graphicPack = NativeGraphicPacks.getGraphicPack(graphicPackDataNode.getId()); - genericRecyclerViewAdapter.addRecyclerViewItem(new GraphicPackRecyclerViewItem(graphicPack)); + genericRecyclerViewAdapter.addRecyclerViewItem(new GraphicPackRecyclerViewItem(graphicPack, graphicPackDataNode::setEnabled)); fillPresets(graphicPack); } @@ -88,7 +83,9 @@ public class GraphicPacksFragment extends Fragment { IntStream.range(0, graphicPack.presets.size()).forEach(index -> { var graphicPackPreset = graphicPack.presets.get(index); String category = graphicPackPreset.getCategory(); - if (category == null) category = getString(R.string.active_preset_category); + if (category == null) { + category = getString(R.string.active_preset_category); + } var recyclerViewItem = new SingleSelectionRecyclerViewItem<>( category, graphicPackPreset.getActivePreset(), diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRecyclerViewAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRecyclerViewAdapter.java index b533bffd..4593f507 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRecyclerViewAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRecyclerViewAdapter.java @@ -5,11 +5,13 @@ import static java.util.regex.Pattern.CASE_INSENSITIVE; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.card.MaterialCardView; +import com.google.android.material.textview.MaterialTextView; + import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -22,15 +24,16 @@ import info.cemu.Cemu.guibasecomponents.RecyclerViewItem; public class GraphicPacksRecyclerViewAdapter extends RecyclerView.Adapter { private static class GraphicPackSearchItemRecyclerViewItem implements RecyclerViewItem { + private static class ViewHolder extends RecyclerView.ViewHolder { + MaterialTextView name; + MaterialTextView path; + MaterialCardView enabledIcon; - private static class HeaderViewHolder extends RecyclerView.ViewHolder { - TextView name; - TextView path; - - public HeaderViewHolder(View itemView) { + public ViewHolder(View itemView) { super(itemView); name = itemView.findViewById(R.id.graphic_pack_search_name); path = itemView.findViewById(R.id.graphic_pack_search_path); + enabledIcon = itemView.findViewById(R.id.graphic_pack_enabled_icon); } } @@ -38,34 +41,35 @@ public class GraphicPacksRecyclerViewAdapter extends RecyclerView.Adapter adapter) { - HeaderViewHolder headerViewHolder = (HeaderViewHolder) viewHolder; - headerViewHolder.itemView.setOnClickListener(v -> onClickCallback.onClick()); - headerViewHolder.name.setText(name); - headerViewHolder.path.setText(path); + ViewHolder graphicPackViewHolder = (ViewHolder) viewHolder; + graphicPackViewHolder.itemView.setOnClickListener(v -> onClickCallback.onClick()); + graphicPackViewHolder.name.setText(dataNode.getName()); + graphicPackViewHolder.path.setText(dataNode.getPath()); + if (dataNode.isEnabled()) { + graphicPackViewHolder.enabledIcon.setVisibility(View.VISIBLE); + } } } public interface OnItemClickCallback { - void onClick(GraphicPacksRootFragment.GraphicPackDataNode graphicPackDataNode); + void onClick(GraphicPackDataNode graphicPackDataNode); } private final OnItemClickCallback onItemClickCallback; @@ -84,27 +88,27 @@ public class GraphicPacksRecyclerViewAdapter extends RecyclerView.Adapter graphicPackDataNodes) { + public void setItems(List graphicPackDataNodes) { clearItems(); recyclerViewItems = graphicPackDataNodes.stream() - .sorted(Comparator.comparing(GraphicPacksRootFragment.GraphicPackDataNode::getPath)) + .sorted(Comparator.comparing(GraphicPackDataNode::getPath)) .map(graphicPackDataNode -> new GraphicPackSearchItemRecyclerViewItem( - graphicPackDataNode.getName(), - graphicPackDataNode.getPath(), - graphicPackDataNode.hasTitleIdInstalled(), + graphicPackDataNode, () -> GraphicPacksRecyclerViewAdapter.this.onItemClickCallback.onClick(graphicPackDataNode) )).collect(Collectors.toList()); filteredViewItems = recyclerViewItems; - if (showInstalledGamesOnly) + if (showInstalledGamesOnly) { filteredViewItems = filteredViewItems.stream().filter(g -> g.hasInstalledTitleId).collect(Collectors.toList()); + } notifyItemRangeInserted(0, filteredViewItems.size()); } private void resetFilteredItems() { filteredViewItems = recyclerViewItems; - if (showInstalledGamesOnly) + if (showInstalledGamesOnly) { filteredViewItems = filteredViewItems.stream().filter(g -> g.hasInstalledTitleId).collect(Collectors.toList()); + } notifyDataSetChanged(); } @@ -126,15 +130,18 @@ public class GraphicPacksRecyclerViewAdapter extends RecyclerView.Adapter g.hasInstalledTitleId); - filteredData = filteredData.filter(g -> regex.matcher(g.path).matches()); + } + filteredData = filteredData.filter(g -> regex.matcher(g.dataNode.getPath()).matches()); filteredViewItems = filteredData.collect(Collectors.toList()); notifyDataSetChanged(); } public void clearItems() { - if (filteredViewItems.isEmpty()) return; + if (filteredViewItems.isEmpty()) { + return; + } int itemsCount = filteredViewItems.size(); filteredViewItems.clear(); recyclerViewItems.clear(); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRootFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRootFragment.java index 179d9a4a..dd043a7b 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRootFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphicpacks/GraphicPacksRootFragment.java @@ -16,9 +16,9 @@ import androidx.appcompat.widget.SearchView; import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import androidx.navigation.fragment.NavHostFragment; +import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; @@ -28,20 +28,17 @@ import org.json.JSONObject; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; import info.cemu.Cemu.R; import info.cemu.Cemu.databinding.LayoutGenericRecyclerViewBinding; +import info.cemu.Cemu.io.Files; import info.cemu.Cemu.guibasecomponents.FilterableRecyclerViewAdapter; -import info.cemu.Cemu.nativeinterface.FileCallbacks; import info.cemu.Cemu.nativeinterface.NativeGameTitles; import info.cemu.Cemu.nativeinterface.NativeGraphicPacks; import info.cemu.Cemu.zip.Zip; @@ -52,102 +49,13 @@ import okhttp3.Request; import okhttp3.Response; import okhttp3.ResponseBody; -public class GraphicPacksRootFragment extends Fragment { - public abstract static class GraphicPackNode { - protected boolean titleIdInstalled; - protected String name; - - public boolean hasTitleIdInstalled() { - return titleIdInstalled; - } - - public String getName() { - return name; - } - } - - public static class GraphicPackDataNode extends GraphicPackNode { - private final long id; - private final String path; - - public GraphicPackDataNode(long id, String name, String path, boolean hasTitleIdInstalled) { - this.id = id; - this.name = name; - this.path = path; - this.titleIdInstalled = hasTitleIdInstalled; - } - - public long getId() { - return id; - } - - public String getPath() { - return path; - } - } - - public static class GraphicPackSectionNode extends GraphicPackNode { - ArrayList children = new ArrayList<>(); - - public GraphicPackSectionNode(String name, boolean hasTitleIdInstalled) { - this.name = name; - this.titleIdInstalled = hasTitleIdInstalled; - } - - public GraphicPackSectionNode() { - } - - void clear() { - children.clear(); - } - - private void addGraphicPackDataByTokens(List tokens, GraphicPackDataNode dataNode) { - var node = this; - for (var token : tokens) { - node = getOrAddSectionByToken(token, node.children, dataNode.titleIdInstalled); - } - node.children.add(dataNode); - } - - private GraphicPackSectionNode getOrAddSectionByToken(String token, ArrayList graphicPackNodes, boolean hasTitleIdInstalled) { - var sectionNodeOptional = graphicPackNodes.stream() - .filter(g -> (g instanceof GraphicPackSectionNode) && g.name.equals(token)) - .findFirst(); - if (!sectionNodeOptional.isPresent()) { - var sectionNode = new GraphicPackSectionNode(token, hasTitleIdInstalled); - graphicPackNodes.add(sectionNode); - return sectionNode; - } - return (GraphicPackSectionNode) sectionNodeOptional.get(); - } - - private void sort() { - children.forEach(node -> { - if (node instanceof GraphicPackSectionNode sectionNode) sectionNode.sort(); - }); - children.sort(Comparator.comparing(o -> o.name)); - } - } - - public static class GraphicPackViewModel extends ViewModel { - private GraphicPackNode graphicPackNode; - - public GraphicPackViewModel() { - } - - public GraphicPackNode getGraphicPackNode() { - return graphicPackNode; - } - - public void setGraphicPackNode(GraphicPackNode graphicPackNode) { - this.graphicPackNode = graphicPackNode; - } - } - +public class GraphicPacksRootFragment extends Fragment implements MenuProvider { + private boolean installedOnly = true; private final ArrayList installedTitleIds = NativeGameTitles.getInstalledGamesTitleIds(); private final GraphicPackSectionNode graphicPackSectionRootNode = new GraphicPackSectionNode(); - private final FilterableRecyclerViewAdapter genericRecyclerViewAdapter = new FilterableRecyclerViewAdapter<>(); + private final FilterableRecyclerViewAdapter genericRecyclerViewAdapter = new FilterableRecyclerViewAdapter<>(); private GraphicPackViewModel graphicPackViewModel; + private RecyclerView recyclerView; private final GraphicPacksRecyclerViewAdapter graphicPacksRecyclerViewAdapter = new GraphicPacksRecyclerViewAdapter(graphicPackDataNode -> { graphicPackViewModel.setGraphicPackNode(graphicPackDataNode); Bundle bundle = new Bundle(); @@ -163,104 +71,117 @@ public class GraphicPacksRootFragment extends Fragment { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); genericRecyclerViewAdapter.setPredicate(g -> g.hasInstalledTitleId); - graphicPacksRecyclerViewAdapter.setShowInstalledOnly(true); + graphicPacksRecyclerViewAdapter.setShowInstalledOnly(this.installedOnly); fillGraphicPacks(); var navController = NavHostFragment.findNavController(GraphicPacksRootFragment.this); graphicPackViewModel = new ViewModelProvider(Objects.requireNonNull(navController.getCurrentBackStackEntry())) .get(GraphicPackViewModel.class); } + @Override + public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { + menuInflater.inflate(R.menu.graphic_packs, menu); + var searchMenuItem = menu.findItem(R.id.action_graphic_packs_search); + menu.findItem(R.id.show_installed_games_only).setChecked(installedOnly); + SearchView searchView = (SearchView) Objects.requireNonNull(searchMenuItem.getActionView()); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + graphicPacksRecyclerViewAdapter.setFilterText(query); + return true; + } + + @Override + public boolean onQueryTextChange(String newText) { + graphicPacksRecyclerViewAdapter.setFilterText(newText); + return true; + } + }); + searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { + @Override + public boolean onMenuItemActionExpand(@NonNull MenuItem item) { + if (recyclerView == null) { + return true; + } + recyclerView.setAdapter(graphicPacksRecyclerViewAdapter); + return true; + } + + @Override + public boolean onMenuItemActionCollapse(@NonNull MenuItem item) { + if (recyclerView == null) { + return true; + } + recyclerView.setAdapter(genericRecyclerViewAdapter); + return true; + } + }); + } + + private void showInstalledOnly(boolean installedOnly) { + this.installedOnly = installedOnly; + if (installedOnly) { + genericRecyclerViewAdapter.setPredicate(g -> g.hasInstalledTitleId); + return; + } + genericRecyclerViewAdapter.clearPredicate(); + } + + @Override + public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { + if (menuItem.getItemId() == R.id.action_graphic_packs_download && !updateInProgress) { + checkForUpdates(); + return true; + } + if (menuItem.getItemId() == R.id.action_graphic_packs_search) { + return true; + } + if (menuItem.getItemId() == R.id.show_installed_games_only) { + boolean installedOnly = !menuItem.isChecked(); + graphicPacksRecyclerViewAdapter.setShowInstalledOnly(installedOnly); + menuItem.setChecked(installedOnly); + showInstalledOnly(installedOnly); + return true; + } + return false; + } + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { var binding = LayoutGenericRecyclerViewBinding.inflate(inflater, container, false); - binding.recyclerView.setAdapter(this.genericRecyclerViewAdapter); - requireActivity().addMenuProvider(new MenuProvider() { - @Override - public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { - menuInflater.inflate(R.menu.graphic_packs, menu); - var searchMenuItem = menu.findItem(R.id.action_graphic_packs_search); - SearchView searchView = (SearchView) Objects.requireNonNull(searchMenuItem.getActionView()); - searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String query) { - graphicPacksRecyclerViewAdapter.setFilterText(query); - return true; - } - - @Override - public boolean onQueryTextChange(String newText) { - graphicPacksRecyclerViewAdapter.setFilterText(newText); - return true; - } - }); - searchMenuItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() { - @Override - public boolean onMenuItemActionExpand(@NonNull MenuItem item) { - binding.recyclerView.setAdapter(graphicPacksRecyclerViewAdapter); - return true; - } - - @Override - public boolean onMenuItemActionCollapse(@NonNull MenuItem item) { - binding.recyclerView.setAdapter(genericRecyclerViewAdapter); - return true; - } - }); - } - - private void showInstalledOnly(boolean installedOnly) { - if (installedOnly) { - genericRecyclerViewAdapter.setPredicate(g -> g.hasInstalledTitleId); - return; - } - genericRecyclerViewAdapter.clearPredicate(); - } - - @Override - public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { - if (menuItem.getItemId() == R.id.action_graphic_packs_download && !updateInProgress) { - checkForUpdates(); - return true; - } - if (menuItem.getItemId() == R.id.action_graphic_packs_search) { - return true; - } - if (menuItem.getItemId() == R.id.show_installed_games_only) { - boolean installedOnly = !menuItem.isChecked(); - graphicPacksRecyclerViewAdapter.setShowInstalledOnly(installedOnly); - menuItem.setChecked(installedOnly); - showInstalledOnly(installedOnly); - return true; - } - return false; - } - }, getViewLifecycleOwner(), Lifecycle.State.RESUMED); + recyclerView = binding.recyclerView; + recyclerView.setAdapter(genericRecyclerViewAdapter); + requireActivity().addMenuProvider(this, getViewLifecycleOwner(), Lifecycle.State.RESUMED); return binding.getRoot(); } + private void setDataNodes(List graphicPackDataNodes, GraphicPackSectionNode graphicPackSectionNode) { + for (var node : graphicPackSectionNode.getChildren()) { + if (node instanceof GraphicPackSectionNode sectionNode) { + setDataNodes(graphicPackDataNodes, sectionNode); + } else if (node instanceof GraphicPackDataNode dataNode) { + graphicPackDataNodes.add(dataNode); + } + } + } + private void fillGraphicPacks() { var nativeGraphicPacks = NativeGraphicPacks.getGraphicPackBasicInfos(); graphicPackSectionRootNode.clear(); List graphicPackDataNodes = new ArrayList<>(); nativeGraphicPacks.forEach(graphicPack -> { - var tokens = Arrays.asList(graphicPack.virtualPath().split("/")); boolean hasTitleIdInstalled = graphicPack.titleIds().stream().anyMatch(installedTitleIds::contains); - var dataNode = new GraphicPackDataNode(graphicPack.id(), tokens.get(tokens.size() - 1), graphicPack.virtualPath(), hasTitleIdInstalled); - graphicPackDataNodes.add(dataNode); - graphicPackSectionRootNode.addGraphicPackDataByTokens(tokens.subList(0, tokens.size() - 1), dataNode); + graphicPackSectionRootNode.addGraphicPackDataByTokens(graphicPack, hasTitleIdInstalled); }); + + setDataNodes(graphicPackDataNodes, graphicPackSectionRootNode); graphicPacksRecyclerViewAdapter.setItems(graphicPackDataNodes); genericRecyclerViewAdapter.clearRecyclerViewItems(); graphicPackSectionRootNode.sort(); - graphicPackSectionRootNode.children.forEach(node -> { - var graphicPackItemType = node instanceof GraphicPackSectionNode - ? GraphicPackItemRecyclerViewItem.GraphicPackItemType.SECTION - : GraphicPackItemRecyclerViewItem.GraphicPackItemType.GRAPHIC_PACK; - GraphicPackItemRecyclerViewItem graphicPackItemRecyclerViewItem = new GraphicPackItemRecyclerViewItem(node.name, - node.hasTitleIdInstalled(), - graphicPackItemType, + graphicPackSectionRootNode.getChildren().forEach(node -> { + GraphicPackListItemRecyclerViewItem graphicPackItemRecyclerViewItem = new GraphicPackListItemRecyclerViewItem(node, () -> { graphicPackViewModel.setGraphicPackNode(node); Bundle bundle = new Bundle(); @@ -301,7 +222,9 @@ public class GraphicPacksRootFragment extends Fragment { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { - if (!call.isCanceled()) onDownloadError(dialog); + if (!call.isCanceled()) { + onDownloadError(dialog); + } } }); } @@ -312,7 +235,7 @@ public class GraphicPacksRootFragment extends Fragment { "downloadedGraphicPacks", "version.txt"); try { - return Optional.of(new String(Files.readAllBytes(graphicPacksVersionFile))); + return Optional.of(new String(java.nio.file.Files.readAllBytes(graphicPacksVersionFile))); } catch (IOException ignored) { return Optional.empty(); } @@ -329,12 +252,12 @@ public class GraphicPacksRootFragment extends Fragment { public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { try (ResponseBody responseBody = response.body()) { Path graphicPacksTempDirPath = graphicPacksDirPath.resolve("downloadedGraphicPacksTemp"); - FileCallbacks.delete(graphicPacksTempDirPath); + Files.delete(graphicPacksTempDirPath.toFile()); Zip.unzip(Objects.requireNonNull(responseBody).byteStream(), graphicPacksTempDirPath.toString()); - Files.write(graphicPacksTempDirPath.resolve("version.txt"), version.getBytes(StandardCharsets.UTF_8)); + java.nio.file.Files.write(graphicPacksTempDirPath.resolve("version.txt"), version.getBytes(StandardCharsets.UTF_8)); Path downloadedGraphicPacksDirPath = graphicPacksDirPath.resolve("downloadedGraphicPacks"); - FileCallbacks.delete(downloadedGraphicPacksDirPath); - Files.move(graphicPacksTempDirPath, downloadedGraphicPacksDirPath); + Files.delete(downloadedGraphicPacksDirPath.toFile()); + java.nio.file.Files.move(graphicPacksTempDirPath, downloadedGraphicPacksDirPath); NativeGraphicPacks.refreshGraphicPacks(); requireActivity().runOnUiThread(() -> fillGraphicPacks()); onDownloadFinish(dialog); @@ -343,7 +266,9 @@ public class GraphicPacksRootFragment extends Fragment { @Override public void onFailure(@NonNull Call call, @NonNull IOException e) { - if (!call.isCanceled()) onDownloadError(dialog); + if (!call.isCanceled()) { + onDownloadError(dialog); + } } }); } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphics/GraphicsSettingsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphics/GraphicsSettingsFragment.java index d49cf2be..20b683da 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/graphics/GraphicsSettingsFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/graphics/GraphicsSettingsFragment.java @@ -32,8 +32,7 @@ public class GraphicsSettingsFragment extends Fragment { int resourceId = switch (fullscreenScaling) { case NativeSettings.FULLSCREEN_SCALING_KEEP_ASPECT_RATIO -> R.string.keep_aspect_ratio; case NativeSettings.FULLSCREEN_SCALING_STRETCH -> R.string.stretch; - default -> - throw new IllegalArgumentException("Invalid fullscreen scaling mode: " + fullscreenScaling); + default -> throw new IllegalArgumentException("Invalid fullscreen scaling mode: " + fullscreenScaling); }; return getString(resourceId); } @@ -44,8 +43,7 @@ public class GraphicsSettingsFragment extends Fragment { case NativeSettings.SCALING_FILTER_BICUBIC_FILTER -> R.string.bicubic; case NativeSettings.SCALING_FILTER_BICUBIC_HERMITE_FILTER -> R.string.hermite; case NativeSettings.SCALING_FILTER_NEAREST_NEIGHBOR_FILTER -> R.string.nearest_neighbor; - default -> - throw new IllegalArgumentException("Invalid scaling filter: " + scalingFilter); + default -> throw new IllegalArgumentException("Invalid scaling filter: " + scalingFilter); }; return getString(resourceId); } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsDataProvider.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsDataProvider.java index 377be6d3..a69f7c2c 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsDataProvider.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsDataProvider.java @@ -39,8 +39,7 @@ public class ControllerInputsDataProvider { case NativeInput.VPAD_BUTTON_MIC -> R.string.button_mic; case NativeInput.VPAD_BUTTON_SCREEN -> R.string.button_screen; case NativeInput.VPAD_BUTTON_HOME -> R.string.button_home; - default -> - throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); + default -> throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); }; } if (controllerType == NativeInput.EMULATED_CONTROLLER_TYPE_PRO) { @@ -70,8 +69,7 @@ public class ControllerInputsDataProvider { case NativeInput.PRO_BUTTON_STICKR_DOWN -> R.string.button_stickr_down; case NativeInput.PRO_BUTTON_STICKR_LEFT -> R.string.button_stickr_left; case NativeInput.PRO_BUTTON_STICKR_RIGHT -> R.string.button_stickr_right; - default -> - throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); + default -> throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); }; } if (controllerType == NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC) { @@ -99,8 +97,7 @@ public class ControllerInputsDataProvider { case NativeInput.CLASSIC_BUTTON_STICKR_DOWN -> R.string.button_stickr_down; case NativeInput.CLASSIC_BUTTON_STICKR_LEFT -> R.string.button_stickr_left; case NativeInput.CLASSIC_BUTTON_STICKR_RIGHT -> R.string.button_stickr_right; - default -> - throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); + default -> throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); }; } if (controllerType == NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) { @@ -122,8 +119,7 @@ public class ControllerInputsDataProvider { case NativeInput.WIIMOTE_BUTTON_NUNCHUCK_LEFT -> R.string.button_nunchuck_left; case NativeInput.WIIMOTE_BUTTON_NUNCHUCK_RIGHT -> R.string.button_nunchuck_right; case NativeInput.WIIMOTE_BUTTON_HOME -> R.string.button_home; - default -> - throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); + default -> throw new IllegalArgumentException("Invalid buttonId " + buttonId + " for controllerType " + controllerType); }; } throw new IllegalArgumentException("Invalid controllerType " + controllerType); diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsFragment.java index 41737b29..479e7d54 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/ControllerInputsFragment.java @@ -4,14 +4,16 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import java.util.HashMap; import java.util.Map; @@ -33,7 +35,9 @@ public class ControllerInputsFragment extends Fragment { private final EmulatedControllerTypeAdapter emulatedControllerTypeAdapter = new EmulatedControllerTypeAdapter(); private void onTypeChanged(int controllerType) { - if (this.controllerType == controllerType) return; + if (this.controllerType == controllerType) { + return; + } this.controllerType = controllerType; NativeInput.setControllerType(controllerIndex, controllerType); genericRecyclerViewAdapter.clearRecyclerViewItems(); @@ -56,30 +60,32 @@ public class ControllerInputsFragment extends Fragment { int buttonId = controllerInput.buttonId; int buttonResourceIdName = controllerInput.buttonResourceIdName; InputRecyclerViewItem inputRecyclerViewItem = new InputRecyclerViewItem(controllerInput.buttonResourceIdName, controllerInput.boundInput, inputItem -> { - MotionDialog inputDialog = new MotionDialog(getContext()); - inputDialog.setOnKeyListener((dialog, keyCode, keyEvent) -> { - if (inputManager.mapKeyEventToMappingId(controllerIndex, buttonId, keyEvent)) { + var inputDialog = new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.inputBindingDialogTitle) + .setMessage(getString(R.string.inputBindingDialogMessage, getString(buttonResourceIdName))) + .setNeutralButton(getString(R.string.clear), (dialogInterface, i) -> { + NativeInput.clearControllerMapping(controllerIndex, buttonId); + inputItem.clearBoundInput(); + dialogInterface.dismiss(); + }) + .setNegativeButton(getString(R.string.cancel), (dialogInterface, i) -> dialogInterface.dismiss()) + .show(); + TextView messageTextView = inputDialog.requireViewById(android.R.id.message); + messageTextView.setFocusableInTouchMode(true); + messageTextView.requestFocus(); + messageTextView.setOnKeyListener((v, keyCode, event) -> { + if (inputManager.mapKeyEventToMappingId(controllerIndex, buttonId, event)) { inputItem.setBoundInput(NativeInput.getControllerMapping(controllerIndex, buttonId)); inputDialog.dismiss(); } return true; }); - inputDialog.setOnMotionListener(motionEvent -> { - if (inputManager.mapMotionEventToMappingId(controllerIndex, buttonId, motionEvent)) { + messageTextView.setOnGenericMotionListener((v, event) -> { + if (inputManager.mapMotionEventToMappingId(controllerIndex, buttonId, event)) { inputItem.setBoundInput(NativeInput.getControllerMapping(controllerIndex, buttonId)); inputDialog.dismiss(); } return true; }); - inputDialog.setMessage(getString(R.string.inputBindingDialogMessage, getString(buttonResourceIdName))); - inputDialog.setButton(AlertDialog.BUTTON_NEUTRAL, getString(R.string.clear), (dialogInterface, i) -> { - NativeInput.clearControllerMapping(controllerIndex, buttonId); - inputItem.clearBoundInput(); - dialogInterface.dismiss(); - }); - inputDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getString(R.string.cancel), (dialogInterface, i) -> dialogInterface.dismiss()); - inputDialog.setTitle(R.string.inputBindingDialogTitle); - inputDialog.show(); }); genericRecyclerViewAdapter.addRecyclerViewItem(inputRecyclerViewItem); } @@ -92,17 +98,19 @@ public class ControllerInputsFragment extends Fragment { super.onCreate(savedInstanceState); controllerIndex = requireArguments().getInt(CONTROLLER_INDEX); ActionBar actionBar = ((AppCompatActivity) requireActivity()).getSupportActionBar(); - if (actionBar != null) + if (actionBar != null) { actionBar.setTitle(getString(R.string.controller_numbered, controllerIndex + 1)); - if (NativeInput.isControllerDisabled(controllerIndex)) + } + if (NativeInput.isControllerDisabled(controllerIndex)) { controllerType = NativeInput.EMULATED_CONTROLLER_TYPE_DISABLED; - else controllerType = NativeInput.getControllerType(controllerIndex); + } else { + controllerType = NativeInput.getControllerType(controllerIndex); + } setControllerInputs(NativeInput.getControllerMappings(controllerIndex)); var binding = LayoutGenericRecyclerViewBinding.inflate(inflater, container, false); binding.recyclerView.setAdapter(genericRecyclerViewAdapter); - return binding.getRoot(); } } \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/EmulatedControllerTypeAdapter.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/EmulatedControllerTypeAdapter.java index 22894d76..8c601ebf 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/EmulatedControllerTypeAdapter.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/EmulatedControllerTypeAdapter.java @@ -17,7 +17,7 @@ public class EmulatedControllerTypeAdapter extends SelectionAdapter { NativeInput.EMULATED_CONTROLLER_TYPE_PRO, NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC, NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) - .map(type -> new ChoiceItem<>(t->t.setText(ControllerTypeResourceNameMapper.controllerTypeToResourceNameId(type)), type)) + .map(type -> new ChoiceItem<>(t -> t.setText(ControllerTypeResourceNameMapper.controllerTypeToResourceNameId(type)), type)) .collect(Collectors.toList()); setSelectedValue(NativeInput.EMULATED_CONTROLLER_TYPE_DISABLED); } @@ -32,10 +32,12 @@ public class EmulatedControllerTypeAdapter extends SelectionAdapter { public boolean isEnabled(int position) { int currentControllerType = getItem(selectedPosition); int type = getItem(position); - if (type == NativeInput.EMULATED_CONTROLLER_TYPE_DISABLED) + if (type == NativeInput.EMULATED_CONTROLLER_TYPE_DISABLED) { return true; - if (type == NativeInput.EMULATED_CONTROLLER_TYPE_VPAD) + } + if (type == NativeInput.EMULATED_CONTROLLER_TYPE_VPAD) { return currentControllerType == NativeInput.EMULATED_CONTROLLER_TYPE_VPAD || vpadCount < NativeInput.MAX_VPAD_CONTROLLERS; + } boolean isWPAD = currentControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_VPAD && currentControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_DISABLED; return isWPAD || wpadCount < NativeInput.MAX_WPAD_CONTROLLERS; } diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/MotionDialog.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/input/MotionDialog.java deleted file mode 100644 index ae73b979..00000000 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/input/MotionDialog.java +++ /dev/null @@ -1,32 +0,0 @@ -package info.cemu.Cemu.settings.input; - -import android.app.AlertDialog; -import android.content.Context; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; - -public class MotionDialog extends AlertDialog { - public static interface OnMotionListener { - boolean onMotion(MotionEvent ev); - } - - private OnMotionListener onMotionListener; - - protected MotionDialog(Context context) { - super(context); - } - - public void setOnMotionListener(OnMotionListener onMotionListener) { - this.onMotionListener = onMotionListener; - } - - @Override - public boolean onGenericMotionEvent(@NonNull MotionEvent event) { - if (onMotionListener != null && onMotionListener.onMotion(event)) { - return true; - } - return super.onGenericMotionEvent(event); - } -} - diff --git a/src/android/app/src/main/java/info/cemu/Cemu/settings/overlay/OverlaySettingsFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/settings/overlay/OverlaySettingsFragment.java index ee822ce4..57433971 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/settings/overlay/OverlaySettingsFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/settings/overlay/OverlaySettingsFragment.java @@ -22,22 +22,14 @@ import info.cemu.Cemu.nativeinterface.NativeSettings; public class OverlaySettingsFragment extends Fragment { private String overlayScreenPositionToString(int overlayScreenPosition) { int resourceId = switch (overlayScreenPosition) { - case NativeSettings.OVERLAY_SCREEN_POSITION_DISABLED -> - R.string.overlay_position_disabled; - case NativeSettings.OVERLAY_SCREEN_POSITION_TOP_LEFT -> - R.string.overlay_position_top_left; - case NativeSettings.OVERLAY_SCREEN_POSITION_TOP_CENTER -> - R.string.overlay_position_top_center; - case NativeSettings.OVERLAY_SCREEN_POSITION_TOP_RIGHT -> - R.string.overlay_position_top_right; - case NativeSettings.OVERLAY_SCREEN_POSITION_BOTTOM_LEFT -> - R.string.overlay_position_bottom_left; - case NativeSettings.OVERLAY_SCREEN_POSITION_BOTTOM_CENTER -> - R.string.overlay_position_bottom_center; - case NativeSettings.OVERLAY_SCREEN_POSITION_BOTTOM_RIGHT -> - R.string.overlay_position_bottom_right; - default -> - throw new IllegalArgumentException("Invalid overlay position: " + overlayScreenPosition); + case NativeSettings.OVERLAY_SCREEN_POSITION_DISABLED -> R.string.overlay_position_disabled; + case NativeSettings.OVERLAY_SCREEN_POSITION_TOP_LEFT -> R.string.overlay_position_top_left; + case NativeSettings.OVERLAY_SCREEN_POSITION_TOP_CENTER -> R.string.overlay_position_top_center; + case NativeSettings.OVERLAY_SCREEN_POSITION_TOP_RIGHT -> R.string.overlay_position_top_right; + case NativeSettings.OVERLAY_SCREEN_POSITION_BOTTOM_LEFT -> R.string.overlay_position_bottom_left; + case NativeSettings.OVERLAY_SCREEN_POSITION_BOTTOM_CENTER -> R.string.overlay_position_bottom_center; + case NativeSettings.OVERLAY_SCREEN_POSITION_BOTTOM_RIGHT -> R.string.overlay_position_bottom_right; + default -> throw new IllegalArgumentException("Invalid overlay position: " + overlayScreenPosition); }; return getString(resourceId); } diff --git a/src/android/app/src/main/res/drawable/button_a.xml b/src/android/app/src/main/res/drawable/button_a.xml index d81250c5..075c9b15 100644 --- a/src/android/app/src/main/res/drawable/button_a.xml +++ b/src/android/app/src/main/res/drawable/button_a.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_b.xml b/src/android/app/src/main/res/drawable/button_b.xml index f16c1506..26ccd8fa 100644 --- a/src/android/app/src/main/res/drawable/button_b.xml +++ b/src/android/app/src/main/res/drawable/button_b.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_c.xml b/src/android/app/src/main/res/drawable/button_c.xml index f80f90be..c83f0bee 100644 --- a/src/android/app/src/main/res/drawable/button_c.xml +++ b/src/android/app/src/main/res/drawable/button_c.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_home.xml b/src/android/app/src/main/res/drawable/button_home.xml index f519e6d7..daa3a938 100644 --- a/src/android/app/src/main/res/drawable/button_home.xml +++ b/src/android/app/src/main/res/drawable/button_home.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_l.xml b/src/android/app/src/main/res/drawable/button_l.xml index a6b54c61..afc0ac7c 100644 --- a/src/android/app/src/main/res/drawable/button_l.xml +++ b/src/android/app/src/main/res/drawable/button_l.xml @@ -3,13 +3,13 @@ android:height="53.792dp" android:viewportWidth="94.033" android:viewportHeight="53.792"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_minus.xml b/src/android/app/src/main/res/drawable/button_minus.xml index de1b123a..b3f2a8cd 100644 --- a/src/android/app/src/main/res/drawable/button_minus.xml +++ b/src/android/app/src/main/res/drawable/button_minus.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_one.xml b/src/android/app/src/main/res/drawable/button_one.xml index c7bdc4a0..80770e97 100644 --- a/src/android/app/src/main/res/drawable/button_one.xml +++ b/src/android/app/src/main/res/drawable/button_one.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_plus.xml b/src/android/app/src/main/res/drawable/button_plus.xml index 00a892ee..d6c7bdb1 100644 --- a/src/android/app/src/main/res/drawable/button_plus.xml +++ b/src/android/app/src/main/res/drawable/button_plus.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_r.xml b/src/android/app/src/main/res/drawable/button_r.xml index 4ac99b4e..7da63d16 100644 --- a/src/android/app/src/main/res/drawable/button_r.xml +++ b/src/android/app/src/main/res/drawable/button_r.xml @@ -3,13 +3,13 @@ android:height="53.792dp" android:viewportWidth="94.033" android:viewportHeight="53.792"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_stick.xml b/src/android/app/src/main/res/drawable/button_stick.xml index 684e43a2..cc266942 100644 --- a/src/android/app/src/main/res/drawable/button_stick.xml +++ b/src/android/app/src/main/res/drawable/button_stick.xml @@ -3,20 +3,20 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - - + + + diff --git a/src/android/app/src/main/res/drawable/button_two.xml b/src/android/app/src/main/res/drawable/button_two.xml index 280ecb41..e60d43a9 100644 --- a/src/android/app/src/main/res/drawable/button_two.xml +++ b/src/android/app/src/main/res/drawable/button_two.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_x.xml b/src/android/app/src/main/res/drawable/button_x.xml index 9410fd0a..1d53b1b7 100644 --- a/src/android/app/src/main/res/drawable/button_x.xml +++ b/src/android/app/src/main/res/drawable/button_x.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_y.xml b/src/android/app/src/main/res/drawable/button_y.xml index ccfabb62..d945b9a2 100644 --- a/src/android/app/src/main/res/drawable/button_y.xml +++ b/src/android/app/src/main/res/drawable/button_y.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_z.xml b/src/android/app/src/main/res/drawable/button_z.xml index f509ecff..003eab95 100644 --- a/src/android/app/src/main/res/drawable/button_z.xml +++ b/src/android/app/src/main/res/drawable/button_z.xml @@ -3,14 +3,14 @@ android:height="95.036dp" android:viewportWidth="95.036" android:viewportHeight="95.036"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_zl.xml b/src/android/app/src/main/res/drawable/button_zl.xml index 754fc47e..a458611f 100644 --- a/src/android/app/src/main/res/drawable/button_zl.xml +++ b/src/android/app/src/main/res/drawable/button_zl.xml @@ -3,13 +3,13 @@ android:height="53.792dp" android:viewportWidth="94.033" android:viewportHeight="53.792"> - - + + diff --git a/src/android/app/src/main/res/drawable/button_zr.xml b/src/android/app/src/main/res/drawable/button_zr.xml index 03ef5b9d..abd70e13 100644 --- a/src/android/app/src/main/res/drawable/button_zr.xml +++ b/src/android/app/src/main/res/drawable/button_zr.xml @@ -3,13 +3,13 @@ android:height="53.792dp" android:viewportWidth="94.033" android:viewportHeight="53.792"> - - + + diff --git a/src/android/app/src/main/res/drawable/dpad_background.xml b/src/android/app/src/main/res/drawable/dpad_background.xml index bbe39bb9..58b24979 100644 --- a/src/android/app/src/main/res/drawable/dpad_background.xml +++ b/src/android/app/src/main/res/drawable/dpad_background.xml @@ -3,10 +3,10 @@ android:height="67.574dp" android:viewportWidth="67.574" android:viewportHeight="67.574"> - + diff --git a/src/android/app/src/main/res/drawable/dpad_button.xml b/src/android/app/src/main/res/drawable/dpad_button.xml index e4e49338..de03cc37 100644 --- a/src/android/app/src/main/res/drawable/dpad_button.xml +++ b/src/android/app/src/main/res/drawable/dpad_button.xml @@ -3,9 +3,9 @@ android:height="17.543dp" android:viewportWidth="17.543" android:viewportHeight="17.543"> - + diff --git a/src/android/app/src/main/res/drawable/ic_check.xml b/src/android/app/src/main/res/drawable/ic_check.xml new file mode 100644 index 00000000..a0ac8750 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_check.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/android/app/src/main/res/drawable/ic_download.xml b/src/android/app/src/main/res/drawable/ic_download.xml index dba46014..e2c863fa 100644 --- a/src/android/app/src/main/res/drawable/ic_download.xml +++ b/src/android/app/src/main/res/drawable/ic_download.xml @@ -4,7 +4,7 @@ android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal"> - + diff --git a/src/android/app/src/main/res/drawable/ic_favorite.xml b/src/android/app/src/main/res/drawable/ic_favorite.xml index 99679b17..a2580833 100644 --- a/src/android/app/src/main/res/drawable/ic_favorite.xml +++ b/src/android/app/src/main/res/drawable/ic_favorite.xml @@ -6,5 +6,5 @@ android:tint="?attr/colorControlNormal"> + android:pathData="M320,720L480,598L640,720L580,522L740,408L544,408L480,200L416,408L220,408L380,522L320,720ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z" /> diff --git a/src/android/app/src/main/res/drawable/ic_move.xml b/src/android/app/src/main/res/drawable/ic_move.xml index a9c4d927..a1909388 100644 --- a/src/android/app/src/main/res/drawable/ic_move.xml +++ b/src/android/app/src/main/res/drawable/ic_move.xml @@ -6,5 +6,5 @@ android:tint="?attr/colorControlNormal"> + android:pathData="M480,880L310,710L367,653L440,726L440,520L235,520L308,592L250,650L80,480L249,311L306,368L234,440L440,440L440,234L367,307L310,250L480,80L650,250L593,307L520,234L520,440L725,440L652,368L710,310L880,480L710,650L653,593L726,520L520,520L520,725L592,652L650,710L480,880Z" /> diff --git a/src/android/app/src/main/res/drawable/ic_resize.xml b/src/android/app/src/main/res/drawable/ic_resize.xml index d5db6649..bd0461bf 100644 --- a/src/android/app/src/main/res/drawable/ic_resize.xml +++ b/src/android/app/src/main/res/drawable/ic_resize.xml @@ -6,5 +6,5 @@ android:tint="?attr/colorControlNormal"> + android:pathData="M120,840L120,520L200,520L200,704L704,200L520,200L520,120L840,120L840,440L760,440L760,256L256,760L440,760L440,840L120,840Z" /> diff --git a/src/android/app/src/main/res/drawable/ic_search.xml b/src/android/app/src/main/res/drawable/ic_search.xml index ecd11383..0db52578 100644 --- a/src/android/app/src/main/res/drawable/ic_search.xml +++ b/src/android/app/src/main/res/drawable/ic_search.xml @@ -4,7 +4,7 @@ android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal"> - + diff --git a/src/android/app/src/main/res/drawable/ic_settings.xml b/src/android/app/src/main/res/drawable/ic_settings.xml index f112c1d1..3477b4b7 100644 --- a/src/android/app/src/main/res/drawable/ic_settings.xml +++ b/src/android/app/src/main/res/drawable/ic_settings.xml @@ -4,7 +4,7 @@ android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal"> - + diff --git a/src/android/app/src/main/res/drawable/stick_background.xml b/src/android/app/src/main/res/drawable/stick_background.xml index 347c1b3d..e9729831 100644 --- a/src/android/app/src/main/res/drawable/stick_background.xml +++ b/src/android/app/src/main/res/drawable/stick_background.xml @@ -3,14 +3,14 @@ android:height="76.53dp" android:viewportWidth="76.53" android:viewportHeight="76.53"> - - + + diff --git a/src/android/app/src/main/res/drawable/stick_inner.xml b/src/android/app/src/main/res/drawable/stick_inner.xml index 39e76644..a6a1d831 100644 --- a/src/android/app/src/main/res/drawable/stick_inner.xml +++ b/src/android/app/src/main/res/drawable/stick_inner.xml @@ -3,10 +3,10 @@ android:height="79.295dp" android:viewportWidth="79.295" android:viewportHeight="79.295"> - + diff --git a/src/android/app/src/main/res/layout/activity_main.xml b/src/android/app/src/main/res/layout/activity_main.xml index c4c7db9b..23c5ee37 100644 --- a/src/android/app/src/main/res/layout/activity_main.xml +++ b/src/android/app/src/main/res/layout/activity_main.xml @@ -1,17 +1,12 @@ - - - - \ No newline at end of file + app:defaultNavHost="true" + app:navGraph="@navigation/nav_main_graph" + tools:context=".MainActivity" /> diff --git a/src/android/app/src/main/res/layout/activity_settings.xml b/src/android/app/src/main/res/layout/activity_settings.xml index 5ce93921..21646275 100644 --- a/src/android/app/src/main/res/layout/activity_settings.xml +++ b/src/android/app/src/main/res/layout/activity_settings.xml @@ -20,21 +20,11 @@ android:layout_height="?attr/actionBarSize" /> - - - - - + android:layout_height="match_parent" + app:defaultNavHost="true" + app:navGraph="@navigation/nav_settings_graph" /> \ No newline at end of file diff --git a/src/android/app/src/main/res/layout/layout_graphic_pack_item.xml b/src/android/app/src/main/res/layout/layout_graphic_pack_item.xml index a2cdb5ac..ea86c4f7 100644 --- a/src/android/app/src/main/res/layout/layout_graphic_pack_item.xml +++ b/src/android/app/src/main/res/layout/layout_graphic_pack_item.xml @@ -8,16 +8,59 @@ android:foreground="?selectableItemBackground" android:padding="8dp"> - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/layout/layout_graphic_pack_search_item.xml b/src/android/app/src/main/res/layout/layout_graphic_pack_search_item.xml index 929d4519..e63f380a 100644 --- a/src/android/app/src/main/res/layout/layout_graphic_pack_search_item.xml +++ b/src/android/app/src/main/res/layout/layout_graphic_pack_search_item.xml @@ -8,17 +8,47 @@ android:foreground="?selectableItemBackground" android:padding="8dp"> - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + + + + + - diff --git a/src/android/app/src/main/res/menu/game.xml b/src/android/app/src/main/res/menu/game.xml index 42f6bfc5..f72c2054 100644 --- a/src/android/app/src/main/res/menu/game.xml +++ b/src/android/app/src/main/res/menu/game.xml @@ -13,4 +13,7 @@ + diff --git a/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index c4a603d4..00f9eaaf 100644 --- a/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/src/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/src/android/app/src/main/res/navigation/nav_settings_graph.xml b/src/android/app/src/main/res/navigation/nav_settings_graph.xml index 519cacfc..41d0d9aa 100644 --- a/src/android/app/src/main/res/navigation/nav_settings_graph.xml +++ b/src/android/app/src/main/res/navigation/nav_settings_graph.xml @@ -95,7 +95,7 @@ android:id="@+id/generalSettingsFragment" android:name="info.cemu.Cemu.settings.general.GeneralSettingsFragment" android:label="@string/general_settings" - tools:layout="@layout/layout_generic_recycler_view" > + tools:layout="@layout/layout_generic_recycler_view"> diff --git a/src/android/app/src/main/res/values-night/themes.xml b/src/android/app/src/main/res/values-night/themes.xml index b42cb9ce..e866a714 100644 --- a/src/android/app/src/main/res/values-night/themes.xml +++ b/src/android/app/src/main/res/values-night/themes.xml @@ -1,7 +1,4 @@ - + - + - \ No newline at end of file diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 77d6b2db..1240a04d 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -240,4 +240,8 @@ Console language Input text Done + Create shortcut + Device doesn\'t support creating shortcuts + Enabled graphic packs count + Graphic pack is enabled \ No newline at end of file diff --git a/src/android/app/src/main/res/values/themes.xml b/src/android/app/src/main/res/values/themes.xml index 1b7ea415..13b21d94 100644 --- a/src/android/app/src/main/res/values/themes.xml +++ b/src/android/app/src/main/res/values/themes.xml @@ -1,9 +1,16 @@ - + - - -