diff --git a/src/android/app/src/main/java/org/citra/citra_android/DolphinApplication.java b/src/android/app/src/main/java/org/citra/citra_android/DolphinApplication.java index e2eae92b4..09810d5f5 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/DolphinApplication.java +++ b/src/android/app/src/main/java/org/citra/citra_android/DolphinApplication.java @@ -6,18 +6,16 @@ import org.citra.citra_android.model.GameDatabase; import org.citra.citra_android.services.DirectoryInitializationService; import org.citra.citra_android.utils.PermissionsHandler; -public class DolphinApplication extends Application -{ - public static GameDatabase databaseHelper; +public class DolphinApplication extends Application { + public static GameDatabase databaseHelper; - @Override - public void onCreate() - { - super.onCreate(); + @Override + public void onCreate() { + super.onCreate(); - if (PermissionsHandler.hasWriteAccess(getApplicationContext())) - DirectoryInitializationService.startService(getApplicationContext()); + if (PermissionsHandler.hasWriteAccess(getApplicationContext())) + DirectoryInitializationService.startService(getApplicationContext()); - databaseHelper = new GameDatabase(this); - } + databaseHelper = new GameDatabase(this); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java b/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java index 6a9b5afe3..a6b56447e 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java +++ b/src/android/app/src/main/java/org/citra/citra_android/NativeLibrary.java @@ -18,411 +18,385 @@ import java.lang.ref.WeakReference; * Class which contains methods that interact * with the native side of the Dolphin code. */ -public final class NativeLibrary -{ - public static WeakReference sEmulationActivity = new WeakReference<>(null); +public final class NativeLibrary { + /** + * Default touchscreen device + */ + public static final String TouchScreenDevice = "Touchscreen"; + public static WeakReference sEmulationActivity = new WeakReference<>(null); + private static boolean alertResult = false; - /** - * Button type for use in onTouchEvent - */ - public static final class ButtonType - { - public static final int BUTTON_A = 700; - public static final int BUTTON_B = 701; - public static final int BUTTON_X = 702; - public static final int BUTTON_Y = 703; - public static final int BUTTON_START = 704; - public static final int BUTTON_SELECT = 705; - public static final int BUTTON_HOME = 706; - public static final int BUTTON_ZL = 707; - public static final int BUTTON_ZR = 708; - public static final int DPAD_UP = 709; - public static final int DPAD_DOWN = 710; - public static final int DPAD_LEFT = 711; - public static final int DPAD_RIGHT = 712; - public static final int STICK_LEFT = 713; - public static final int STICK_LEFT_UP = 714; - public static final int STICK_LEFT_DOWN = 715; - public static final int STICK_LEFT_LEFT = 716; - public static final int STICK_LEFT_RIGHT = 717; - public static final int STICK_C = 718; - public static final int STICK_C_UP = 719; - public static final int STICK_C_DOWN = 720; - public static final int STICK_C_LEFT = 771; - public static final int STICK_C_RIGHT = 772; - public static final int TRIGGER_L = 773; - public static final int TRIGGER_R = 774; - } - - /** - * Button states - */ - public static final class ButtonState - { - public static final int RELEASED = 0; - public static final int PRESSED = 1; - } - - private NativeLibrary() - { - // Disallows instantiation. - } - - /** - * Default touchscreen device - */ - public static final String TouchScreenDevice = "Touchscreen"; - - /** - * Handles button press events for a gamepad. - * - * @param Device The input descriptor of the gamepad. - * @param Button Key code identifying which button was pressed. - * @param Action Mask identifying which action is happening (button pressed down, or button released). - * @return If we handled the button press. - */ - public static native boolean onGamePadEvent(String Device, int Button, int Action); - - /** - * Handles gamepad movement events. - * - * @param Device The device ID of the gamepad. - * @param Axis The axis ID - * @param x_axis The value of the x-axis represented by the given ID. - * @param y_axis The value of the y-axis represented by the given ID - */ - public static native boolean onGamePadMoveEvent(String Device, int Axis, float x_axis, float y_axis); - - /** - * Handles gamepad movement events. - * - * @param Device The device ID of the gamepad. - * @param Axis_id The axis ID - * @param axis_val The value of the axis represented by the given ID. - */ - public static native boolean onGamePadAxisEvent(String Device, int Axis_id, float axis_val); - - /** - * Handles touch events. - * - * @param x_axis The value of the x-axis. - * @param y_axis The value of the y-axis - * @param pressed To identify if the touch held down or released. - */ - public static native void onTouchEvent(float x_axis, float y_axis, boolean pressed); - - /** - * Handles touch movement. - * - * @param x_axis The value of the instantaneous x-axis. - * @param y_axis The value of the instantaneous y-axis. - */ - public static native void onTouchMoved(float x_axis, float y_axis); - - public static native String GetUserSetting(String gameID, String Section, String Key); - - public static native void SetUserSetting(String gameID, String Section, String Key, String Value); - - public static native void InitGameIni(String gameID); - - /** - * Gets a value from a key in the given ini-based config file. - * - * @param configFile The ini-based config file to get the value from. - * @param Section The section key that the actual key is in. - * @param Key The key to get the value from. - * @param Default The value to return in the event the given key doesn't exist. - * @return the value stored at the key, or a default value if it doesn't exist. - */ - public static native String GetConfig(String configFile, String Section, String Key, - String Default); - - /** - * Sets a value to a key in the given ini config file. - * - * @param configFile The ini-based config file to add the value to. - * @param Section The section key for the ini key - * @param Key The actual ini key to set. - * @param Value The string to set the ini key to. - */ - public static native void SetConfig(String configFile, String Section, String Key, String Value); - - /** - * Gets the embedded banner within the given ISO/ROM. - * - * @param filename the file path to the ISO/ROM. - * @return an integer array containing the color data for the banner. - */ - public static native int[] GetBanner(String filename); - - /** - * Gets the embedded title of the given ISO/ROM. - * - * @param filename The file path to the ISO/ROM. - * @return the embedded title of the ISO/ROM. - */ - public static native String GetTitle(String filename); - - public static native String GetDescription(String filename); - - public static native String GetGameId(String filename); - - public static native int GetCountry(String filename); - - public static native String GetCompany(String filename); - - public static native long GetFilesize(String filename); - - public static native int GetPlatform(String filename); - - /** - * Gets the Dolphin version string. - * - * @return the Dolphin version string. - */ - public static native String GetVersionString(); - - public static native String GetGitRevision(); - - /** - * Saves a screen capture of the game - */ - public static native void SaveScreenShot(); - - /** - * Saves a game state to the slot number. - * - * @param slot The slot location to save state to. - * @param wait If false, returns as early as possible. - * If true, returns once the savestate has been written to disk. - */ - public static native void SaveState(int slot, boolean wait); - - /** - * Saves a game state to the specified path. - * - * @param path The path to save state to. - * @param wait If false, returns as early as possible. - * If true, returns once the savestate has been written to disk. - */ - public static native void SaveStateAs(String path, boolean wait); - - /** - * Loads a game state from the slot number. - * - * @param slot The slot location to load state from. - */ - public static native void LoadState(int slot); - - /** - * Loads a game state from the specified path. - * - * @param path The path to load state from. - */ - public static native void LoadStateAs(String path); - - /** - * Sets the current working user directory - * If not set, it auto-detects a location - */ - public static native void SetUserDirectory(String directory); - - /** - * Returns the current working user directory - */ - public static native String GetUserDirectory(); - - // Create the config.ini file. - public static native void CreateConfigFile(); - - public static native int DefaultCPUCore(); - - /** - * Begins emulation. - */ - public static native void Run(String path); - - /** - * Begins emulation from the specified savestate. - */ - public static native void Run(String path, String savestatePath, boolean deleteSavestate); - - public static native void ChangeDisc(String path); - - // Surface Handling - public static native void SurfaceChanged(Surface surf); - - public static native void SurfaceDestroyed(); - - /** - * Unpauses emulation from a paused state. - */ - public static native void UnPauseEmulation(); - - /** - * Pauses emulation. - */ - public static native void PauseEmulation(); - - /** - * Stops emulation. - */ - public static native void StopEmulation(); - - /** - * Returns true if emulation is running (or is paused). - */ - public static native boolean IsRunning(); - - /** - * Enables or disables CPU block profiling - * - * @param enable - */ - public static native void SetProfiling(boolean enable); - - /** - * Writes out the block profile results - */ - public static native void WriteProfileResults(); - - /** - * Native EGL functions not exposed by Java bindings - **/ - public static native void eglBindAPI(int api); - - /** - * Provides a way to refresh the connections on Wiimotes - */ - public static native void RefreshWiimotes(); - - /** - * Returns the performance stats for the current game - **/ - public static native double[] GetPerfStats(); - - /** - * The methods C++ uses to find references to Java classes and methods - * are really expensive. Rather than calling them every time we want to - * run them, do it once when we load the native library. - */ - private static native void CacheClassesAndMethods(); - - /** - * Switches the screen layout. - */ - public static native void SwitchScreenLayout(); - - /** - * Swaps the top and bottom screens. - */ - public static native void SwapScreens(); - - static - { - try - { - System.loadLibrary("main"); - } - catch (UnsatisfiedLinkError ex) - { - Log.error("[NativeLibrary] " + ex.toString()); - } - - CacheClassesAndMethods(); - } - - private static boolean alertResult = false; - - public static boolean displayAlertMsg(final String caption, final String text, - final boolean yesNo) - { - Log.error("[NativeLibrary] Alert: " + text); - final EmulationActivity emulationActivity = sEmulationActivity.get(); - boolean result = false; - if (emulationActivity == null) - { - Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic alert."); - } - else - { - // Create object used for waiting. - final Object lock = new Object(); - AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) - .setTitle(caption) - .setMessage(text); - - // If not yes/no dialog just have one button that dismisses modal, - // otherwise have a yes and no button that sets alertResult accordingly. - if (!yesNo) - { - builder - .setCancelable(false) - .setPositiveButton("OK", (dialog, whichButton) -> - { - dialog.dismiss(); - synchronized (lock) - { - lock.notify(); - } - }); - } - else - { - alertResult = false; - - builder - .setPositiveButton("Yes", (dialog, whichButton) -> - { - alertResult = true; - dialog.dismiss(); - synchronized (lock) - { - lock.notify(); - } - }) - .setNegativeButton("No", (dialog, whichButton) -> - { - alertResult = false; - dialog.dismiss(); - synchronized (lock) - { - lock.notify(); - } - }); - } - - // Show the AlertDialog on the main thread. - emulationActivity.runOnUiThread(() -> builder.show()); - - // Wait for the lock to notify that it is complete. - synchronized (lock) - { - try - { - lock.wait(); + static { + try { + System.loadLibrary("main"); + } catch (UnsatisfiedLinkError ex) { + Log.error("[NativeLibrary] " + ex.toString()); } - catch (Exception e) - { - } - } - if (yesNo) - result = alertResult; + CacheClassesAndMethods(); } - return result; - } - public static void setEmulationActivity(EmulationActivity emulationActivity) - { - Log.verbose("[NativeLibrary] Registering EmulationActivity."); - sEmulationActivity = new WeakReference<>(emulationActivity); - } + private NativeLibrary() { + // Disallows instantiation. + } - public static void clearEmulationActivity() - { - Log.verbose("[NativeLibrary] Unregistering EmulationActivity."); + /** + * Handles button press events for a gamepad. + * + * @param Device The input descriptor of the gamepad. + * @param Button Key code identifying which button was pressed. + * @param Action Mask identifying which action is happening (button pressed down, or button released). + * @return If we handled the button press. + */ + public static native boolean onGamePadEvent(String Device, int Button, int Action); - sEmulationActivity.clear(); - } + /** + * Handles gamepad movement events. + * + * @param Device The device ID of the gamepad. + * @param Axis The axis ID + * @param x_axis The value of the x-axis represented by the given ID. + * @param y_axis The value of the y-axis represented by the given ID + */ + public static native boolean onGamePadMoveEvent(String Device, int Axis, float x_axis, float y_axis); + + /** + * Handles gamepad movement events. + * + * @param Device The device ID of the gamepad. + * @param Axis_id The axis ID + * @param axis_val The value of the axis represented by the given ID. + */ + public static native boolean onGamePadAxisEvent(String Device, int Axis_id, float axis_val); + + /** + * Handles touch events. + * + * @param x_axis The value of the x-axis. + * @param y_axis The value of the y-axis + * @param pressed To identify if the touch held down or released. + */ + public static native void onTouchEvent(float x_axis, float y_axis, boolean pressed); + + /** + * Handles touch movement. + * + * @param x_axis The value of the instantaneous x-axis. + * @param y_axis The value of the instantaneous y-axis. + */ + public static native void onTouchMoved(float x_axis, float y_axis); + + public static native String GetUserSetting(String gameID, String Section, String Key); + + public static native void SetUserSetting(String gameID, String Section, String Key, String Value); + + public static native void InitGameIni(String gameID); + + /** + * Gets a value from a key in the given ini-based config file. + * + * @param configFile The ini-based config file to get the value from. + * @param Section The section key that the actual key is in. + * @param Key The key to get the value from. + * @param Default The value to return in the event the given key doesn't exist. + * @return the value stored at the key, or a default value if it doesn't exist. + */ + public static native String GetConfig(String configFile, String Section, String Key, + String Default); + + /** + * Sets a value to a key in the given ini config file. + * + * @param configFile The ini-based config file to add the value to. + * @param Section The section key for the ini key + * @param Key The actual ini key to set. + * @param Value The string to set the ini key to. + */ + public static native void SetConfig(String configFile, String Section, String Key, String Value); + + /** + * Gets the embedded banner within the given ISO/ROM. + * + * @param filename the file path to the ISO/ROM. + * @return an integer array containing the color data for the banner. + */ + public static native int[] GetBanner(String filename); + + /** + * Gets the embedded title of the given ISO/ROM. + * + * @param filename The file path to the ISO/ROM. + * @return the embedded title of the ISO/ROM. + */ + public static native String GetTitle(String filename); + + public static native String GetDescription(String filename); + + public static native String GetGameId(String filename); + + public static native int GetCountry(String filename); + + public static native String GetCompany(String filename); + + public static native long GetFilesize(String filename); + + public static native int GetPlatform(String filename); + + /** + * Gets the Dolphin version string. + * + * @return the Dolphin version string. + */ + public static native String GetVersionString(); + + public static native String GetGitRevision(); + + /** + * Saves a screen capture of the game + */ + public static native void SaveScreenShot(); + + /** + * Saves a game state to the slot number. + * + * @param slot The slot location to save state to. + * @param wait If false, returns as early as possible. + * If true, returns once the savestate has been written to disk. + */ + public static native void SaveState(int slot, boolean wait); + + /** + * Saves a game state to the specified path. + * + * @param path The path to save state to. + * @param wait If false, returns as early as possible. + * If true, returns once the savestate has been written to disk. + */ + public static native void SaveStateAs(String path, boolean wait); + + /** + * Loads a game state from the slot number. + * + * @param slot The slot location to load state from. + */ + public static native void LoadState(int slot); + + /** + * Loads a game state from the specified path. + * + * @param path The path to load state from. + */ + public static native void LoadStateAs(String path); + + /** + * Sets the current working user directory + * If not set, it auto-detects a location + */ + public static native void SetUserDirectory(String directory); + + /** + * Returns the current working user directory + */ + public static native String GetUserDirectory(); + + // Create the config.ini file. + public static native void CreateConfigFile(); + + public static native int DefaultCPUCore(); + + /** + * Begins emulation. + */ + public static native void Run(String path); + + /** + * Begins emulation from the specified savestate. + */ + public static native void Run(String path, String savestatePath, boolean deleteSavestate); + + public static native void ChangeDisc(String path); + + // Surface Handling + public static native void SurfaceChanged(Surface surf); + + public static native void SurfaceDestroyed(); + + /** + * Unpauses emulation from a paused state. + */ + public static native void UnPauseEmulation(); + + /** + * Pauses emulation. + */ + public static native void PauseEmulation(); + + /** + * Stops emulation. + */ + public static native void StopEmulation(); + + /** + * Returns true if emulation is running (or is paused). + */ + public static native boolean IsRunning(); + + /** + * Enables or disables CPU block profiling + * + * @param enable + */ + public static native void SetProfiling(boolean enable); + + /** + * Writes out the block profile results + */ + public static native void WriteProfileResults(); + + /** + * Native EGL functions not exposed by Java bindings + **/ + public static native void eglBindAPI(int api); + + /** + * Provides a way to refresh the connections on Wiimotes + */ + public static native void RefreshWiimotes(); + + /** + * Returns the performance stats for the current game + **/ + public static native double[] GetPerfStats(); + + /** + * The methods C++ uses to find references to Java classes and methods + * are really expensive. Rather than calling them every time we want to + * run them, do it once when we load the native library. + */ + private static native void CacheClassesAndMethods(); + + /** + * Switches the screen layout. + */ + public static native void SwitchScreenLayout(); + + /** + * Swaps the top and bottom screens. + */ + public static native void SwapScreens(); + + public static boolean displayAlertMsg(final String caption, final String text, + final boolean yesNo) { + Log.error("[NativeLibrary] Alert: " + text); + final EmulationActivity emulationActivity = sEmulationActivity.get(); + boolean result = false; + if (emulationActivity == null) { + Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic alert."); + } else { + // Create object used for waiting. + final Object lock = new Object(); + AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity) + .setTitle(caption) + .setMessage(text); + + // If not yes/no dialog just have one button that dismisses modal, + // otherwise have a yes and no button that sets alertResult accordingly. + if (!yesNo) { + builder + .setCancelable(false) + .setPositiveButton("OK", (dialog, whichButton) -> + { + dialog.dismiss(); + synchronized (lock) { + lock.notify(); + } + }); + } else { + alertResult = false; + + builder + .setPositiveButton("Yes", (dialog, whichButton) -> + { + alertResult = true; + dialog.dismiss(); + synchronized (lock) { + lock.notify(); + } + }) + .setNegativeButton("No", (dialog, whichButton) -> + { + alertResult = false; + dialog.dismiss(); + synchronized (lock) { + lock.notify(); + } + }); + } + + // Show the AlertDialog on the main thread. + emulationActivity.runOnUiThread(() -> builder.show()); + + // Wait for the lock to notify that it is complete. + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + } + } + + if (yesNo) + result = alertResult; + } + return result; + } + + public static void setEmulationActivity(EmulationActivity emulationActivity) { + Log.verbose("[NativeLibrary] Registering EmulationActivity."); + sEmulationActivity = new WeakReference<>(emulationActivity); + } + + public static void clearEmulationActivity() { + Log.verbose("[NativeLibrary] Unregistering EmulationActivity."); + + sEmulationActivity.clear(); + } + + /** + * Button type for use in onTouchEvent + */ + public static final class ButtonType { + public static final int BUTTON_A = 700; + public static final int BUTTON_B = 701; + public static final int BUTTON_X = 702; + public static final int BUTTON_Y = 703; + public static final int BUTTON_START = 704; + public static final int BUTTON_SELECT = 705; + public static final int BUTTON_HOME = 706; + public static final int BUTTON_ZL = 707; + public static final int BUTTON_ZR = 708; + public static final int DPAD_UP = 709; + public static final int DPAD_DOWN = 710; + public static final int DPAD_LEFT = 711; + public static final int DPAD_RIGHT = 712; + public static final int STICK_LEFT = 713; + public static final int STICK_LEFT_UP = 714; + public static final int STICK_LEFT_DOWN = 715; + public static final int STICK_LEFT_LEFT = 716; + public static final int STICK_LEFT_RIGHT = 717; + public static final int STICK_C = 718; + public static final int STICK_C_UP = 719; + public static final int STICK_C_DOWN = 720; + public static final int STICK_C_LEFT = 771; + public static final int STICK_C_RIGHT = 772; + public static final int TRIGGER_L = 773; + public static final int TRIGGER_R = 774; + } + + /** + * Button states + */ + public static final class ButtonState { + public static final int RELEASED = 0; + public static final int PRESSED = 1; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/activities/CustomFilePickerActivity.java b/src/android/app/src/main/java/org/citra/citra_android/activities/CustomFilePickerActivity.java index 06b5a4886..0c97ba182 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/activities/CustomFilePickerActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/activities/CustomFilePickerActivity.java @@ -10,20 +10,17 @@ import org.citra.citra_android.fragments.CustomFilePickerFragment; import java.io.File; -public class CustomFilePickerActivity extends FilePickerActivity - -{ - @Override - protected AbstractFilePickerFragment getFragment( - @Nullable final String startPath, final int mode, final boolean allowMultiple, - final boolean allowCreateDir, final boolean allowExistingFile, - final boolean singleClick) - { - AbstractFilePickerFragment fragment = new CustomFilePickerFragment(); - // startPath is allowed to be null. In that case, default folder should be SD-card and not "/" - fragment.setArgs( - startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(), - mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick); - return fragment; - } +public class CustomFilePickerActivity extends FilePickerActivity { + @Override + protected AbstractFilePickerFragment getFragment( + @Nullable final String startPath, final int mode, final boolean allowMultiple, + final boolean allowCreateDir, final boolean allowExistingFile, + final boolean singleClick) { + AbstractFilePickerFragment fragment = new CustomFilePickerFragment(); + // startPath is allowed to be null. In that case, default folder should be SD-card and not "/" + fragment.setArgs( + startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(), + mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick); + return fragment; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java b/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java index 125a44bff..7ef66e835 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java @@ -12,7 +12,6 @@ import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v7.app.AppCompatActivity; -import android.text.TextUtils; import android.util.SparseIntArray; import android.view.InputDevice; import android.view.KeyEvent; @@ -46,604 +45,522 @@ import static android.view.MotionEvent.AXIS_Y; import static android.view.MotionEvent.AXIS_Z; import static java.lang.annotation.RetentionPolicy.SOURCE; -public final class EmulationActivity extends AppCompatActivity -{ - private static final String BACKSTACK_NAME_MENU = "menu"; - private static final String BACKSTACK_NAME_SUBMENU = "submenu"; - public static final int REQUEST_CHANGE_DISC = 1; +public final class EmulationActivity extends AppCompatActivity { + public static final int REQUEST_CHANGE_DISC = 1; + public static final String EXTRA_SELECTED_GAME = "SelectedGame"; + public static final String EXTRA_SELECTED_TITLE = "SelectedTitle"; + public static final String EXTRA_SCREEN_PATH = "ScreenPath"; + public static final String EXTRA_GRID_POSITION = "GridPosition"; + public static final int MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0; + public static final int MENU_ACTION_TOGGLE_CONTROLS = 1; + public static final int MENU_ACTION_ADJUST_SCALE = 2; + public static final int MENU_ACTION_EXIT = 3; + public static final int MENU_ACTION_TOGGLE_PREF_STATS = 4; + public static final int MENU_ACTION_SWITCH_SCREEN_LAYOUT = 5; + public static final int MENU_ACTION_SWAP_SCREENS = 6; + public static final int MENU_ACTION_RESET_OVERLAY = 7; + private static final String BACKSTACK_NAME_MENU = "menu"; + private static final String BACKSTACK_NAME_SUBMENU = "submenu"; + private static SparseIntArray buttonsActionsMap = new SparseIntArray(); - private View mDecorView; - private ImageView mImageView; - private EmulationFragment mEmulationFragment; - - private SharedPreferences mPreferences; - private ControllerMappingHelper mControllerMappingHelper; - - // So that MainActivity knows which view to invalidate before the return animation. - private int mPosition; - - private boolean mDeviceHasTouchScreen; - private boolean mMenuVisible; - private boolean mBackPressedOnce; - - private boolean activityRecreated; - private String mScreenPath; - private String mSelectedTitle; - private String mPath; - - public static final String EXTRA_SELECTED_GAME = "SelectedGame"; - public static final String EXTRA_SELECTED_TITLE = "SelectedTitle"; - public static final String EXTRA_SCREEN_PATH = "ScreenPath"; - public static final String EXTRA_GRID_POSITION = "GridPosition"; - - @Retention(SOURCE) - @IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE, - MENU_ACTION_EXIT, MENU_ACTION_TOGGLE_PREF_STATS, MENU_ACTION_SWITCH_SCREEN_LAYOUT, - MENU_ACTION_SWAP_SCREENS, MENU_ACTION_RESET_OVERLAY}) - public @interface MenuAction - { - } - - public static final int MENU_ACTION_EDIT_CONTROLS_PLACEMENT = 0; - public static final int MENU_ACTION_TOGGLE_CONTROLS = 1; - public static final int MENU_ACTION_ADJUST_SCALE = 2; - public static final int MENU_ACTION_EXIT = 3; - public static final int MENU_ACTION_TOGGLE_PREF_STATS = 4; - public static final int MENU_ACTION_SWITCH_SCREEN_LAYOUT = 5; - public static final int MENU_ACTION_SWAP_SCREENS = 6; - public static final int MENU_ACTION_RESET_OVERLAY = 7; - - private static SparseIntArray buttonsActionsMap = new SparseIntArray(); - - static - { - buttonsActionsMap.append(R.id.menu_emulation_edit_layout, - EmulationActivity.MENU_ACTION_EDIT_CONTROLS_PLACEMENT); - buttonsActionsMap.append(R.id.menu_emulation_toggle_controls, - EmulationActivity.MENU_ACTION_TOGGLE_CONTROLS); - buttonsActionsMap - .append(R.id.menu_emulation_adjust_scale, EmulationActivity.MENU_ACTION_ADJUST_SCALE); - buttonsActionsMap.append(R.id.menu_emulation_toggle_perf_stats, - EmulationActivity.MENU_ACTION_TOGGLE_PREF_STATS); - buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); - buttonsActionsMap.append(R.id.menu_emulation_switch_screen_layout, - EmulationActivity.MENU_ACTION_SWITCH_SCREEN_LAYOUT); - buttonsActionsMap.append(R.id.menu_emulation_swap_screens, - EmulationActivity.MENU_ACTION_SWAP_SCREENS); - buttonsActionsMap - .append(R.id.menu_emulation_reset_overlay, EmulationActivity.MENU_ACTION_RESET_OVERLAY); - } - - public static void launch(FragmentActivity activity, String path, String title, - String screenshotPath, int position, View sharedView) - { - Intent launcher = new Intent(activity, EmulationActivity.class); - - launcher.putExtra(EXTRA_SELECTED_GAME, path); - launcher.putExtra(EXTRA_SELECTED_TITLE, title); - launcher.putExtra(EXTRA_SCREEN_PATH, screenshotPath); - launcher.putExtra(EXTRA_GRID_POSITION, position); - - ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( - activity, - sharedView, - "image_game_screenshot"); - - // I believe this warning is a bug. Activities are FragmentActivity from the support lib - //noinspection RestrictedApi - activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME, - options.toBundle()); - } - - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - if (savedInstanceState == null) - { - // Get params we were passed - Intent gameToEmulate = getIntent(); - mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME); - mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE); - mScreenPath = gameToEmulate.getStringExtra(EXTRA_SCREEN_PATH); - mPosition = gameToEmulate.getIntExtra(EXTRA_GRID_POSITION, -1); - activityRecreated = false; - } - else - { - activityRecreated = true; - restoreState(savedInstanceState); + static { + buttonsActionsMap.append(R.id.menu_emulation_edit_layout, + EmulationActivity.MENU_ACTION_EDIT_CONTROLS_PLACEMENT); + buttonsActionsMap.append(R.id.menu_emulation_toggle_controls, + EmulationActivity.MENU_ACTION_TOGGLE_CONTROLS); + buttonsActionsMap + .append(R.id.menu_emulation_adjust_scale, EmulationActivity.MENU_ACTION_ADJUST_SCALE); + buttonsActionsMap.append(R.id.menu_emulation_toggle_perf_stats, + EmulationActivity.MENU_ACTION_TOGGLE_PREF_STATS); + buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); + buttonsActionsMap.append(R.id.menu_emulation_switch_screen_layout, + EmulationActivity.MENU_ACTION_SWITCH_SCREEN_LAYOUT); + buttonsActionsMap.append(R.id.menu_emulation_swap_screens, + EmulationActivity.MENU_ACTION_SWAP_SCREENS); + buttonsActionsMap + .append(R.id.menu_emulation_reset_overlay, EmulationActivity.MENU_ACTION_RESET_OVERLAY); } - mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen"); - mControllerMappingHelper = new ControllerMappingHelper(); - - int themeId; - if (mDeviceHasTouchScreen) - { - themeId = R.style.CitraEmulation; - - // Get a handle to the Window containing the UI. - mDecorView = getWindow().getDecorView(); - mDecorView.setOnSystemUiVisibilityChangeListener(visibility -> - { - if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) - { - // Go back to immersive fullscreen mode in 3s - Handler handler = new Handler(getMainLooper()); - handler.postDelayed(this::enableFullscreenImmersive, 3000 /* 3s */); + private View mDecorView; + private ImageView mImageView; + private EmulationFragment mEmulationFragment; + private SharedPreferences mPreferences; + private ControllerMappingHelper mControllerMappingHelper; + // So that MainActivity knows which view to invalidate before the return animation. + private int mPosition; + private boolean mDeviceHasTouchScreen; + private boolean mMenuVisible; + private boolean mBackPressedOnce; + private boolean activityRecreated; + private String mScreenPath; + private String mSelectedTitle; + private String mPath; + private Runnable afterShowingScreenshot = new Runnable() { + @Override + public void run() { + setResult(mPosition); + supportFinishAfterTransition(); } - }); - // Set these options now so that the SurfaceView the game renders into is the right size. - enableFullscreenImmersive(); - } - else - { - themeId = R.style.CitraEmulationTv; + }; + + public static void launch(FragmentActivity activity, String path, String title, + String screenshotPath, int position, View sharedView) { + Intent launcher = new Intent(activity, EmulationActivity.class); + + launcher.putExtra(EXTRA_SELECTED_GAME, path); + launcher.putExtra(EXTRA_SELECTED_TITLE, title); + launcher.putExtra(EXTRA_SCREEN_PATH, screenshotPath); + launcher.putExtra(EXTRA_GRID_POSITION, position); + + ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation( + activity, + sharedView, + "image_game_screenshot"); + + // I believe this warning is a bug. Activities are FragmentActivity from the support lib + //noinspection RestrictedApi + activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME, + options.toBundle()); } - setTheme(themeId); - - setContentView(R.layout.activity_emulation); - - mImageView = findViewById(R.id.image_screenshot); - - // Find or create the EmulationFragment - mEmulationFragment = (EmulationFragment) getSupportFragmentManager() - .findFragmentById(R.id.frame_emulation_fragment); - if (mEmulationFragment == null) - { - mEmulationFragment = EmulationFragment.newInstance(mPath); - getSupportFragmentManager().beginTransaction() - .add(R.id.frame_emulation_fragment, mEmulationFragment) - .commit(); - } - - if (savedInstanceState == null) - { - // Picasso will take a while to load these big-ass screenshots. So don't run - // the animation until we say so. - postponeEnterTransition(); - - Picasso.with(this) - .load(mScreenPath) - .noFade() - .noPlaceholder() - .into(mImageView, new Callback() - { - @Override - public void onSuccess() - { - supportStartPostponedEnterTransition(); - } - - @Override - public void onError() - { - // Still have to do this, or else the app will crash. - supportStartPostponedEnterTransition(); - } - }); - - Animations.fadeViewOut(mImageView) - .setStartDelay(2000) - .withEndAction(() -> mImageView.setVisibility(View.GONE)); - } - else - { - mImageView.setVisibility(View.GONE); - } - - if (mDeviceHasTouchScreen) - { - setTitle(mSelectedTitle); - } - - mPreferences = PreferenceManager.getDefaultSharedPreferences(this); - - } - - @Override - protected void onSaveInstanceState(Bundle outState) - { - outState.putString(EXTRA_SELECTED_GAME, mPath); - outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle); - outState.putString(EXTRA_SCREEN_PATH, mScreenPath); - outState.putInt(EXTRA_GRID_POSITION, mPosition); - super.onSaveInstanceState(outState); - } - - protected void restoreState(Bundle savedInstanceState) - { - mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME); - mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE); - mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH); - mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION); - } - - @Override - public void onBackPressed() - { - if (!mDeviceHasTouchScreen) - { - boolean popResult = getSupportFragmentManager().popBackStackImmediate( - BACKSTACK_NAME_SUBMENU, FragmentManager.POP_BACK_STACK_INCLUSIVE); - if (!popResult) - { - toggleMenu(); - } - } - else - { - if (mBackPressedOnce) { - mEmulationFragment.stopEmulation(); - exitWithAnimation(); - } - else - { - mBackPressedOnce = true; - Toast.makeText(this, "Press back again to exit", Toast.LENGTH_SHORT).show(); - } - - Handler mHandler = new Handler(); - mHandler.postDelayed(new Runnable() - { - @Override public void run() - { - mBackPressedOnce = false; - mHandler.removeCallbacks(this); - } - }, 2000); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent result) - { - } - - private void enableFullscreenImmersive() - { - // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar. - mDecorView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE | - View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | - View.SYSTEM_UI_FLAG_FULLSCREEN | - View.SYSTEM_UI_FLAG_IMMERSIVE); - } - - private void toggleMenu() - { - boolean result = getSupportFragmentManager().popBackStackImmediate( - BACKSTACK_NAME_MENU, FragmentManager.POP_BACK_STACK_INCLUSIVE); - mMenuVisible = false; - - if (!result) - { - // Removing the menu failed, so that means it wasn't visible. Add it. - Fragment fragment = MenuFragment.newInstance(mSelectedTitle); - getSupportFragmentManager().beginTransaction() - .setCustomAnimations( - R.animator.menu_slide_in_from_left, - R.animator.menu_slide_out_to_left, - R.animator.menu_slide_in_from_left, - R.animator.menu_slide_out_to_left) - .add(R.id.frame_menu, fragment) - .addToBackStack(BACKSTACK_NAME_MENU) - .commit(); - mMenuVisible = true; - } - } - - public void exitWithAnimation() - { - runOnUiThread(() -> - { - Picasso.with(EmulationActivity.this) - .invalidate(mScreenPath); - - Picasso.with(EmulationActivity.this) - .load(mScreenPath) - .noFade() - .noPlaceholder() - .into(mImageView, new Callback() - { - @Override - public void onSuccess() - { - showScreenshot(); - } - - @Override - public void onError() - { - finish(); - } - }); - }); - } - - private void showScreenshot() - { - Animations.fadeViewIn(mImageView) - .withEndAction(afterShowingScreenshot); - } - - private Runnable afterShowingScreenshot = new Runnable() - { @Override - public void run() - { - setResult(mPosition); - supportFinishAfterTransition(); - } - }; + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_emulation, menu); - return true; - } - - @SuppressWarnings("WrongConstant") - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - int action = buttonsActionsMap.get(item.getItemId(), -1); - if (action >= 0) - { - handleMenuAction(action); - } - return true; - } - - public void handleMenuAction(@MenuAction int menuAction) - { - switch (menuAction) - { - // Edit the placement of the controls - case MENU_ACTION_EDIT_CONTROLS_PLACEMENT: - editControlsPlacement(); - break; - - // Enable/Disable specific buttons or the entire input overlay. - case MENU_ACTION_TOGGLE_CONTROLS: - toggleControls(); - return; - - // Adjust the scale of the overlay controls. - case MENU_ACTION_ADJUST_SCALE: - adjustScale(); - return; - - // Toggle the visibility of the Performance stats TextView - case MENU_ACTION_TOGGLE_PREF_STATS: - mEmulationFragment.togglePerfStatsVisibility(); - return; - - // Switch the layout of the screens - case MENU_ACTION_SWITCH_SCREEN_LAYOUT: - NativeLibrary.SwitchScreenLayout(); - return; - - // Swap the top and bottom screen locations - case MENU_ACTION_SWAP_SCREENS: - NativeLibrary.SwapScreens(); - return; - - // Reset overlay placement - case MENU_ACTION_RESET_OVERLAY: - resetOverlay(); - break; - - case MENU_ACTION_EXIT: - toggleMenu(); // Hide the menu (it will be showing since we just clicked it) - mEmulationFragment.stopEmulation(); - exitWithAnimation(); - return; - } - } - - - private void editControlsPlacement() - { - if (mEmulationFragment.isConfiguringControls()) - { - mEmulationFragment.stopConfiguringControls(); - } - else - { - mEmulationFragment.startConfiguringControls(); - } - } - - // Gets button presses - @Override - public boolean dispatchKeyEvent(KeyEvent event) - { - if (mMenuVisible) - { - return super.dispatchKeyEvent(event); - } - - int action; - - switch (event.getAction()) - { - case KeyEvent.ACTION_DOWN: - // Handling the case where the back button is pressed. - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) - { - onBackPressed(); - return true; + if (savedInstanceState == null) { + // Get params we were passed + Intent gameToEmulate = getIntent(); + mPath = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAME); + mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE); + mScreenPath = gameToEmulate.getStringExtra(EXTRA_SCREEN_PATH); + mPosition = gameToEmulate.getIntExtra(EXTRA_GRID_POSITION, -1); + activityRecreated = false; + } else { + activityRecreated = true; + restoreState(savedInstanceState); + } + + mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen"); + mControllerMappingHelper = new ControllerMappingHelper(); + + int themeId; + if (mDeviceHasTouchScreen) { + themeId = R.style.CitraEmulation; + + // Get a handle to the Window containing the UI. + mDecorView = getWindow().getDecorView(); + mDecorView.setOnSystemUiVisibilityChangeListener(visibility -> + { + if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) { + // Go back to immersive fullscreen mode in 3s + Handler handler = new Handler(getMainLooper()); + handler.postDelayed(this::enableFullscreenImmersive, 3000 /* 3s */); + } + }); + // Set these options now so that the SurfaceView the game renders into is the right size. + enableFullscreenImmersive(); + } else { + themeId = R.style.CitraEmulationTv; + } + + setTheme(themeId); + + setContentView(R.layout.activity_emulation); + + mImageView = findViewById(R.id.image_screenshot); + + // Find or create the EmulationFragment + mEmulationFragment = (EmulationFragment) getSupportFragmentManager() + .findFragmentById(R.id.frame_emulation_fragment); + if (mEmulationFragment == null) { + mEmulationFragment = EmulationFragment.newInstance(mPath); + getSupportFragmentManager().beginTransaction() + .add(R.id.frame_emulation_fragment, mEmulationFragment) + .commit(); + } + + if (savedInstanceState == null) { + // Picasso will take a while to load these big-ass screenshots. So don't run + // the animation until we say so. + postponeEnterTransition(); + + Picasso.with(this) + .load(mScreenPath) + .noFade() + .noPlaceholder() + .into(mImageView, new Callback() { + @Override + public void onSuccess() { + supportStartPostponedEnterTransition(); + } + + @Override + public void onError() { + // Still have to do this, or else the app will crash. + supportStartPostponedEnterTransition(); + } + }); + + Animations.fadeViewOut(mImageView) + .setStartDelay(2000) + .withEndAction(() -> mImageView.setVisibility(View.GONE)); + } else { + mImageView.setVisibility(View.GONE); + } + + if (mDeviceHasTouchScreen) { + setTitle(mSelectedTitle); + } + + mPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + outState.putString(EXTRA_SELECTED_GAME, mPath); + outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle); + outState.putString(EXTRA_SCREEN_PATH, mScreenPath); + outState.putInt(EXTRA_GRID_POSITION, mPosition); + super.onSaveInstanceState(outState); + } + + protected void restoreState(Bundle savedInstanceState) { + mPath = savedInstanceState.getString(EXTRA_SELECTED_GAME); + mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE); + mScreenPath = savedInstanceState.getString(EXTRA_SCREEN_PATH); + mPosition = savedInstanceState.getInt(EXTRA_GRID_POSITION); + } + + @Override + public void onBackPressed() { + if (!mDeviceHasTouchScreen) { + boolean popResult = getSupportFragmentManager().popBackStackImmediate( + BACKSTACK_NAME_SUBMENU, FragmentManager.POP_BACK_STACK_INCLUSIVE); + if (!popResult) { + toggleMenu(); + } + } else { + if (mBackPressedOnce) { + mEmulationFragment.stopEmulation(); + exitWithAnimation(); + } else { + mBackPressedOnce = true; + Toast.makeText(this, "Press back again to exit", Toast.LENGTH_SHORT).show(); + } + + Handler mHandler = new Handler(); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + mBackPressedOnce = false; + mHandler.removeCallbacks(this); + } + }, 2000); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent result) { + } + + private void enableFullscreenImmersive() { + // It would be nice to use IMMERSIVE_STICKY, but that doesn't show the toolbar. + mDecorView.setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_FULLSCREEN | + View.SYSTEM_UI_FLAG_IMMERSIVE); + } + + private void toggleMenu() { + boolean result = getSupportFragmentManager().popBackStackImmediate( + BACKSTACK_NAME_MENU, FragmentManager.POP_BACK_STACK_INCLUSIVE); + mMenuVisible = false; + + if (!result) { + // Removing the menu failed, so that means it wasn't visible. Add it. + Fragment fragment = MenuFragment.newInstance(mSelectedTitle); + getSupportFragmentManager().beginTransaction() + .setCustomAnimations( + R.animator.menu_slide_in_from_left, + R.animator.menu_slide_out_to_left, + R.animator.menu_slide_in_from_left, + R.animator.menu_slide_out_to_left) + .add(R.id.frame_menu, fragment) + .addToBackStack(BACKSTACK_NAME_MENU) + .commit(); + mMenuVisible = true; + } + } + + public void exitWithAnimation() { + runOnUiThread(() -> + { + Picasso.with(EmulationActivity.this) + .invalidate(mScreenPath); + + Picasso.with(EmulationActivity.this) + .load(mScreenPath) + .noFade() + .noPlaceholder() + .into(mImageView, new Callback() { + @Override + public void onSuccess() { + showScreenshot(); + } + + @Override + public void onError() { + finish(); + } + }); + }); + } + + private void showScreenshot() { + Animations.fadeViewIn(mImageView) + .withEndAction(afterShowingScreenshot); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_emulation, menu); + return true; + } + + @SuppressWarnings("WrongConstant") + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int action = buttonsActionsMap.get(item.getItemId(), -1); + if (action >= 0) { + handleMenuAction(action); + } + return true; + } + + public void handleMenuAction(@MenuAction int menuAction) { + switch (menuAction) { + // Edit the placement of the controls + case MENU_ACTION_EDIT_CONTROLS_PLACEMENT: + editControlsPlacement(); + break; + + // Enable/Disable specific buttons or the entire input overlay. + case MENU_ACTION_TOGGLE_CONTROLS: + toggleControls(); + return; + + // Adjust the scale of the overlay controls. + case MENU_ACTION_ADJUST_SCALE: + adjustScale(); + return; + + // Toggle the visibility of the Performance stats TextView + case MENU_ACTION_TOGGLE_PREF_STATS: + mEmulationFragment.togglePerfStatsVisibility(); + return; + + // Switch the layout of the screens + case MENU_ACTION_SWITCH_SCREEN_LAYOUT: + NativeLibrary.SwitchScreenLayout(); + return; + + // Swap the top and bottom screen locations + case MENU_ACTION_SWAP_SCREENS: + NativeLibrary.SwapScreens(); + return; + + // Reset overlay placement + case MENU_ACTION_RESET_OVERLAY: + resetOverlay(); + break; + + case MENU_ACTION_EXIT: + toggleMenu(); // Hide the menu (it will be showing since we just clicked it) + mEmulationFragment.stopEmulation(); + exitWithAnimation(); + return; + } + } + + private void editControlsPlacement() { + if (mEmulationFragment.isConfiguringControls()) { + mEmulationFragment.stopConfiguringControls(); + } else { + mEmulationFragment.startConfiguringControls(); + } + } + + // Gets button presses + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (mMenuVisible) { + return super.dispatchKeyEvent(event); + } + + int action; + + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + // Handling the case where the back button is pressed. + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + onBackPressed(); + return true; + } + + // Normal key events. + action = NativeLibrary.ButtonState.PRESSED; + break; + case KeyEvent.ACTION_UP: + action = NativeLibrary.ButtonState.RELEASED; + break; + default: + return false; + } + InputDevice input = event.getDevice(); + return NativeLibrary.onGamePadEvent(input.getDescriptor(), event.getKeyCode(), action); + } + + private void toggleControls() { + final SharedPreferences.Editor editor = mPreferences.edit(); + boolean[] enabledButtons = new boolean[14]; + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.emulation_toggle_controls); + + for (int i = 0; i < enabledButtons.length; i++) { + enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, true); + } + builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons, + (dialog, indexSelected, isChecked) -> editor + .putBoolean("buttonToggle" + indexSelected, isChecked)); + + + builder.setNeutralButton(getString(R.string.emulation_toggle_all), + (dialogInterface, i) -> mEmulationFragment.toggleInputOverlayVisibility()); + builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> + { + editor.apply(); + + mEmulationFragment.refreshInputOverlay(); + }); + + AlertDialog alertDialog = builder.create(); + alertDialog.show(); + } + + private void adjustScale() { + LayoutInflater inflater = LayoutInflater.from(this); + View view = inflater.inflate(R.layout.dialog_seekbar, null); + + final SeekBar seekbar = view.findViewById(R.id.seekbar); + final TextView value = view.findViewById(R.id.text_value); + final TextView units = view.findViewById(R.id.text_units); + + seekbar.setMax(150); + seekbar.setProgress(mPreferences.getInt("controlScale", 50)); + seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + public void onStartTrackingTouch(SeekBar seekBar) { + // Do nothing + } + + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + value.setText(String.valueOf(progress + 50)); + } + + public void onStopTrackingTouch(SeekBar seekBar) { + // Do nothing + } + }); + + value.setText(String.valueOf(seekbar.getProgress() + 50)); + units.setText("%"); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.emulation_control_scale); + builder.setView(view); + builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> + { + SharedPreferences.Editor editor = mPreferences.edit(); + editor.putInt("controlScale", seekbar.getProgress()); + editor.apply(); + + mEmulationFragment.refreshInputOverlay(); + }); + + AlertDialog alertDialog = builder.create(); + alertDialog.show(); + } + + private void resetOverlay() { + new AlertDialog.Builder(this) + .setTitle(getString(R.string.emulation_touch_overlay_reset)) + .setPositiveButton(R.string.yes, (dialogInterface, i) -> + { + mEmulationFragment.resetInputOverlay(); + }) + .setNegativeButton(R.string.cancel, (dialogInterface, i) -> + { + }) + .create() + .show(); + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + if (mMenuVisible) { + return false; + } + + if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)) { + return super.dispatchGenericMotionEvent(event); + } + + // Don't attempt to do anything if we are disconnecting a device. + if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) + return true; + + InputDevice input = event.getDevice(); + List motions = input.getMotionRanges(); + + float[] axisValues = {0.0f, 0.0f}; + for (InputDevice.MotionRange range : motions) { + boolean consumed = false; + int axis = range.getAxis(); + float origValue = event.getAxisValue(axis); + float value = mControllerMappingHelper.scaleAxis(input, axis, origValue); + + if (axis == AXIS_X || axis == AXIS_Z) { + axisValues[0] = value; + } else if (axis == AXIS_Y || axis == AXIS_RZ) { + axisValues[1] = value; + } + + // If the input is still in the "flat" area, that means it's really zero. + // This is used to compensate for imprecision in joysticks. + if (Math.abs(axisValues[0]) > range.getFlat() || Math.abs(axisValues[1]) > range.getFlat()) { + consumed = NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, axisValues[0], axisValues[1]); + } else { + consumed = NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, 0.0f, 0.0f); + } + + return NativeLibrary.onGamePadAxisEvent(input.getDescriptor(), axis, value) || consumed; } - // Normal key events. - action = NativeLibrary.ButtonState.PRESSED; - break; - case KeyEvent.ACTION_UP: - action = NativeLibrary.ButtonState.RELEASED; - break; - default: return false; } - InputDevice input = event.getDevice(); - return NativeLibrary.onGamePadEvent(input.getDescriptor(), event.getKeyCode(), action); - } - private void toggleControls() - { - final SharedPreferences.Editor editor = mPreferences.edit(); - boolean[] enabledButtons = new boolean[14]; - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.emulation_toggle_controls); - - for (int i = 0; i < enabledButtons.length; i++) - { - enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, true); - } - builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons, - (dialog, indexSelected, isChecked) -> editor - .putBoolean("buttonToggle" + indexSelected, isChecked)); - - - builder.setNeutralButton(getString(R.string.emulation_toggle_all), - (dialogInterface, i) -> mEmulationFragment.toggleInputOverlayVisibility()); - builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> - { - editor.apply(); - - mEmulationFragment.refreshInputOverlay(); - }); - - AlertDialog alertDialog = builder.create(); - alertDialog.show(); - } - - private void adjustScale() - { - LayoutInflater inflater = LayoutInflater.from(this); - View view = inflater.inflate(R.layout.dialog_seekbar, null); - - final SeekBar seekbar = view.findViewById(R.id.seekbar); - final TextView value = view.findViewById(R.id.text_value); - final TextView units = view.findViewById(R.id.text_units); - - seekbar.setMax(150); - seekbar.setProgress(mPreferences.getInt("controlScale", 50)); - seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() - { - public void onStartTrackingTouch(SeekBar seekBar) - { - // Do nothing - } - - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) - { - value.setText(String.valueOf(progress + 50)); - } - - public void onStopTrackingTouch(SeekBar seekBar) - { - // Do nothing - } - }); - - value.setText(String.valueOf(seekbar.getProgress() + 50)); - units.setText("%"); - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.emulation_control_scale); - builder.setView(view); - builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) -> - { - SharedPreferences.Editor editor = mPreferences.edit(); - editor.putInt("controlScale", seekbar.getProgress()); - editor.apply(); - - mEmulationFragment.refreshInputOverlay(); - }); - - AlertDialog alertDialog = builder.create(); - alertDialog.show(); - } - - private void resetOverlay() - { - new AlertDialog.Builder(this) - .setTitle(getString(R.string.emulation_touch_overlay_reset)) - .setPositiveButton(R.string.yes, (dialogInterface, i) -> - { - mEmulationFragment.resetInputOverlay(); - }) - .setNegativeButton(R.string.cancel, (dialogInterface, i) -> - { - }) - .create() - .show(); - } - - @Override - public boolean dispatchGenericMotionEvent(MotionEvent event) - { - if (mMenuVisible) - { - return false; + public boolean isActivityRecreated() { + return activityRecreated; } - if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)) - { - return super.dispatchGenericMotionEvent(event); + @Retention(SOURCE) + @IntDef({MENU_ACTION_EDIT_CONTROLS_PLACEMENT, MENU_ACTION_TOGGLE_CONTROLS, MENU_ACTION_ADJUST_SCALE, + MENU_ACTION_EXIT, MENU_ACTION_TOGGLE_PREF_STATS, MENU_ACTION_SWITCH_SCREEN_LAYOUT, + MENU_ACTION_SWAP_SCREENS, MENU_ACTION_RESET_OVERLAY}) + public @interface MenuAction { } - - // Don't attempt to do anything if we are disconnecting a device. - if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) - return true; - - InputDevice input = event.getDevice(); - List motions = input.getMotionRanges(); - - float[] axisValues = {0.0f, 0.0f}; - for (InputDevice.MotionRange range : motions) - { - boolean consumed = false; - int axis = range.getAxis(); - float origValue = event.getAxisValue(axis); - float value = mControllerMappingHelper.scaleAxis(input, axis, origValue); - - if (axis == AXIS_X || axis == AXIS_Z) - { - axisValues[0] = value; - } - else if (axis == AXIS_Y || axis == AXIS_RZ) - { - axisValues[1] = value; - } - - // If the input is still in the "flat" area, that means it's really zero. - // This is used to compensate for imprecision in joysticks. - if (Math.abs(axisValues[0]) > range.getFlat() || Math.abs(axisValues[1]) > range.getFlat()) - { - consumed = NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, axisValues[0], axisValues[1]); - } - else - { - consumed = NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, 0.0f, 0.0f); - } - - return NativeLibrary.onGamePadAxisEvent(input.getDescriptor(),axis, value) || consumed; - } - - return false; - } - - public boolean isActivityRecreated() - { - return activityRecreated; - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/adapters/GameAdapter.java b/src/android/app/src/main/java/org/citra/citra_android/adapters/GameAdapter.java index bd17e758e..2445ec1ed 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/adapters/GameAdapter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/adapters/GameAdapter.java @@ -22,230 +22,200 @@ import org.citra.citra_android.viewholders.GameViewHolder; * large dataset. */ public final class GameAdapter extends RecyclerView.Adapter implements - View.OnClickListener -{ - private Cursor mCursor; - private GameDataSetObserver mObserver; + View.OnClickListener { + private Cursor mCursor; + private GameDataSetObserver mObserver; - private boolean mDatasetValid; + private boolean mDatasetValid; - /** - * Initializes the adapter's observer, which watches for changes to the dataset. The adapter will - * display no data until a Cursor is supplied by a CursorLoader. - */ - public GameAdapter() - { - mDatasetValid = false; - mObserver = new GameDataSetObserver(); - } - - /** - * Called by the LayoutManager when it is necessary to create a new view. - * - * @param parent The RecyclerView (I think?) the created view will be thrown into. - * @param viewType Not used here, but useful when more than one type of child will be used in the RecyclerView. - * @return The created ViewHolder with references to all the child view's members. - */ - @Override - public GameViewHolder onCreateViewHolder(ViewGroup parent, int viewType) - { - // Create a new view. - View gameCard = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.card_game, parent, false); - - gameCard.setOnClickListener(this); - - // Use that view to create a ViewHolder. - return new GameViewHolder(gameCard); - } - - /** - * Called by the LayoutManager when a new view is not necessary because we can recycle - * an existing one (for example, if a view just scrolled onto the screen from the bottom, we - * can use the view that just scrolled off the top instead of inflating a new one.) - * - * @param holder A ViewHolder representing the view we're recycling. - * @param position The position of the 'new' view in the dataset. - */ - @Override - public void onBindViewHolder(GameViewHolder holder, int position) - { - if (mDatasetValid) - { - if (mCursor.moveToPosition(position)) - { - String screenPath = mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH); - PicassoUtils.loadGameBanner(holder.imageScreenshot, screenPath, - mCursor.getString(GameDatabase.GAME_COLUMN_PATH)); - - holder.textGameTitle.setText(mCursor.getString(GameDatabase.GAME_COLUMN_TITLE)); - holder.textCompany.setText(mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY)); - - // TODO These shouldn't be necessary once the move to a DB-based model is complete. - holder.gameId = mCursor.getString(GameDatabase.GAME_COLUMN_GAME_ID); - holder.path = mCursor.getString(GameDatabase.GAME_COLUMN_PATH); - holder.title = mCursor.getString(GameDatabase.GAME_COLUMN_TITLE); - holder.description = mCursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION); - holder.country = mCursor.getInt(GameDatabase.GAME_COLUMN_COUNTRY); - holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY); - holder.screenshotPath = mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH); - } - else - { - Log.error("[GameAdapter] Can't bind view; Cursor is not valid."); - } - } - else - { - Log.error("[GameAdapter] Can't bind view; dataset is not valid."); - } - } - - /** - * Called by the LayoutManager to find out how much data we have. - * - * @return Size of the dataset. - */ - @Override - public int getItemCount() - { - if (mDatasetValid && mCursor != null) - { - return mCursor.getCount(); - } - Log.error("[GameAdapter] Dataset is not valid."); - return 0; - } - - /** - * Return the contents of the _id column for a given row. - * - * @param position The row for which Android wants an ID. - * @return A valid ID from the database, or 0 if not available. - */ - @Override - public long getItemId(int position) - { - if (mDatasetValid && mCursor != null) - { - if (mCursor.moveToPosition(position)) - { - return mCursor.getLong(GameDatabase.COLUMN_DB_ID); - } - } - - Log.error("[GameAdapter] Dataset is not valid."); - return 0; - } - - /** - * Tell Android whether or not each item in the dataset has a stable identifier. - * Which it does, because it's a database, so always tell Android 'true'. - * - * @param hasStableIds ignored. - */ - @Override - public void setHasStableIds(boolean hasStableIds) - { - super.setHasStableIds(true); - } - - /** - * When a load is finished, call this to replace the existing data with the newly-loaded - * data. - * - * @param cursor The newly-loaded Cursor. - */ - public void swapCursor(Cursor cursor) - { - // Sanity check. - if (cursor == mCursor) - { - return; - } - - // Before getting rid of the old cursor, disassociate it from the Observer. - final Cursor oldCursor = mCursor; - if (oldCursor != null && mObserver != null) - { - oldCursor.unregisterDataSetObserver(mObserver); - } - - mCursor = cursor; - if (mCursor != null) - { - // Attempt to associate the new Cursor with the Observer. - if (mObserver != null) - { - mCursor.registerDataSetObserver(mObserver); - } - - mDatasetValid = true; - } - else - { - mDatasetValid = false; - } - - notifyDataSetChanged(); - } - - /** - * Launches the game that was clicked on. - * - * @param view The card representing the game the user wants to play. - */ - @Override - public void onClick(View view) - { - GameViewHolder holder = (GameViewHolder) view.getTag(); - - EmulationActivity.launch((FragmentActivity) view.getContext(), - holder.path, - holder.title, - holder.screenshotPath, - holder.getAdapterPosition(), - holder.imageScreenshot); - } - - public static class SpacesItemDecoration extends RecyclerView.ItemDecoration - { - private int space; - - public SpacesItemDecoration(int space) - { - this.space = space; + /** + * Initializes the adapter's observer, which watches for changes to the dataset. The adapter will + * display no data until a Cursor is supplied by a CursorLoader. + */ + public GameAdapter() { + mDatasetValid = false; + mObserver = new GameDataSetObserver(); } + /** + * Called by the LayoutManager when it is necessary to create a new view. + * + * @param parent The RecyclerView (I think?) the created view will be thrown into. + * @param viewType Not used here, but useful when more than one type of child will be used in the RecyclerView. + * @return The created ViewHolder with references to all the child view's members. + */ @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, - RecyclerView.State state) - { - outRect.left = space; - outRect.right = space; - outRect.bottom = space; - outRect.top = space; - } - } + public GameViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + // Create a new view. + View gameCard = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.card_game, parent, false); - private final class GameDataSetObserver extends DataSetObserver - { + gameCard.setOnClickListener(this); + + // Use that view to create a ViewHolder. + return new GameViewHolder(gameCard); + } + + /** + * Called by the LayoutManager when a new view is not necessary because we can recycle + * an existing one (for example, if a view just scrolled onto the screen from the bottom, we + * can use the view that just scrolled off the top instead of inflating a new one.) + * + * @param holder A ViewHolder representing the view we're recycling. + * @param position The position of the 'new' view in the dataset. + */ @Override - public void onChanged() - { - super.onChanged(); + public void onBindViewHolder(GameViewHolder holder, int position) { + if (mDatasetValid) { + if (mCursor.moveToPosition(position)) { + String screenPath = mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH); + PicassoUtils.loadGameBanner(holder.imageScreenshot, screenPath, + mCursor.getString(GameDatabase.GAME_COLUMN_PATH)); - mDatasetValid = true; - notifyDataSetChanged(); + holder.textGameTitle.setText(mCursor.getString(GameDatabase.GAME_COLUMN_TITLE)); + holder.textCompany.setText(mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY)); + + // TODO These shouldn't be necessary once the move to a DB-based model is complete. + holder.gameId = mCursor.getString(GameDatabase.GAME_COLUMN_GAME_ID); + holder.path = mCursor.getString(GameDatabase.GAME_COLUMN_PATH); + holder.title = mCursor.getString(GameDatabase.GAME_COLUMN_TITLE); + holder.description = mCursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION); + holder.country = mCursor.getInt(GameDatabase.GAME_COLUMN_COUNTRY); + holder.company = mCursor.getString(GameDatabase.GAME_COLUMN_COMPANY); + holder.screenshotPath = mCursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH); + } else { + Log.error("[GameAdapter] Can't bind view; Cursor is not valid."); + } + } else { + Log.error("[GameAdapter] Can't bind view; dataset is not valid."); + } } + /** + * Called by the LayoutManager to find out how much data we have. + * + * @return Size of the dataset. + */ @Override - public void onInvalidated() - { - super.onInvalidated(); - - mDatasetValid = false; - notifyDataSetChanged(); + public int getItemCount() { + if (mDatasetValid && mCursor != null) { + return mCursor.getCount(); + } + Log.error("[GameAdapter] Dataset is not valid."); + return 0; + } + + /** + * Return the contents of the _id column for a given row. + * + * @param position The row for which Android wants an ID. + * @return A valid ID from the database, or 0 if not available. + */ + @Override + public long getItemId(int position) { + if (mDatasetValid && mCursor != null) { + if (mCursor.moveToPosition(position)) { + return mCursor.getLong(GameDatabase.COLUMN_DB_ID); + } + } + + Log.error("[GameAdapter] Dataset is not valid."); + return 0; + } + + /** + * Tell Android whether or not each item in the dataset has a stable identifier. + * Which it does, because it's a database, so always tell Android 'true'. + * + * @param hasStableIds ignored. + */ + @Override + public void setHasStableIds(boolean hasStableIds) { + super.setHasStableIds(true); + } + + /** + * When a load is finished, call this to replace the existing data with the newly-loaded + * data. + * + * @param cursor The newly-loaded Cursor. + */ + public void swapCursor(Cursor cursor) { + // Sanity check. + if (cursor == mCursor) { + return; + } + + // Before getting rid of the old cursor, disassociate it from the Observer. + final Cursor oldCursor = mCursor; + if (oldCursor != null && mObserver != null) { + oldCursor.unregisterDataSetObserver(mObserver); + } + + mCursor = cursor; + if (mCursor != null) { + // Attempt to associate the new Cursor with the Observer. + if (mObserver != null) { + mCursor.registerDataSetObserver(mObserver); + } + + mDatasetValid = true; + } else { + mDatasetValid = false; + } + + notifyDataSetChanged(); + } + + /** + * Launches the game that was clicked on. + * + * @param view The card representing the game the user wants to play. + */ + @Override + public void onClick(View view) { + GameViewHolder holder = (GameViewHolder) view.getTag(); + + EmulationActivity.launch((FragmentActivity) view.getContext(), + holder.path, + holder.title, + holder.screenshotPath, + holder.getAdapterPosition(), + holder.imageScreenshot); + } + + public static class SpacesItemDecoration extends RecyclerView.ItemDecoration { + private int space; + + public SpacesItemDecoration(int space) { + this.space = space; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, + RecyclerView.State state) { + outRect.left = space; + outRect.right = space; + outRect.bottom = space; + outRect.top = space; + } + } + + private final class GameDataSetObserver extends DataSetObserver { + @Override + public void onChanged() { + super.onChanged(); + + mDatasetValid = true; + notifyDataSetChanged(); + } + + @Override + public void onInvalidated() { + super.onInvalidated(); + + mDatasetValid = false; + notifyDataSetChanged(); + } } - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/adapters/GameRowPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/adapters/GameRowPresenter.java index bff54864b..e4cec0a80 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/adapters/GameRowPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/adapters/GameRowPresenter.java @@ -17,57 +17,53 @@ import org.citra.citra_android.viewholders.TvGameViewHolder; * The Leanback library / docs call this a Presenter, but it works very * similarly to a RecyclerView.Adapter. */ -public final class GameRowPresenter extends Presenter -{ - @Override - public ViewHolder onCreateViewHolder(ViewGroup parent) - { - // Create a new view. - ImageCardView gameCard = new ImageCardView(parent.getContext()); +public final class GameRowPresenter extends Presenter { + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent) { + // Create a new view. + ImageCardView gameCard = new ImageCardView(parent.getContext()); - gameCard.setMainImageAdjustViewBounds(true); - gameCard.setMainImageDimensions(48, 48); - gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP); + gameCard.setMainImageAdjustViewBounds(true); + gameCard.setMainImageDimensions(48, 48); + gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP); - gameCard.setFocusable(true); - gameCard.setFocusableInTouchMode(true); + gameCard.setFocusable(true); + gameCard.setFocusableInTouchMode(true); - // Use that view to create a ViewHolder. - return new TvGameViewHolder(gameCard); - } + // Use that view to create a ViewHolder. + return new TvGameViewHolder(gameCard); + } - @Override - public void onBindViewHolder(ViewHolder viewHolder, Object item) - { - TvGameViewHolder holder = (TvGameViewHolder) viewHolder; - Game game = (Game) item; + @Override + public void onBindViewHolder(ViewHolder viewHolder, Object item) { + TvGameViewHolder holder = (TvGameViewHolder) viewHolder; + Game game = (Game) item; - String screenPath = game.getScreenshotPath(); + String screenPath = game.getScreenshotPath(); - holder.imageScreenshot.setImageDrawable(null); - PicassoUtils.loadGameBanner(holder.imageScreenshot, screenPath, game.getPath()); + holder.imageScreenshot.setImageDrawable(null); + PicassoUtils.loadGameBanner(holder.imageScreenshot, screenPath, game.getPath()); - holder.cardParent.setTitleText(game.getTitle()); - holder.cardParent.setContentText(game.getCompany()); + holder.cardParent.setTitleText(game.getTitle()); + holder.cardParent.setContentText(game.getCompany()); - // TODO These shouldn't be necessary once the move to a DB-based model is complete. - holder.gameId = game.getGameId(); - holder.path = game.getPath(); - holder.title = game.getTitle(); - holder.description = game.getDescription(); - holder.country = game.getCountry(); - holder.company = game.getCompany(); - holder.screenshotPath = game.getScreenshotPath(); + // TODO These shouldn't be necessary once the move to a DB-based model is complete. + holder.gameId = game.getGameId(); + holder.path = game.getPath(); + holder.title = game.getTitle(); + holder.description = game.getDescription(); + holder.country = game.getCountry(); + holder.company = game.getCompany(); + holder.screenshotPath = game.getScreenshotPath(); - // Set the platform-dependent background color of the card - Context context = holder.cardParent.getContext(); - Drawable background = ContextCompat.getDrawable(context, R.drawable.tv_card_background_gamecube); - holder.cardParent.setInfoAreaBackground(background); - } + // Set the platform-dependent background color of the card + Context context = holder.cardParent.getContext(); + Drawable background = ContextCompat.getDrawable(context, R.drawable.tv_card_background_gamecube); + holder.cardParent.setInfoAreaBackground(background); + } - @Override - public void onUnbindViewHolder(ViewHolder viewHolder) - { - // no op - } + @Override + public void onUnbindViewHolder(ViewHolder viewHolder) { + // no op + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/adapters/SettingsRowPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/adapters/SettingsRowPresenter.java index 77c8c9ad8..309d4d595 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/adapters/SettingsRowPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/adapters/SettingsRowPresenter.java @@ -8,39 +8,35 @@ import android.view.ViewGroup; import org.citra.citra_android.model.TvSettingsItem; import org.citra.citra_android.viewholders.TvSettingsViewHolder; -public final class SettingsRowPresenter extends Presenter -{ - public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) - { - // Create a new view. - ImageCardView settingsCard = new ImageCardView(parent.getContext()); +public final class SettingsRowPresenter extends Presenter { + public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) { + // Create a new view. + ImageCardView settingsCard = new ImageCardView(parent.getContext()); - settingsCard.setMainImageAdjustViewBounds(true); - settingsCard.setMainImageDimensions(192, 160); + settingsCard.setMainImageAdjustViewBounds(true); + settingsCard.setMainImageDimensions(192, 160); - settingsCard.setFocusable(true); - settingsCard.setFocusableInTouchMode(true); + settingsCard.setFocusable(true); + settingsCard.setFocusableInTouchMode(true); - // Use that view to create a ViewHolder. - return new TvSettingsViewHolder(settingsCard); - } + // Use that view to create a ViewHolder. + return new TvSettingsViewHolder(settingsCard); + } - public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) - { - TvSettingsViewHolder holder = (TvSettingsViewHolder) viewHolder; - TvSettingsItem settingsItem = (TvSettingsItem) item; + public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) { + TvSettingsViewHolder holder = (TvSettingsViewHolder) viewHolder; + TvSettingsItem settingsItem = (TvSettingsItem) item; - Resources resources = holder.cardParent.getResources(); + Resources resources = holder.cardParent.getResources(); - holder.itemId = settingsItem.getItemId(); + holder.itemId = settingsItem.getItemId(); - holder.cardParent.setTitleText(resources.getString(settingsItem.getLabelId())); - holder.cardParent.setMainImage(resources.getDrawable(settingsItem.getIconId(), null)); - } + holder.cardParent.setTitleText(resources.getString(settingsItem.getLabelId())); + holder.cardParent.setMainImage(resources.getDrawable(settingsItem.getIconId(), null)); + } - public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) - { - // no op - } + public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) { + // no op + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/dialogs/GameDetailsDialog.java b/src/android/app/src/main/java/org/citra/citra_android/dialogs/GameDetailsDialog.java index 7e62dcae7..de78485f9 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/dialogs/GameDetailsDialog.java +++ b/src/android/app/src/main/java/org/citra/citra_android/dialogs/GameDetailsDialog.java @@ -16,82 +16,79 @@ import org.citra.citra_android.activities.EmulationActivity; import de.hdodenhof.circleimageview.CircleImageView; -public final class GameDetailsDialog extends DialogFragment -{ - private static final String ARG_GAME_TITLE = "game_title"; - private static final String ARG_GAME_DESCRIPTION = "game_description"; - private static final String ARG_GAME_COUNTRY = "game_country"; - private static final String ARG_GAME_DATE = "game_date"; - private static final String ARG_GAME_PATH = "game_path"; - private static final String ARG_GAME_SCREENSHOT_PATH = "game_screenshot_path"; +public final class GameDetailsDialog extends DialogFragment { + private static final String ARG_GAME_TITLE = "game_title"; + private static final String ARG_GAME_DESCRIPTION = "game_description"; + private static final String ARG_GAME_COUNTRY = "game_country"; + private static final String ARG_GAME_DATE = "game_date"; + private static final String ARG_GAME_PATH = "game_path"; + private static final String ARG_GAME_SCREENSHOT_PATH = "game_screenshot_path"; - // TODO Add all of this to the Loader in GameActivity.java - public static GameDetailsDialog newInstance(String title, String description, int country, - String company, String path, String screenshotPath) - { - GameDetailsDialog fragment = new GameDetailsDialog(); + // TODO Add all of this to the Loader in GameActivity.java + public static GameDetailsDialog newInstance(String title, String description, int country, + String company, String path, String screenshotPath) { + GameDetailsDialog fragment = new GameDetailsDialog(); - Bundle arguments = new Bundle(); - arguments.putString(ARG_GAME_TITLE, title); - arguments.putString(ARG_GAME_DESCRIPTION, description); - arguments.putInt(ARG_GAME_COUNTRY, country); - arguments.putString(ARG_GAME_DATE, company); - arguments.putString(ARG_GAME_PATH, path); - arguments.putString(ARG_GAME_SCREENSHOT_PATH, screenshotPath); - fragment.setArguments(arguments); + Bundle arguments = new Bundle(); + arguments.putString(ARG_GAME_TITLE, title); + arguments.putString(ARG_GAME_DESCRIPTION, description); + arguments.putInt(ARG_GAME_COUNTRY, country); + arguments.putString(ARG_GAME_DATE, company); + arguments.putString(ARG_GAME_PATH, path); + arguments.putString(ARG_GAME_SCREENSHOT_PATH, screenshotPath); + fragment.setArguments(arguments); - return fragment; - } + return fragment; + } - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) - { - AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); - ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater() - .inflate(R.layout.dialog_game_details, null); + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater() + .inflate(R.layout.dialog_game_details, null); - final ImageView imageGameScreen = contents.findViewById(R.id.image_game_screen); - CircleImageView circleBanner = contents.findViewById(R.id.circle_banner); + final ImageView imageGameScreen = contents.findViewById(R.id.image_game_screen); + CircleImageView circleBanner = contents.findViewById(R.id.circle_banner); - TextView textTitle = contents.findViewById(R.id.text_game_title); - TextView textDescription = contents.findViewById(R.id.text_company); + TextView textTitle = contents.findViewById(R.id.text_game_title); + TextView textDescription = contents.findViewById(R.id.text_company); - TextView textCountry = contents.findViewById(R.id.text_country); - TextView textDate = contents.findViewById(R.id.text_date); + TextView textCountry = contents.findViewById(R.id.text_country); + TextView textDate = contents.findViewById(R.id.text_date); - FloatingActionButton buttonLaunch = contents.findViewById(R.id.button_launch); + FloatingActionButton buttonLaunch = contents.findViewById(R.id.button_launch); - int countryIndex = getArguments().getInt(ARG_GAME_COUNTRY); - String country = getResources().getStringArray(R.array.countryNames)[countryIndex]; + int countryIndex = getArguments().getInt(ARG_GAME_COUNTRY); + String country = getResources().getStringArray(R.array.countryNames)[countryIndex]; - textTitle.setText(getArguments().getString(ARG_GAME_TITLE)); - textDescription.setText(getArguments().getString(ARG_GAME_DESCRIPTION)); - textCountry.setText(country); - textDate.setText(getArguments().getString(ARG_GAME_DATE)); + textTitle.setText(getArguments().getString(ARG_GAME_TITLE)); + textDescription.setText(getArguments().getString(ARG_GAME_DESCRIPTION)); + textCountry.setText(country); + textDate.setText(getArguments().getString(ARG_GAME_DATE)); - buttonLaunch.setOnClickListener(view -> - { - // Start the emulation activity and send the path of the clicked ROM to it. - EmulationActivity.launch(getActivity(), - getArguments().getString(ARG_GAME_PATH), - getArguments().getString(ARG_GAME_TITLE), - getArguments().getString(ARG_GAME_SCREENSHOT_PATH), - -1, - imageGameScreen); - }); + buttonLaunch.setOnClickListener(view -> + { + // Start the emulation activity and send the path of the clicked ROM to it. + EmulationActivity.launch(getActivity(), + getArguments().getString(ARG_GAME_PATH), + getArguments().getString(ARG_GAME_TITLE), + getArguments().getString(ARG_GAME_SCREENSHOT_PATH), + -1, + imageGameScreen); + }); - // Fill in the view contents. - Picasso.with(imageGameScreen.getContext()) - .load(getArguments().getString(ARG_GAME_SCREENSHOT_PATH)) - .fit() - .centerCrop() - .noFade() - .noPlaceholder() - .into(imageGameScreen); + // Fill in the view contents. + Picasso.with(imageGameScreen.getContext()) + .load(getArguments().getString(ARG_GAME_SCREENSHOT_PATH)) + .fit() + .centerCrop() + .noFade() + .noPlaceholder() + .into(imageGameScreen); - circleBanner.setImageResource(R.drawable.no_banner); + circleBanner.setImageResource(R.drawable.no_banner); - builder.setView(contents); - return builder.create(); - } + builder.setView(contents); + return builder.create(); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/dialogs/MotionAlertDialog.java b/src/android/app/src/main/java/org/citra/citra_android/dialogs/MotionAlertDialog.java index 25421d034..4fbf32b96 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/dialogs/MotionAlertDialog.java +++ b/src/android/app/src/main/java/org/citra/citra_android/dialogs/MotionAlertDialog.java @@ -18,157 +18,141 @@ import java.util.List; * {@link AlertDialog} derivative that listens for * motion events from controllers and joysticks. */ -public final class MotionAlertDialog extends AlertDialog -{ - // The selected input preference - private final InputBindingSetting setting; - private final ControllerMappingHelper mControllerMappingHelper; - private boolean mWaitingForEvent = true; +public final class MotionAlertDialog extends AlertDialog { + // The selected input preference + private final InputBindingSetting setting; + private final ControllerMappingHelper mControllerMappingHelper; + private boolean mWaitingForEvent = true; - /** - * Constructor - * - * @param context The current {@link Context}. - * @param setting The Preference to show this dialog for. - */ - public MotionAlertDialog(Context context, InputBindingSetting setting) - { - super(context); + /** + * Constructor + * + * @param context The current {@link Context}. + * @param setting The Preference to show this dialog for. + */ + public MotionAlertDialog(Context context, InputBindingSetting setting) { + super(context); - this.setting = setting; - this.mControllerMappingHelper = new ControllerMappingHelper(); - } + this.setting = setting; + this.mControllerMappingHelper = new ControllerMappingHelper(); + } - public boolean onKeyEvent(int keyCode, KeyEvent event) - { - Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); - switch (event.getAction()) - { - case KeyEvent.ACTION_DOWN: - if (!mControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode)) - { - saveKeyInput(event); + public boolean onKeyEvent(int keyCode, KeyEvent event) { + Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); + switch (event.getAction()) { + case KeyEvent.ACTION_DOWN: + if (!mControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode)) { + saveKeyInput(event); + } + // Even if we ignore the key, we still consume it. Thus return true regardless. + return true; + + default: + return false; } - // Even if we ignore the key, we still consume it. Thus return true regardless. + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + // Handle this key if we care about it, otherwise pass it down the framework + return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + // Handle this event if we care about it, otherwise pass it down the framework + return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); + } + + private boolean onMotionEvent(MotionEvent event) { + if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) + return false; + if (event.getAction() != MotionEvent.ACTION_MOVE) + return false; + + InputDevice input = event.getDevice(); + + List motionRanges = input.getMotionRanges(); + + int numMovedAxis = 0; + float axisMoveValue = 0.0f; + InputDevice.MotionRange lastMovedRange = null; + char lastMovedDir = '?'; + if (mWaitingForEvent) { + // Get only the axis that seem to have moved (more than .5) + for (InputDevice.MotionRange range : motionRanges) { + int axis = range.getAxis(); + float origValue = event.getAxisValue(axis); + float value = mControllerMappingHelper.scaleAxis(input, axis, origValue); + if (Math.abs(value) > 0.5f) { + // It is common to have multiple axis with the same physical input. For example, + // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. + // To handle this, we ignore an axis motion that's the exact same as a motion + // we already saw. This way, we ignore axis with two names, but catch the case + // where a joystick is moved in two directions. + // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html + if (value != axisMoveValue) { + axisMoveValue = value; + numMovedAxis++; + lastMovedRange = range; + lastMovedDir = value < 0.0f ? '-' : '+'; + } + } + } + + // If only one axis moved, that's the winner. + if (numMovedAxis == 1) { + mWaitingForEvent = false; + saveMotionInput(input, lastMovedRange, lastMovedDir); + } + } + return true; - - default: - return false; - } - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) - { - // Handle this key if we care about it, otherwise pass it down the framework - return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); - } - - @Override - public boolean dispatchGenericMotionEvent(MotionEvent event) - { - // Handle this event if we care about it, otherwise pass it down the framework - return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); - } - - private boolean onMotionEvent(MotionEvent event) - { - if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) - return false; - if (event.getAction() != MotionEvent.ACTION_MOVE) - return false; - - InputDevice input = event.getDevice(); - - List motionRanges = input.getMotionRanges(); - - int numMovedAxis = 0; - float axisMoveValue = 0.0f; - InputDevice.MotionRange lastMovedRange = null; - char lastMovedDir = '?'; - if (mWaitingForEvent) - { - // Get only the axis that seem to have moved (more than .5) - for (InputDevice.MotionRange range : motionRanges) - { - int axis = range.getAxis(); - float origValue = event.getAxisValue(axis); - float value = mControllerMappingHelper.scaleAxis(input, axis, origValue); - if (Math.abs(value) > 0.5f) - { - // It is common to have multiple axis with the same physical input. For example, - // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. - // To handle this, we ignore an axis motion that's the exact same as a motion - // we already saw. This way, we ignore axis with two names, but catch the case - // where a joystick is moved in two directions. - // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html - if (value != axisMoveValue) - { - axisMoveValue = value; - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = value < 0.0f ? '-' : '+'; - } - } - } - - // If only one axis moved, that's the winner. - if (numMovedAxis == 1) - { - mWaitingForEvent = false; - saveMotionInput(input, lastMovedRange, lastMovedDir); - } } - return true; - } + /** + * Saves the provided key input setting both to the INI file (so native code can use it) and as + * an Android preference (so it persists correctly and is human-readable.) + * + * @param keyEvent KeyEvent of this key press. + */ + private void saveKeyInput(KeyEvent keyEvent) { + InputDevice device = keyEvent.getDevice(); + String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); + String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); - /** - * Saves the provided key input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param keyEvent KeyEvent of this key press. - */ - private void saveKeyInput(KeyEvent keyEvent) - { - InputDevice device = keyEvent.getDevice(); - String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); - String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); + saveInput(bindStr, uiString); + } - saveInput(bindStr, uiString); - } + /** + * Saves the provided motion input setting both to the INI file (so native code can use it) and as + * an Android preference (so it persists correctly and is human-readable.) + * + * @param device InputDevice from which the input event originated. + * @param motionRange MotionRange of the movement + * @param axisDir Either '-' or '+' + */ + private void saveMotionInput(InputDevice device, InputDevice.MotionRange motionRange, + char axisDir) { + String bindStr = + "Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir; + String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir; - /** - * Saves the provided motion input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param device InputDevice from which the input event originated. - * @param motionRange MotionRange of the movement - * @param axisDir Either '-' or '+' - */ - private void saveMotionInput(InputDevice device, InputDevice.MotionRange motionRange, - char axisDir) - { - String bindStr = - "Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir; - String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir; + saveInput(bindStr, uiString); + } - saveInput(bindStr, uiString); - } + /** + * Save the input string to settings and SharedPreferences, then dismiss this Dialog. + */ + private void saveInput(String bind, String ui) { + setting.setValue(bind); - /** - * Save the input string to settings and SharedPreferences, then dismiss this Dialog. - */ - private void saveInput(String bind, String ui) - { - setting.setValue(bind); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor editor = preferences.edit(); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); - SharedPreferences.Editor editor = preferences.edit(); + editor.putString(setting.getKey(), ui); + editor.apply(); - editor.putString(setting.getKey(), ui); - editor.apply(); - - dismiss(); - } + dismiss(); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/fragments/CustomFilePickerFragment.java b/src/android/app/src/main/java/org/citra/citra_android/fragments/CustomFilePickerFragment.java index f2bc01a38..9861577a8 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/fragments/CustomFilePickerFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_android/fragments/CustomFilePickerFragment.java @@ -8,15 +8,13 @@ import com.nononsenseapps.filepicker.FilePickerFragment; import java.io.File; -public class CustomFilePickerFragment extends FilePickerFragment -{ - @NonNull - @Override - public Uri toUri(@NonNull final File file) - { - return FileProvider - .getUriForFile(getContext(), - getContext().getApplicationContext().getPackageName() + ".filesprovider", - file); - } +public class CustomFilePickerFragment extends FilePickerFragment { + @NonNull + @Override + public Uri toUri(@NonNull final File file) { + return FileProvider + .getUriForFile(getContext(), + getContext().getApplicationContext().getPackageName() + ".filesprovider", + file); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/fragments/EmulationFragment.java b/src/android/app/src/main/java/org/citra/citra_android/fragments/EmulationFragment.java index c79305254..ee1d91d88 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/fragments/EmulationFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_android/fragments/EmulationFragment.java @@ -27,465 +27,378 @@ import org.citra.citra_android.services.DirectoryInitializationService.Directory import org.citra.citra_android.utils.DirectoryStateReceiver; import org.citra.citra_android.utils.Log; -public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback -{ - private static final String KEY_GAMEPATH = "gamepath"; +public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback { + private static final String KEY_GAMEPATH = "gamepath"; - private static final Handler perfStatsUpdateHandler = new Handler(); + private static final Handler perfStatsUpdateHandler = new Handler(); - private SharedPreferences mPreferences; + private SharedPreferences mPreferences; - private InputOverlay mInputOverlay; + private InputOverlay mInputOverlay; - private EmulationState mEmulationState; + private EmulationState mEmulationState; - private DirectoryStateReceiver directoryStateReceiver; + private DirectoryStateReceiver directoryStateReceiver; - private EmulationActivity activity; + private EmulationActivity activity; - private TextView mPerfStats; + private TextView mPerfStats; - private Runnable perfStatsUpdater; + private Runnable perfStatsUpdater; - public static EmulationFragment newInstance(String gamePath) - { + public static EmulationFragment newInstance(String gamePath) { - Bundle args = new Bundle(); - args.putString(KEY_GAMEPATH, gamePath); + Bundle args = new Bundle(); + args.putString(KEY_GAMEPATH, gamePath); - EmulationFragment fragment = new EmulationFragment(); - fragment.setArguments(args); - return fragment; - } - - @Override - public void onAttach(Context context) - { - super.onAttach(context); - - if (context instanceof EmulationActivity) - { - activity = (EmulationActivity) context; - NativeLibrary.setEmulationActivity((EmulationActivity) context); - } - else - { - throw new IllegalStateException("EmulationFragment must have EmulationActivity parent"); - } - } - - /** - * Initialize anything that doesn't depend on the layout / views in here. - */ - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - - // So this fragment doesn't restart on configuration changes; i.e. rotation. - setRetainInstance(true); - - mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); - - String gamePath = getArguments().getString(KEY_GAMEPATH); - mEmulationState = new EmulationState(gamePath); - } - - /** - * Initialize the UI and start emulation in here. - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) - { - View contents = inflater.inflate(R.layout.fragment_emulation, container, false); - - SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation); - surfaceView.getHolder().addCallback(this); - - mInputOverlay = contents.findViewById(R.id.surface_input_overlay); - if (mInputOverlay != null) - { - // If the input overlay was previously disabled, then don't show it. - if (!mPreferences.getBoolean("showInputOverlay", true)) - { - mInputOverlay.setVisibility(View.GONE); - } + EmulationFragment fragment = new EmulationFragment(); + fragment.setArguments(args); + return fragment; } - Button doneButton = contents.findViewById(R.id.done_control_config); - if (doneButton != null) - { - doneButton.setOnClickListener(v -> stopConfiguringControls()); - } + @Override + public void onAttach(Context context) { + super.onAttach(context); - mPerfStats = contents.findViewById(R.id.perf_stats_text); - if (mPerfStats != null) - { - // If the overlay was previously disabled, then don't show it. - if (!mPreferences.getBoolean("showPerfStats", true)) - { - mPerfStats.setVisibility(View.GONE); - } - else - { - updatePerfStats(); - } - } - - // The new Surface created here will get passed to the native code via onSurfaceChanged. - - return contents; - } - - @Override - public void onResume() - { - super.onResume(); - if (DirectoryInitializationService.areDolphinDirectoriesReady()) - { - mEmulationState.run(activity.isActivityRecreated()); - } - else - { - setupDolphinDirectoriesThenStartEmulation(); - } - } - - @Override - public void onPause() - { - if (directoryStateReceiver != null) - { - LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(directoryStateReceiver); - directoryStateReceiver = null; - } - - mEmulationState.pause(); - super.onPause(); - } - - @Override - public void onDetach() - { - NativeLibrary.clearEmulationActivity(); - super.onDetach(); - } - - private void setupDolphinDirectoriesThenStartEmulation() - { - IntentFilter statusIntentFilter = new IntentFilter( - DirectoryInitializationService.BROADCAST_ACTION); - - directoryStateReceiver = - new DirectoryStateReceiver(directoryInitializationState -> - { - if (directoryInitializationState == - DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) - { - mEmulationState.run(activity.isActivityRecreated()); - } - else if (directoryInitializationState == - DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) - { - Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT) - .show(); - } - else if (directoryInitializationState == - DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) - { - Toast.makeText(getContext(), R.string.external_storage_not_mounted, - Toast.LENGTH_SHORT) - .show(); - } - }); - - // Registers the DirectoryStateReceiver and its intent filters - LocalBroadcastManager.getInstance(getActivity()).registerReceiver( - directoryStateReceiver, - statusIntentFilter); - DirectoryInitializationService.startService(getActivity()); - } - - public void toggleInputOverlayVisibility() - { - SharedPreferences.Editor editor = mPreferences.edit(); - - // If the overlay is currently set to INVISIBLE - if (!mPreferences.getBoolean("showInputOverlay", false)) - { - // Set it to VISIBLE - mInputOverlay.setVisibility(View.VISIBLE); - editor.putBoolean("showInputOverlay", true); - } - else - { - // Set it to INVISIBLE - mInputOverlay.setVisibility(View.GONE); - editor.putBoolean("showInputOverlay", false); - } - - editor.apply(); - } - - public void refreshInputOverlay() - { - mInputOverlay.refreshControls(); - } - - public void resetInputOverlay() - { - mInputOverlay.resetButtonPlacement(); - } - - public void togglePerfStatsVisibility() - { - SharedPreferences.Editor editor = mPreferences.edit(); - - // If the overlay is currently set to INVISIBLE - if (!mPreferences.getBoolean("showPerfStats", false)) - { - updatePerfStats(); - // Set it to VISIBLE - mPerfStats.setVisibility(View.VISIBLE); - editor.putBoolean("showPerfStats", true); - } - else - { - stopPerfStatsUpdates(); - // Set it to INVISIBLE - mPerfStats.setVisibility(View.GONE); - editor.putBoolean("showPerfStats", false); - } - - editor.apply(); - } - - private void updatePerfStats() - { - final int SYSTEM_FPS = 0; - final int FPS = 1; - final int FRAMETIME = 2; - final int SPEED = 3; - - perfStatsUpdater = () -> - { - double perfStats[] = NativeLibrary.GetPerfStats(); - mPerfStats - .setText(String.format("FPS: %.5s\nFrametime: %.7sms\nSpeed: %.4s%%", perfStats[FPS], - perfStats[FRAMETIME] * 1000.0, perfStats[SPEED] * 100.0)); - - perfStatsUpdateHandler.postDelayed(perfStatsUpdater, 3000 /* 3s */); - }; - perfStatsUpdateHandler.post(perfStatsUpdater); - } - - private void stopPerfStatsUpdates() - { - if (perfStatsUpdater != null) - { - perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater); - } - } - - @Override - public void surfaceCreated(SurfaceHolder holder) - { - // We purposely don't do anything here. - // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation. - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) - { - Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height); - mEmulationState.newSurface(holder.getSurface()); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) - { - mEmulationState.clearSurface(); - } - - public void stopEmulation() - { - mEmulationState.stop(); - } - - public void startConfiguringControls() - { - getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE); - mInputOverlay.setIsInEditMode(true); - } - - public void stopConfiguringControls() - { - getView().findViewById(R.id.done_control_config).setVisibility(View.GONE); - mInputOverlay.setIsInEditMode(false); - } - - public boolean isConfiguringControls() - { - return mInputOverlay.isInEditMode(); - } - - private static class EmulationState - { - private enum State - { - STOPPED, RUNNING, PAUSED - } - - private final String mGamePath; - private Thread mEmulationThread; - private State state; - private Surface mSurface; - private boolean mRunWhenSurfaceIsValid; - - EmulationState(String gamePath) - { - mGamePath = gamePath; - // Starting state is stopped. - state = State.STOPPED; - } - - // Getters for the current state - - public synchronized boolean isStopped() - { - return state == State.STOPPED; - } - - public synchronized boolean isPaused() - { - return state == State.PAUSED; - } - - public synchronized boolean isRunning() - { - return state == State.RUNNING; - } - - // State changing methods - - public synchronized void stop() - { - if (state != State.STOPPED) - { - Log.debug("[EmulationFragment] Stopping emulation."); - state = State.STOPPED; - NativeLibrary.StopEmulation(); - } - else - { - Log.warning("[EmulationFragment] Stop called while already stopped."); - } - } - - public synchronized void pause() - { - if (state != State.PAUSED) - { - state = State.PAUSED; - Log.debug("[EmulationFragment] Pausing emulation."); - - // Release the surface before pausing, since emulation has to be running for that. - NativeLibrary.SurfaceDestroyed(); - NativeLibrary.PauseEmulation(); - } - else - { - Log.warning("[EmulationFragment] Pause called while already paused."); - } - } - - public synchronized void run(boolean isActivityRecreated) - { - if (isActivityRecreated) - { - if (NativeLibrary.IsRunning()) - { - state = State.PAUSED; + if (context instanceof EmulationActivity) { + activity = (EmulationActivity) context; + NativeLibrary.setEmulationActivity((EmulationActivity) context); + } else { + throw new IllegalStateException("EmulationFragment must have EmulationActivity parent"); } - } - else - { - Log.debug("[EmulationFragment] activity resumed or fresh start"); - } - - // If the surface is set, run now. Otherwise, wait for it to get set. - if (mSurface != null) - { - runWithValidSurface(); - } - else - { - mRunWhenSurfaceIsValid = true; - } } - // Surface callbacks - public synchronized void newSurface(Surface surface) - { - mSurface = surface; - if (mRunWhenSurfaceIsValid) - { - runWithValidSurface(); - } + /** + * Initialize anything that doesn't depend on the layout / views in here. + */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // So this fragment doesn't restart on configuration changes; i.e. rotation. + setRetainInstance(true); + + mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); + + String gamePath = getArguments().getString(KEY_GAMEPATH); + mEmulationState = new EmulationState(gamePath); } - public synchronized void clearSurface() - { - if (mSurface == null) - { - Log.warning("[EmulationFragment] clearSurface called, but surface already null."); - } - else - { - mSurface = null; - Log.debug("[EmulationFragment] Surface destroyed."); + /** + * Initialize the UI and start emulation in here. + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View contents = inflater.inflate(R.layout.fragment_emulation, container, false); - if (state == State.RUNNING) - { - NativeLibrary.SurfaceDestroyed(); - state = State.PAUSED; + SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation); + surfaceView.getHolder().addCallback(this); + + mInputOverlay = contents.findViewById(R.id.surface_input_overlay); + if (mInputOverlay != null) { + // If the input overlay was previously disabled, then don't show it. + if (!mPreferences.getBoolean("showInputOverlay", true)) { + mInputOverlay.setVisibility(View.GONE); + } } - else if (state == State.PAUSED) - { - Log.warning("[EmulationFragment] Surface cleared while emulation paused."); + + Button doneButton = contents.findViewById(R.id.done_control_config); + if (doneButton != null) { + doneButton.setOnClickListener(v -> stopConfiguringControls()); } - else - { - Log.warning("[EmulationFragment] Surface cleared while emulation stopped."); + + mPerfStats = contents.findViewById(R.id.perf_stats_text); + if (mPerfStats != null) { + // If the overlay was previously disabled, then don't show it. + if (!mPreferences.getBoolean("showPerfStats", true)) { + mPerfStats.setVisibility(View.GONE); + } else { + updatePerfStats(); + } } - } + + // The new Surface created here will get passed to the native code via onSurfaceChanged. + + return contents; } - private void runWithValidSurface() - { - mRunWhenSurfaceIsValid = false; - if (state == State.STOPPED) - { - mEmulationThread = new Thread(() -> - { - NativeLibrary.SurfaceChanged(mSurface); - Log.debug("[EmulationFragment] Starting emulation thread."); - NativeLibrary.Run(mGamePath); - }, "NativeEmulation"); - mEmulationThread.start(); - - } - else if (state == State.PAUSED) - { - Log.debug("[EmulationFragment] Resuming emulation."); - NativeLibrary.SurfaceChanged(mSurface); - NativeLibrary.UnPauseEmulation(); - } - else - { - Log.debug("[EmulationFragment] Bug, run called while already running."); - } - state = State.RUNNING; + @Override + public void onResume() { + super.onResume(); + if (DirectoryInitializationService.areDolphinDirectoriesReady()) { + mEmulationState.run(activity.isActivityRecreated()); + } else { + setupDolphinDirectoriesThenStartEmulation(); + } + } + + @Override + public void onPause() { + if (directoryStateReceiver != null) { + LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(directoryStateReceiver); + directoryStateReceiver = null; + } + + mEmulationState.pause(); + super.onPause(); + } + + @Override + public void onDetach() { + NativeLibrary.clearEmulationActivity(); + super.onDetach(); + } + + private void setupDolphinDirectoriesThenStartEmulation() { + IntentFilter statusIntentFilter = new IntentFilter( + DirectoryInitializationService.BROADCAST_ACTION); + + directoryStateReceiver = + new DirectoryStateReceiver(directoryInitializationState -> + { + if (directoryInitializationState == + DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) { + mEmulationState.run(activity.isActivityRecreated()); + } else if (directoryInitializationState == + DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) { + Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show(); + } else if (directoryInitializationState == + DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) { + Toast.makeText(getContext(), R.string.external_storage_not_mounted, + Toast.LENGTH_SHORT) + .show(); + } + }); + + // Registers the DirectoryStateReceiver and its intent filters + LocalBroadcastManager.getInstance(getActivity()).registerReceiver( + directoryStateReceiver, + statusIntentFilter); + DirectoryInitializationService.startService(getActivity()); + } + + public void toggleInputOverlayVisibility() { + SharedPreferences.Editor editor = mPreferences.edit(); + + // If the overlay is currently set to INVISIBLE + if (!mPreferences.getBoolean("showInputOverlay", false)) { + // Set it to VISIBLE + mInputOverlay.setVisibility(View.VISIBLE); + editor.putBoolean("showInputOverlay", true); + } else { + // Set it to INVISIBLE + mInputOverlay.setVisibility(View.GONE); + editor.putBoolean("showInputOverlay", false); + } + + editor.apply(); + } + + public void refreshInputOverlay() { + mInputOverlay.refreshControls(); + } + + public void resetInputOverlay() { + mInputOverlay.resetButtonPlacement(); + } + + public void togglePerfStatsVisibility() { + SharedPreferences.Editor editor = mPreferences.edit(); + + // If the overlay is currently set to INVISIBLE + if (!mPreferences.getBoolean("showPerfStats", false)) { + updatePerfStats(); + // Set it to VISIBLE + mPerfStats.setVisibility(View.VISIBLE); + editor.putBoolean("showPerfStats", true); + } else { + stopPerfStatsUpdates(); + // Set it to INVISIBLE + mPerfStats.setVisibility(View.GONE); + editor.putBoolean("showPerfStats", false); + } + + editor.apply(); + } + + private void updatePerfStats() { + final int SYSTEM_FPS = 0; + final int FPS = 1; + final int FRAMETIME = 2; + final int SPEED = 3; + + perfStatsUpdater = () -> + { + double[] perfStats = NativeLibrary.GetPerfStats(); + mPerfStats + .setText(String.format("FPS: %.5s\nFrametime: %.7sms\nSpeed: %.4s%%", perfStats[FPS], + perfStats[FRAMETIME] * 1000.0, perfStats[SPEED] * 100.0)); + + perfStatsUpdateHandler.postDelayed(perfStatsUpdater, 3000 /* 3s */); + }; + perfStatsUpdateHandler.post(perfStatsUpdater); + } + + private void stopPerfStatsUpdates() { + if (perfStatsUpdater != null) { + perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + // We purposely don't do anything here. + // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation. + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height); + mEmulationState.newSurface(holder.getSurface()); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mEmulationState.clearSurface(); + } + + public void stopEmulation() { + mEmulationState.stop(); + } + + public void startConfiguringControls() { + getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE); + mInputOverlay.setIsInEditMode(true); + } + + public void stopConfiguringControls() { + getView().findViewById(R.id.done_control_config).setVisibility(View.GONE); + mInputOverlay.setIsInEditMode(false); + } + + public boolean isConfiguringControls() { + return mInputOverlay.isInEditMode(); + } + + private static class EmulationState { + private final String mGamePath; + private Thread mEmulationThread; + private State state; + private Surface mSurface; + private boolean mRunWhenSurfaceIsValid; + + EmulationState(String gamePath) { + mGamePath = gamePath; + // Starting state is stopped. + state = State.STOPPED; + } + + public synchronized boolean isStopped() { + return state == State.STOPPED; + } + + // Getters for the current state + + public synchronized boolean isPaused() { + return state == State.PAUSED; + } + + public synchronized boolean isRunning() { + return state == State.RUNNING; + } + + public synchronized void stop() { + if (state != State.STOPPED) { + Log.debug("[EmulationFragment] Stopping emulation."); + state = State.STOPPED; + NativeLibrary.StopEmulation(); + } else { + Log.warning("[EmulationFragment] Stop called while already stopped."); + } + } + + // State changing methods + + public synchronized void pause() { + if (state != State.PAUSED) { + state = State.PAUSED; + Log.debug("[EmulationFragment] Pausing emulation."); + + // Release the surface before pausing, since emulation has to be running for that. + NativeLibrary.SurfaceDestroyed(); + NativeLibrary.PauseEmulation(); + } else { + Log.warning("[EmulationFragment] Pause called while already paused."); + } + } + + public synchronized void run(boolean isActivityRecreated) { + if (isActivityRecreated) { + if (NativeLibrary.IsRunning()) { + state = State.PAUSED; + } + } else { + Log.debug("[EmulationFragment] activity resumed or fresh start"); + } + + // If the surface is set, run now. Otherwise, wait for it to get set. + if (mSurface != null) { + runWithValidSurface(); + } else { + mRunWhenSurfaceIsValid = true; + } + } + + // Surface callbacks + public synchronized void newSurface(Surface surface) { + mSurface = surface; + if (mRunWhenSurfaceIsValid) { + runWithValidSurface(); + } + } + + public synchronized void clearSurface() { + if (mSurface == null) { + Log.warning("[EmulationFragment] clearSurface called, but surface already null."); + } else { + mSurface = null; + Log.debug("[EmulationFragment] Surface destroyed."); + + if (state == State.RUNNING) { + NativeLibrary.SurfaceDestroyed(); + state = State.PAUSED; + } else if (state == State.PAUSED) { + Log.warning("[EmulationFragment] Surface cleared while emulation paused."); + } else { + Log.warning("[EmulationFragment] Surface cleared while emulation stopped."); + } + } + } + + private void runWithValidSurface() { + mRunWhenSurfaceIsValid = false; + if (state == State.STOPPED) { + mEmulationThread = new Thread(() -> + { + NativeLibrary.SurfaceChanged(mSurface); + Log.debug("[EmulationFragment] Starting emulation thread."); + NativeLibrary.Run(mGamePath); + }, "NativeEmulation"); + mEmulationThread.start(); + + } else if (state == State.PAUSED) { + Log.debug("[EmulationFragment] Resuming emulation."); + NativeLibrary.SurfaceChanged(mSurface); + NativeLibrary.UnPauseEmulation(); + } else { + Log.debug("[EmulationFragment] Bug, run called while already running."); + } + state = State.RUNNING; + } + + private enum State { + STOPPED, RUNNING, PAUSED + } } - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/fragments/MenuFragment.java b/src/android/app/src/main/java/org/citra/citra_android/fragments/MenuFragment.java index 30c404f90..b987527b8 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/fragments/MenuFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_android/fragments/MenuFragment.java @@ -14,65 +14,57 @@ import android.widget.TextView; import org.citra.citra_android.R; import org.citra.citra_android.activities.EmulationActivity; -public final class MenuFragment extends Fragment implements View.OnClickListener -{ - private static final String KEY_TITLE = "title"; - private static SparseIntArray buttonsActionsMap = new SparseIntArray(); +public final class MenuFragment extends Fragment implements View.OnClickListener { + private static final String KEY_TITLE = "title"; + private static SparseIntArray buttonsActionsMap = new SparseIntArray(); - static - { - buttonsActionsMap.append(R.id.menu_emulation_toggle_perf_stats, - EmulationActivity.MENU_ACTION_TOGGLE_PREF_STATS); - buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); - buttonsActionsMap.append(R.id.menu_emulation_switch_screen_layout, - EmulationActivity.MENU_ACTION_SWITCH_SCREEN_LAYOUT); - buttonsActionsMap.append(R.id.menu_emulation_swap_screens, - EmulationActivity.MENU_ACTION_SWAP_SCREENS); - } - - public static MenuFragment newInstance(String title) - { - MenuFragment fragment = new MenuFragment(); - - Bundle arguments = new Bundle(); - arguments.putSerializable(KEY_TITLE, title); - fragment.setArguments(arguments); - - return fragment; - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) - { - View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false); - - LinearLayout options = rootView.findViewById(R.id.layout_options); - for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++) - { - Button button = (Button) options.getChildAt(childIndex); - - button.setOnClickListener(this); + static { + buttonsActionsMap.append(R.id.menu_emulation_toggle_perf_stats, + EmulationActivity.MENU_ACTION_TOGGLE_PREF_STATS); + buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT); + buttonsActionsMap.append(R.id.menu_emulation_switch_screen_layout, + EmulationActivity.MENU_ACTION_SWITCH_SCREEN_LAYOUT); + buttonsActionsMap.append(R.id.menu_emulation_swap_screens, + EmulationActivity.MENU_ACTION_SWAP_SCREENS); } - TextView titleText = rootView.findViewById(R.id.text_game_title); - String title = getArguments().getString(KEY_TITLE); - if (title != null) - { - titleText.setText(title); + public static MenuFragment newInstance(String title) { + MenuFragment fragment = new MenuFragment(); + + Bundle arguments = new Bundle(); + arguments.putSerializable(KEY_TITLE, title); + fragment.setArguments(arguments); + + return fragment; } - return rootView; - } + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false); - @SuppressWarnings("WrongConstant") - @Override - public void onClick(View button) - { - int action = buttonsActionsMap.get(button.getId()); - if (action >= 0) - { - ((EmulationActivity) getActivity()).handleMenuAction(action); + LinearLayout options = rootView.findViewById(R.id.layout_options); + for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++) { + Button button = (Button) options.getChildAt(childIndex); + + button.setOnClickListener(this); + } + + TextView titleText = rootView.findViewById(R.id.text_game_title); + String title = getArguments().getString(KEY_TITLE); + if (title != null) { + titleText.setText(title); + } + + return rootView; + } + + @SuppressWarnings("WrongConstant") + @Override + public void onClick(View button) { + int action = buttonsActionsMap.get(button.getId()); + if (action >= 0) { + ((EmulationActivity) getActivity()).handleMenuAction(action); + } } - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/Game.java b/src/android/app/src/main/java/org/citra/citra_android/model/Game.java index ea524fdf0..a08c4037b 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/Game.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/Game.java @@ -4,93 +4,82 @@ import android.content.ContentValues; import android.database.Cursor; import android.os.Environment; -public final class Game -{ - private static final String PATH_SCREENSHOT_FOLDER = - "file://" + Environment.getExternalStorageDirectory().getPath() + "/citra-emu/ScreenShots/"; +public final class Game { + private static final String PATH_SCREENSHOT_FOLDER = + "file://" + Environment.getExternalStorageDirectory().getPath() + "/citra-emu/ScreenShots/"; - private String mTitle; - private String mDescription; - private String mPath; - private String mGameId; - private String mScreenshotPath; - private String mCompany; + private String mTitle; + private String mDescription; + private String mPath; + private String mGameId; + private String mScreenshotPath; + private String mCompany; - private int mCountry; + private int mCountry; - public Game(String title, String description, int country, String path, - String gameId, String company, String screenshotPath) - { - mTitle = title; - mDescription = description; - mCountry = country; - mPath = path; - mGameId = gameId; - mCompany = company; - mScreenshotPath = screenshotPath; - } + public Game(String title, String description, int country, String path, + String gameId, String company, String screenshotPath) { + mTitle = title; + mDescription = description; + mCountry = country; + mPath = path; + mGameId = gameId; + mCompany = company; + mScreenshotPath = screenshotPath; + } - public String getTitle() - { - return mTitle; - } + public static ContentValues asContentValues(String title, String description, + int country, String path, String gameId, String company) { + ContentValues values = new ContentValues(); - public String getDescription() - { - return mDescription; - } + String screenPath = PATH_SCREENSHOT_FOLDER + gameId + "/" + gameId + "-1.png"; - public String getCompany() - { - return mCompany; - } + values.put(GameDatabase.KEY_GAME_TITLE, title); + values.put(GameDatabase.KEY_GAME_DESCRIPTION, description); + values.put(GameDatabase.KEY_GAME_COUNTRY, country); + values.put(GameDatabase.KEY_GAME_PATH, path); + values.put(GameDatabase.KEY_GAME_ID, gameId); + values.put(GameDatabase.KEY_GAME_COMPANY, company); + values.put(GameDatabase.KEY_GAME_SCREENSHOT_PATH, screenPath); - public int getCountry() - { - return mCountry; - } + return values; + } - public String getPath() - { - return mPath; - } + public static Game fromCursor(Cursor cursor) { + return new Game(cursor.getString(GameDatabase.GAME_COLUMN_TITLE), + cursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION), + cursor.getInt(GameDatabase.GAME_COLUMN_COUNTRY), + cursor.getString(GameDatabase.GAME_COLUMN_PATH), + cursor.getString(GameDatabase.GAME_COLUMN_GAME_ID), + cursor.getString(GameDatabase.GAME_COLUMN_COMPANY), + cursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH)); + } - public String getGameId() - { - return mGameId; - } + public String getTitle() { + return mTitle; + } - public String getScreenshotPath() - { - return mScreenshotPath; - } + public String getDescription() { + return mDescription; + } - public static ContentValues asContentValues(String title, String description, - int country, String path, String gameId, String company) - { - ContentValues values = new ContentValues(); + public String getCompany() { + return mCompany; + } - String screenPath = PATH_SCREENSHOT_FOLDER + gameId + "/" + gameId + "-1.png"; + public int getCountry() { + return mCountry; + } - values.put(GameDatabase.KEY_GAME_TITLE, title); - values.put(GameDatabase.KEY_GAME_DESCRIPTION, description); - values.put(GameDatabase.KEY_GAME_COUNTRY, country); - values.put(GameDatabase.KEY_GAME_PATH, path); - values.put(GameDatabase.KEY_GAME_ID, gameId); - values.put(GameDatabase.KEY_GAME_COMPANY, company); - values.put(GameDatabase.KEY_GAME_SCREENSHOT_PATH, screenPath); + public String getPath() { + return mPath; + } - return values; - } + public String getGameId() { + return mGameId; + } - public static Game fromCursor(Cursor cursor) - { - return new Game(cursor.getString(GameDatabase.GAME_COLUMN_TITLE), - cursor.getString(GameDatabase.GAME_COLUMN_DESCRIPTION), - cursor.getInt(GameDatabase.GAME_COLUMN_COUNTRY), - cursor.getString(GameDatabase.GAME_COLUMN_PATH), - cursor.getString(GameDatabase.GAME_COLUMN_GAME_ID), - cursor.getString(GameDatabase.GAME_COLUMN_COMPANY), - cursor.getString(GameDatabase.GAME_COLUMN_SCREENSHOT_PATH)); - } + public String getScreenshotPath() { + return mScreenshotPath; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java b/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java index f893fff71..b4f536126 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/GameDatabase.java @@ -20,273 +20,241 @@ import rx.Observable; * A helper class that provides several utilities simplifying interaction with * the SQLite database. */ -public final class GameDatabase extends SQLiteOpenHelper -{ - private static final int DB_VERSION = 1; +public final class GameDatabase extends SQLiteOpenHelper { + public static final int COLUMN_DB_ID = 0; + public static final int GAME_COLUMN_PATH = 1; + public static final int GAME_COLUMN_TITLE = 2; + public static final int GAME_COLUMN_DESCRIPTION = 3; + public static final int GAME_COLUMN_COUNTRY = 4; + public static final int GAME_COLUMN_GAME_ID = 5; + public static final int GAME_COLUMN_COMPANY = 6; + public static final int GAME_COLUMN_SCREENSHOT_PATH = 7; + public static final int FOLDER_COLUMN_PATH = 1; + public static final String KEY_DB_ID = "_id"; + public static final String KEY_GAME_PATH = "path"; + public static final String KEY_GAME_TITLE = "title"; + public static final String KEY_GAME_DESCRIPTION = "description"; + public static final String KEY_GAME_COUNTRY = "country"; + public static final String KEY_GAME_ID = "game_id"; + public static final String KEY_GAME_COMPANY = "company"; + public static final String KEY_GAME_SCREENSHOT_PATH = "screenshot_path"; + public static final String KEY_FOLDER_PATH = "path"; + public static final String TABLE_NAME_FOLDERS = "folders"; + public static final String TABLE_NAME_GAMES = "games"; + private static final int DB_VERSION = 1; + private static final String TYPE_PRIMARY = " INTEGER PRIMARY KEY"; + private static final String TYPE_INTEGER = " INTEGER"; + private static final String TYPE_STRING = " TEXT"; - public static final int COLUMN_DB_ID = 0; + private static final String CONSTRAINT_UNIQUE = " UNIQUE"; - public static final int GAME_COLUMN_PATH = 1; - public static final int GAME_COLUMN_TITLE = 2; - public static final int GAME_COLUMN_DESCRIPTION = 3; - public static final int GAME_COLUMN_COUNTRY = 4; - public static final int GAME_COLUMN_GAME_ID = 5; - public static final int GAME_COLUMN_COMPANY = 6; - public static final int GAME_COLUMN_SCREENSHOT_PATH = 7; + private static final String SEPARATOR = ", "; - public static final int FOLDER_COLUMN_PATH = 1; + private static final String SQL_CREATE_GAMES = "CREATE TABLE " + TABLE_NAME_GAMES + "(" + + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR + + KEY_GAME_PATH + TYPE_STRING + SEPARATOR + + KEY_GAME_TITLE + TYPE_STRING + SEPARATOR + + KEY_GAME_DESCRIPTION + TYPE_STRING + SEPARATOR + + KEY_GAME_COUNTRY + TYPE_INTEGER + SEPARATOR + + KEY_GAME_ID + TYPE_STRING + SEPARATOR + + KEY_GAME_COMPANY + TYPE_STRING + SEPARATOR + + KEY_GAME_SCREENSHOT_PATH + TYPE_STRING + ")"; - public static final String KEY_DB_ID = "_id"; + private static final String SQL_CREATE_FOLDERS = "CREATE TABLE " + TABLE_NAME_FOLDERS + "(" + + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR + + KEY_FOLDER_PATH + TYPE_STRING + CONSTRAINT_UNIQUE + ")"; - public static final String KEY_GAME_PATH = "path"; - public static final String KEY_GAME_TITLE = "title"; - public static final String KEY_GAME_DESCRIPTION = "description"; - public static final String KEY_GAME_COUNTRY = "country"; - public static final String KEY_GAME_ID = "game_id"; - public static final String KEY_GAME_COMPANY = "company"; - public static final String KEY_GAME_SCREENSHOT_PATH = "screenshot_path"; + private static final String SQL_DELETE_FOLDERS = "DROP TABLE IF EXISTS " + TABLE_NAME_FOLDERS; + private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES; - public static final String KEY_FOLDER_PATH = "path"; - - public static final String TABLE_NAME_FOLDERS = "folders"; - public static final String TABLE_NAME_GAMES = "games"; - - private static final String TYPE_PRIMARY = " INTEGER PRIMARY KEY"; - private static final String TYPE_INTEGER = " INTEGER"; - private static final String TYPE_STRING = " TEXT"; - - private static final String CONSTRAINT_UNIQUE = " UNIQUE"; - - private static final String SEPARATOR = ", "; - - private static final String SQL_CREATE_GAMES = "CREATE TABLE " + TABLE_NAME_GAMES + "(" - + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR - + KEY_GAME_PATH + TYPE_STRING + SEPARATOR - + KEY_GAME_TITLE + TYPE_STRING + SEPARATOR - + KEY_GAME_DESCRIPTION + TYPE_STRING + SEPARATOR - + KEY_GAME_COUNTRY + TYPE_INTEGER + SEPARATOR - + KEY_GAME_ID + TYPE_STRING + SEPARATOR - + KEY_GAME_COMPANY + TYPE_STRING + SEPARATOR - + KEY_GAME_SCREENSHOT_PATH + TYPE_STRING + ")"; - - private static final String SQL_CREATE_FOLDERS = "CREATE TABLE " + TABLE_NAME_FOLDERS + "(" - + KEY_DB_ID + TYPE_PRIMARY + SEPARATOR - + KEY_FOLDER_PATH + TYPE_STRING + CONSTRAINT_UNIQUE + ")"; - - private static final String SQL_DELETE_FOLDERS = "DROP TABLE IF EXISTS " + TABLE_NAME_FOLDERS; - private static final String SQL_DELETE_GAMES = "DROP TABLE IF EXISTS " + TABLE_NAME_GAMES; - - public GameDatabase(Context context) - { - // Superclass constructor builds a database or uses an existing one. - super(context, "games.db", null, DB_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase database) - { - Log.debug("[GameDatabase] GameDatabase - Creating database..."); - - execSqlAndLog(database, SQL_CREATE_GAMES); - execSqlAndLog(database, SQL_CREATE_FOLDERS); - } - - @Override - public void onDowngrade(SQLiteDatabase database, int oldVersion, int newVersion) - { - Log.verbose("[GameDatabase] Downgrades not supporting, clearing databases.."); - execSqlAndLog(database, SQL_DELETE_FOLDERS); - execSqlAndLog(database, SQL_CREATE_FOLDERS); - - execSqlAndLog(database, SQL_DELETE_GAMES); - execSqlAndLog(database, SQL_CREATE_GAMES); - } - - @Override - public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) - { - Log.info("[GameDatabase] Upgrading database from schema version " + oldVersion + " to " + - newVersion); - - // Delete all the games - execSqlAndLog(database, SQL_DELETE_GAMES); - execSqlAndLog(database, SQL_CREATE_GAMES); - - Log.verbose("[GameDatabase] Re-scanning library with new schema."); - scanLibrary(database); - } - - public void scanLibrary(SQLiteDatabase database) - { - // Before scanning known folders, go through the game table and remove any entries for which the file itself is missing. - Cursor fileCursor = database.query(TABLE_NAME_GAMES, - null, // Get all columns. - null, // Get all rows. - null, - null, // No grouping. - null, - null); // Order of games is irrelevant. - - // Possibly overly defensive, but ensures that moveToNext() does not skip a row. - fileCursor.moveToPosition(-1); - - while (fileCursor.moveToNext()) - { - String gamePath = fileCursor.getString(GAME_COLUMN_PATH); - File game = new File(gamePath); - - if (!game.exists()) - { - Log.error("[GameDatabase] Game file no longer exists. Removing from the library: " + - gamePath); - database.delete(TABLE_NAME_GAMES, - KEY_DB_ID + " = ?", - new String[]{Long.toString(fileCursor.getLong(COLUMN_DB_ID))}); - } + public GameDatabase(Context context) { + // Superclass constructor builds a database or uses an existing one. + super(context, "games.db", null, DB_VERSION); } + @Override + public void onCreate(SQLiteDatabase database) { + Log.debug("[GameDatabase] GameDatabase - Creating database..."); - // Get a cursor listing all the folders the user has added to the library. - Cursor folderCursor = database.query(TABLE_NAME_FOLDERS, - null, // Get all columns. - null, // Get all rows. - null, - null, // No grouping. - null, - null); // Order of folders is irrelevant. + execSqlAndLog(database, SQL_CREATE_GAMES); + execSqlAndLog(database, SQL_CREATE_FOLDERS); + } - Set allowedExtensions = new HashSet(Arrays.asList( - ".3ds", ".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app")); + @Override + public void onDowngrade(SQLiteDatabase database, int oldVersion, int newVersion) { + Log.verbose("[GameDatabase] Downgrades not supporting, clearing databases.."); + execSqlAndLog(database, SQL_DELETE_FOLDERS); + execSqlAndLog(database, SQL_CREATE_FOLDERS); - // Possibly overly defensive, but ensures that moveToNext() does not skip a row. - folderCursor.moveToPosition(-1); + execSqlAndLog(database, SQL_DELETE_GAMES); + execSqlAndLog(database, SQL_CREATE_GAMES); + } - // Iterate through all results of the DB query (i.e. all folders in the library.) - while (folderCursor.moveToNext()) - { + @Override + public void onUpgrade(SQLiteDatabase database, int oldVersion, int newVersion) { + Log.info("[GameDatabase] Upgrading database from schema version " + oldVersion + " to " + + newVersion); - String folderPath = folderCursor.getString(FOLDER_COLUMN_PATH); - File folder = new File(folderPath); + // Delete all the games + execSqlAndLog(database, SQL_DELETE_GAMES); + execSqlAndLog(database, SQL_CREATE_GAMES); - Log.info("[GameDatabase] Reading files from library folder: " + folderPath); + Log.verbose("[GameDatabase] Re-scanning library with new schema."); + scanLibrary(database); + } - // Iterate through every file in the folder. - File[] children = folder.listFiles(); + public void scanLibrary(SQLiteDatabase database) { + // Before scanning known folders, go through the game table and remove any entries for which the file itself is missing. + Cursor fileCursor = database.query(TABLE_NAME_GAMES, + null, // Get all columns. + null, // Get all rows. + null, + null, // No grouping. + null, + null); // Order of games is irrelevant. - if (children != null) - { - for (File file : children) - { - if (!file.isHidden() && !file.isDirectory()) - { - String filePath = file.getPath(); + // Possibly overly defensive, but ensures that moveToNext() does not skip a row. + fileCursor.moveToPosition(-1); - int extensionStart = filePath.lastIndexOf('.'); - if (extensionStart > 0) - { - String fileExtension = filePath.substring(extensionStart); + while (fileCursor.moveToNext()) { + String gamePath = fileCursor.getString(GAME_COLUMN_PATH); + File game = new File(gamePath); - // Check that the file has an extension we care about before trying to read out of it. - if (allowedExtensions.contains(fileExtension.toLowerCase())) - { - String name = NativeLibrary.GetTitle(filePath); - - // If the game's title field is empty, use the filename. - if (name.isEmpty()) - { - name = filePath.substring(filePath.lastIndexOf("/") + 1); - } - - String gameId = NativeLibrary.GetGameId(filePath); - - // If the game's ID field is empty, use the filename without extension. - if (gameId.isEmpty()) - { - gameId = filePath.substring(filePath.lastIndexOf("/") + 1, - filePath.lastIndexOf(".")); - } - - ContentValues game = Game.asContentValues(name, - NativeLibrary.GetDescription(filePath).replace("\n", " "), - NativeLibrary.GetCountry(filePath), - filePath, - gameId, - NativeLibrary.GetCompany(filePath)); - - // Try to update an existing game first. - int rowsMatched = database.update(TABLE_NAME_GAMES, // Which table to update. - game, - // The values to fill the row with. - KEY_GAME_ID + " = ?", - // The WHERE clause used to find the right row. - new String[]{game.getAsString( - KEY_GAME_ID)}); // The ? in WHERE clause is replaced with this, - // which is provided as an array because there - // could potentially be more than one argument. - - // If update fails, insert a new game instead. - if (rowsMatched == 0) - { - Log.verbose("[GameDatabase] Adding game: " + game.getAsString(KEY_GAME_TITLE)); - database.insert(TABLE_NAME_GAMES, null, game); - } - else - { - Log.verbose("[GameDatabase] Updated game: " + game.getAsString(KEY_GAME_TITLE)); - } - } + if (!game.exists()) { + Log.error("[GameDatabase] Game file no longer exists. Removing from the library: " + + gamePath); + database.delete(TABLE_NAME_GAMES, + KEY_DB_ID + " = ?", + new String[]{Long.toString(fileCursor.getLong(COLUMN_DB_ID))}); } - } } - } - // If the folder is empty because it no longer exists, remove it from the library. - else if (!folder.exists()) - { - Log.error( - "[GameDatabase] Folder no longer exists. Removing from the library: " + folderPath); - database.delete(TABLE_NAME_FOLDERS, - KEY_DB_ID + " = ?", - new String[]{Long.toString(folderCursor.getLong(COLUMN_DB_ID))}); - } - else - { - Log.error("[GameDatabase] Folder contains no games: " + folderPath); - } + + + // Get a cursor listing all the folders the user has added to the library. + Cursor folderCursor = database.query(TABLE_NAME_FOLDERS, + null, // Get all columns. + null, // Get all rows. + null, + null, // No grouping. + null, + null); // Order of folders is irrelevant. + + Set allowedExtensions = new HashSet(Arrays.asList( + ".3ds", ".3ds", ".3dsx", ".elf", ".axf", ".cci", ".cxi", ".app")); + + // Possibly overly defensive, but ensures that moveToNext() does not skip a row. + folderCursor.moveToPosition(-1); + + // Iterate through all results of the DB query (i.e. all folders in the library.) + while (folderCursor.moveToNext()) { + + String folderPath = folderCursor.getString(FOLDER_COLUMN_PATH); + File folder = new File(folderPath); + + Log.info("[GameDatabase] Reading files from library folder: " + folderPath); + + // Iterate through every file in the folder. + File[] children = folder.listFiles(); + + if (children != null) { + for (File file : children) { + if (!file.isHidden() && !file.isDirectory()) { + String filePath = file.getPath(); + + int extensionStart = filePath.lastIndexOf('.'); + if (extensionStart > 0) { + String fileExtension = filePath.substring(extensionStart); + + // Check that the file has an extension we care about before trying to read out of it. + if (allowedExtensions.contains(fileExtension.toLowerCase())) { + String name = NativeLibrary.GetTitle(filePath); + + // If the game's title field is empty, use the filename. + if (name.isEmpty()) { + name = filePath.substring(filePath.lastIndexOf("/") + 1); + } + + String gameId = NativeLibrary.GetGameId(filePath); + + // If the game's ID field is empty, use the filename without extension. + if (gameId.isEmpty()) { + gameId = filePath.substring(filePath.lastIndexOf("/") + 1, + filePath.lastIndexOf(".")); + } + + ContentValues game = Game.asContentValues(name, + NativeLibrary.GetDescription(filePath).replace("\n", " "), + NativeLibrary.GetCountry(filePath), + filePath, + gameId, + NativeLibrary.GetCompany(filePath)); + + // Try to update an existing game first. + int rowsMatched = database.update(TABLE_NAME_GAMES, // Which table to update. + game, + // The values to fill the row with. + KEY_GAME_ID + " = ?", + // The WHERE clause used to find the right row. + new String[]{game.getAsString( + KEY_GAME_ID)}); // The ? in WHERE clause is replaced with this, + // which is provided as an array because there + // could potentially be more than one argument. + + // If update fails, insert a new game instead. + if (rowsMatched == 0) { + Log.verbose("[GameDatabase] Adding game: " + game.getAsString(KEY_GAME_TITLE)); + database.insert(TABLE_NAME_GAMES, null, game); + } else { + Log.verbose("[GameDatabase] Updated game: " + game.getAsString(KEY_GAME_TITLE)); + } + } + } + } + } + } + // If the folder is empty because it no longer exists, remove it from the library. + else if (!folder.exists()) { + Log.error( + "[GameDatabase] Folder no longer exists. Removing from the library: " + folderPath); + database.delete(TABLE_NAME_FOLDERS, + KEY_DB_ID + " = ?", + new String[]{Long.toString(folderCursor.getLong(COLUMN_DB_ID))}); + } else { + Log.error("[GameDatabase] Folder contains no games: " + folderPath); + } + } + + fileCursor.close(); + folderCursor.close(); + database.close(); } - fileCursor.close(); - folderCursor.close(); - database.close(); - } + public Observable getGames() { + return Observable.create(subscriber -> + { + Log.info("[GameDatabase] Reading games list..."); - public Observable getGames() - { - return Observable.create(subscriber -> - { - Log.info("[GameDatabase] Reading games list..."); + SQLiteDatabase database = getReadableDatabase(); + Cursor resultCursor = database.query( + TABLE_NAME_GAMES, + null, + null, + null, + null, + null, + KEY_GAME_TITLE + " ASC" + ); - SQLiteDatabase database = getReadableDatabase(); - Cursor resultCursor = database.query( - TABLE_NAME_GAMES, - null, - null, - null, - null, - null, - KEY_GAME_TITLE + " ASC" - ); + // Pass the result cursor to the consumer. + subscriber.onNext(resultCursor); - // Pass the result cursor to the consumer. - subscriber.onNext(resultCursor); + // Tell the consumer we're done; it will unsubscribe implicitly. + subscriber.onCompleted(); + }); + } - // Tell the consumer we're done; it will unsubscribe implicitly. - subscriber.onCompleted(); - }); - } - - private void execSqlAndLog(SQLiteDatabase database, String sql) - { - Log.verbose("[GameDatabase] Executing SQL: " + sql); - database.execSQL(sql); - } + private void execSqlAndLog(SQLiteDatabase database, String sql) { + Log.verbose("[GameDatabase] Executing SQL: " + sql); + database.execSQL(sql); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/GameProvider.java b/src/android/app/src/main/java/org/citra/citra_android/model/GameProvider.java index c01da610f..5aeba6706 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/GameProvider.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/GameProvider.java @@ -14,142 +14,122 @@ import org.citra.citra_android.utils.Log; * Provides an interface allowing Activities to interact with the SQLite database. * CRUD methods in this class can be called by Activities using getContentResolver(). */ -public final class GameProvider extends ContentProvider -{ - public static final String REFRESH_LIBRARY = "refresh"; +public final class GameProvider extends ContentProvider { + public static final String REFRESH_LIBRARY = "refresh"; - public static final String AUTHORITY = "content://" + BuildConfig.APPLICATION_ID + ".provider"; - public static final Uri URI_FOLDER = - Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_FOLDERS + "/"); - public static final Uri URI_GAME = - Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_GAMES + "/"); - public static final Uri URI_REFRESH = Uri.parse(AUTHORITY + "/" + REFRESH_LIBRARY + "/"); + public static final String AUTHORITY = "content://" + BuildConfig.APPLICATION_ID + ".provider"; + public static final Uri URI_FOLDER = + Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_FOLDERS + "/"); + public static final Uri URI_GAME = + Uri.parse(AUTHORITY + "/" + GameDatabase.TABLE_NAME_GAMES + "/"); + public static final Uri URI_REFRESH = Uri.parse(AUTHORITY + "/" + REFRESH_LIBRARY + "/"); - public static final String MIME_TYPE_FOLDER = "vnd.android.cursor.item/vnd.dolphin.folder"; - public static final String MIME_TYPE_GAME = "vnd.android.cursor.item/vnd.dolphin.game"; + public static final String MIME_TYPE_FOLDER = "vnd.android.cursor.item/vnd.dolphin.folder"; + public static final String MIME_TYPE_GAME = "vnd.android.cursor.item/vnd.dolphin.game"; - private GameDatabase mDbHelper; + private GameDatabase mDbHelper; - @Override - public boolean onCreate() - { - Log.info("[GameProvider] Creating Content Provider..."); + @Override + public boolean onCreate() { + Log.info("[GameProvider] Creating Content Provider..."); - mDbHelper = new GameDatabase(getContext()); + mDbHelper = new GameDatabase(getContext()); - return true; - } - - @Override - public Cursor query(@NonNull Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) - { - Log.info("[GameProvider] Querying URI: " + uri); - - SQLiteDatabase db = mDbHelper.getReadableDatabase(); - - String table = uri.getLastPathSegment(); - - if (table == null) - { - Log.error("[GameProvider] Badly formatted URI: " + uri); - return null; + return true; } - Cursor cursor = db.query(table, projection, selection, selectionArgs, null, null, sortOrder); - cursor.setNotificationUri(getContext().getContentResolver(), uri); + @Override + public Cursor query(@NonNull Uri uri, String[] projection, String selection, + String[] selectionArgs, String sortOrder) { + Log.info("[GameProvider] Querying URI: " + uri); - return cursor; - } + SQLiteDatabase db = mDbHelper.getReadableDatabase(); - @Override - public String getType(@NonNull Uri uri) - { - Log.verbose("[GameProvider] Getting MIME type for URI: " + uri); - String lastSegment = uri.getLastPathSegment(); + String table = uri.getLastPathSegment(); - if (lastSegment == null) - { - Log.error("[GameProvider] Badly formatted URI: " + uri); - return null; - } - - if (lastSegment.equals(GameDatabase.TABLE_NAME_FOLDERS)) - { - return MIME_TYPE_FOLDER; - } - else if (lastSegment.equals(GameDatabase.TABLE_NAME_GAMES)) - { - return MIME_TYPE_GAME; - } - - Log.error("[GameProvider] Unknown MIME type for URI: " + uri); - return null; - } - - @Override - public Uri insert(@NonNull Uri uri, ContentValues values) - { - Log.info("[GameProvider] Inserting row at URI: " + uri); - - SQLiteDatabase database = mDbHelper.getWritableDatabase(); - String table = uri.getLastPathSegment(); - - long id = -1; - - if (table != null) - { - if (table.equals(REFRESH_LIBRARY)) - { - Log.info( - "[GameProvider] URI specified table REFRESH_LIBRARY. No insertion necessary; refreshing library contents..."); - mDbHelper.scanLibrary(database); - return uri; - } - - id = database.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE); - - // If insertion was successful... - if (id > 0) - { - // If we just added a folder, add its contents to the game list. - if (table.equals(GameDatabase.TABLE_NAME_FOLDERS)) - { - mDbHelper.scanLibrary(database); + if (table == null) { + Log.error("[GameProvider] Badly formatted URI: " + uri); + return null; } - // Notify the UI that its contents should be refreshed. - getContext().getContentResolver().notifyChange(uri, null); - uri = Uri.withAppendedPath(uri, Long.toString(id)); - } - else - { - Log.error("[GameProvider] Row already exists: " + uri + " id: " + id); - } - } - else - { - Log.error("[GameProvider] Badly formatted URI: " + uri); + Cursor cursor = db.query(table, projection, selection, selectionArgs, null, null, sortOrder); + cursor.setNotificationUri(getContext().getContentResolver(), uri); + + return cursor; } - database.close(); + @Override + public String getType(@NonNull Uri uri) { + Log.verbose("[GameProvider] Getting MIME type for URI: " + uri); + String lastSegment = uri.getLastPathSegment(); - return uri; - } + if (lastSegment == null) { + Log.error("[GameProvider] Badly formatted URI: " + uri); + return null; + } - @Override - public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) - { - Log.error("[GameProvider] Delete operations unsupported. URI: " + uri); - return 0; - } + if (lastSegment.equals(GameDatabase.TABLE_NAME_FOLDERS)) { + return MIME_TYPE_FOLDER; + } else if (lastSegment.equals(GameDatabase.TABLE_NAME_GAMES)) { + return MIME_TYPE_GAME; + } - @Override - public int update(@NonNull Uri uri, ContentValues values, String selection, - String[] selectionArgs) - { - Log.error("[GameProvider] Update operations unsupported. URI: " + uri); - return 0; - } + Log.error("[GameProvider] Unknown MIME type for URI: " + uri); + return null; + } + + @Override + public Uri insert(@NonNull Uri uri, ContentValues values) { + Log.info("[GameProvider] Inserting row at URI: " + uri); + + SQLiteDatabase database = mDbHelper.getWritableDatabase(); + String table = uri.getLastPathSegment(); + + long id = -1; + + if (table != null) { + if (table.equals(REFRESH_LIBRARY)) { + Log.info( + "[GameProvider] URI specified table REFRESH_LIBRARY. No insertion necessary; refreshing library contents..."); + mDbHelper.scanLibrary(database); + return uri; + } + + id = database.insertWithOnConflict(table, null, values, SQLiteDatabase.CONFLICT_IGNORE); + + // If insertion was successful... + if (id > 0) { + // If we just added a folder, add its contents to the game list. + if (table.equals(GameDatabase.TABLE_NAME_FOLDERS)) { + mDbHelper.scanLibrary(database); + } + + // Notify the UI that its contents should be refreshed. + getContext().getContentResolver().notifyChange(uri, null); + uri = Uri.withAppendedPath(uri, Long.toString(id)); + } else { + Log.error("[GameProvider] Row already exists: " + uri + " id: " + id); + } + } else { + Log.error("[GameProvider] Badly formatted URI: " + uri); + } + + database.close(); + + return uri; + } + + @Override + public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { + Log.error("[GameProvider] Delete operations unsupported. URI: " + uri); + return 0; + } + + @Override + public int update(@NonNull Uri uri, ContentValues values, String selection, + String[] selectionArgs) { + Log.error("[GameProvider] Update operations unsupported. URI: " + uri); + return 0; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/TvSettingsItem.java b/src/android/app/src/main/java/org/citra/citra_android/model/TvSettingsItem.java index 7f8d84b66..17cb4b523 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/TvSettingsItem.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/TvSettingsItem.java @@ -1,30 +1,25 @@ package org.citra.citra_android.model; -public final class TvSettingsItem -{ - private final int mItemId; - private final int mIconId; - private final int mLabelId; +public final class TvSettingsItem { + private final int mItemId; + private final int mIconId; + private final int mLabelId; - public TvSettingsItem(int itemId, int iconId, int labelId) - { - mItemId = itemId; - mIconId = iconId; - mLabelId = labelId; - } + public TvSettingsItem(int itemId, int iconId, int labelId) { + mItemId = itemId; + mIconId = iconId; + mLabelId = labelId; + } - public int getItemId() - { - return mItemId; - } + public int getItemId() { + return mItemId; + } - public int getIconId() - { - return mIconId; - } + public int getIconId() { + return mIconId; + } - public int getLabelId() - { - return mLabelId; - } + public int getLabelId() { + return mLabelId; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/BooleanSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/BooleanSetting.java new file mode 100644 index 000000000..17a5c7eee --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/BooleanSetting.java @@ -0,0 +1,23 @@ +package org.citra.citra_android.model.settings; + +public final class BooleanSetting extends Setting { + private boolean mValue; + + public BooleanSetting(String key, String section, int file, boolean value) { + super(key, section, file); + mValue = value; + } + + public boolean getValue() { + return mValue; + } + + public void setValue(boolean value) { + mValue = value; + } + + @Override + public String getValueAsString() { + return mValue ? "True" : "False"; + } +} diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/FloatSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/FloatSetting.java index 659eab9c8..1a5f9358a 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/FloatSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/FloatSetting.java @@ -1,28 +1,23 @@ package org.citra.citra_android.model.settings; -public final class FloatSetting extends Setting -{ - private float mValue; +public final class FloatSetting extends Setting { + private float mValue; - public FloatSetting(String key, String section, int file, float value) - { - super(key, section, file); - mValue = value; - } + public FloatSetting(String key, String section, int file, float value) { + super(key, section, file); + mValue = value; + } - public float getValue() - { - return mValue; - } + public float getValue() { + return mValue; + } - public void setValue(float value) - { - mValue = value; - } + public void setValue(float value) { + mValue = value; + } - @Override - public String getValueAsString() - { - return Float.toString(mValue); - } + @Override + public String getValueAsString() { + return Float.toString(mValue); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/IntSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/IntSetting.java index bd1603749..92d5c2a1a 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/IntSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/IntSetting.java @@ -1,28 +1,23 @@ package org.citra.citra_android.model.settings; -public final class IntSetting extends Setting -{ - private int mValue; +public final class IntSetting extends Setting { + private int mValue; - public IntSetting(String key, String section, int file, int value) - { - super(key, section, file); - mValue = value; - } + public IntSetting(String key, String section, int file, int value) { + super(key, section, file); + mValue = value; + } - public int getValue() - { - return mValue; - } + public int getValue() { + return mValue; + } - public void setValue(int value) - { - mValue = value; - } + public void setValue(int value) { + mValue = value; + } - @Override - public String getValueAsString() - { - return Integer.toString(mValue); - } + @Override + public String getValueAsString() { + return Integer.toString(mValue); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/Setting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/Setting.java index ede86a6bd..70527c581 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/Setting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/Setting.java @@ -6,52 +6,47 @@ package org.citra.citra_android.model.settings; * must be inferred at read-time. The type of value determines which child of this class is used * to represent the Setting. */ -public abstract class Setting -{ - private String mKey; - private String mSection; - private int mFile; +public abstract class Setting { + private String mKey; + private String mSection; + private int mFile; - /** - * Base constructor. - * - * @param key Everything to the left of the = in a line from the ini file. - * @param section The corresponding recent section header; e.g. [Core] or [Enhancements] without the brackets. - * @param file The ini file the Setting is stored in. - */ - public Setting(String key, String section, int file) - { - mKey = key; - mSection = section; - mFile = file; - } + /** + * Base constructor. + * + * @param key Everything to the left of the = in a line from the ini file. + * @param section The corresponding recent section header; e.g. [Core] or [Enhancements] without the brackets. + * @param file The ini file the Setting is stored in. + */ + public Setting(String key, String section, int file) { + mKey = key; + mSection = section; + mFile = file; + } - /** - * @return The identifier used to write this setting to the ini file. - */ - public String getKey() - { - return mKey; - } + /** + * @return The identifier used to write this setting to the ini file. + */ + public String getKey() { + return mKey; + } - /** - * @return The name of the header under which this Setting should be written in the ini file. - */ - public String getSection() - { - return mSection; - } + /** + * @return The name of the header under which this Setting should be written in the ini file. + */ + public String getSection() { + return mSection; + } - /** - * @return The ini file the Setting is stored in. - */ - public int getFile() - { - return mFile; - } + /** + * @return The ini file the Setting is stored in. + */ + public int getFile() { + return mFile; + } - /** - * @return A representation of this Setting's backing value converted to a String (e.g. for serialization). - */ - public abstract String getValueAsString(); + /** + * @return A representation of this Setting's backing value converted to a String (e.g. for serialization). + */ + public abstract String getValueAsString(); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/SettingSection.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/SettingSection.java index c08f13192..68b3c7a8e 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/SettingSection.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/SettingSection.java @@ -6,50 +6,44 @@ import java.util.HashMap; * A semantically-related group of Settings objects. These Settings are * internally stored as a HashMap. */ -public final class SettingSection -{ - private String mName; +public final class SettingSection { + private String mName; - private HashMap mSettings = new HashMap<>(); + private HashMap mSettings = new HashMap<>(); - /** - * Create a new SettingSection with no Settings in it. - * - * @param name The header of this section; e.g. [Core] or [Enhancements] without the brackets. - */ - public SettingSection(String name) - { - mName = name; - } + /** + * Create a new SettingSection with no Settings in it. + * + * @param name The header of this section; e.g. [Core] or [Enhancements] without the brackets. + */ + public SettingSection(String name) { + mName = name; + } - public String getName() - { - return mName; - } + public String getName() { + return mName; + } - /** - * Convenience method; inserts a value directly into the backing HashMap. - * - * @param setting The Setting to be inserted. - */ - public void putSetting(Setting setting) - { - mSettings.put(setting.getKey(), setting); - } + /** + * Convenience method; inserts a value directly into the backing HashMap. + * + * @param setting The Setting to be inserted. + */ + public void putSetting(Setting setting) { + mSettings.put(setting.getKey(), setting); + } - /** - * Convenience method; gets a value directly from the backing HashMap. - * - * @param key Used to retrieve the Setting. - * @return A Setting object (you should probably cast this before using) - */ - public Setting getSetting(String key) - { - return mSettings.get(key); - } + /** + * Convenience method; gets a value directly from the backing HashMap. + * + * @param key Used to retrieve the Setting. + * @return A Setting object (you should probably cast this before using) + */ + public Setting getSetting(String key) { + return mSettings.get(key); + } - public HashMap getSettings() - { - return mSettings; - } + public HashMap getSettings() { + return mSettings; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/StringSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/StringSetting.java index daa0c8237..9b53d049e 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/StringSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/StringSetting.java @@ -1,28 +1,23 @@ package org.citra.citra_android.model.settings; -public final class StringSetting extends Setting -{ - private String mValue; +public final class StringSetting extends Setting { + private String mValue; - public StringSetting(String key, String section, int file, String value) - { - super(key, section, file); - mValue = value; - } + public StringSetting(String key, String section, int file, String value) { + super(key, section, file); + mValue = value; + } - public String getValue() - { - return mValue; - } + public String getValue() { + return mValue; + } - public void setValue(String value) - { - mValue = value; - } + public void setValue(String value) { + mValue = value; + } - @Override - public String getValueAsString() - { - return mValue; - } + @Override + public String getValueAsString() { + return mValue; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/CheckBoxSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/CheckBoxSetting.java index ea37405c1..6bb7d9302 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/CheckBoxSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/CheckBoxSetting.java @@ -3,54 +3,45 @@ package org.citra.citra_android.model.settings.view; import org.citra.citra_android.model.settings.IntSetting; import org.citra.citra_android.model.settings.Setting; -public final class CheckBoxSetting extends SettingsItem -{ - private boolean mDefaultValue; +public final class CheckBoxSetting extends SettingsItem { + private boolean mDefaultValue; - public CheckBoxSetting(String key, String section, int file, int titleId, int descriptionId, - boolean defaultValue, Setting setting) - { - super(key, section, file, setting, titleId, descriptionId); - mDefaultValue = defaultValue; - } - - public boolean isChecked() - { - if (getSetting() == null) - { - return mDefaultValue; + public CheckBoxSetting(String key, String section, int file, int titleId, int descriptionId, + boolean defaultValue, Setting setting) { + super(key, section, file, setting, titleId, descriptionId); + mDefaultValue = defaultValue; } - IntSetting setting = (IntSetting) getSetting(); - return setting.getValue() == 1; - } + public boolean isChecked() { + if (getSetting() == null) { + return mDefaultValue; + } - /** - * Write a value to the backing boolean. If that boolean was previously null, - * initializes a new one and returns it, so it can be added to the Hashmap. - * - * @param checked Pretty self explanatory. - * @return null if overwritten successfully; otherwise, a newly created BooleanSetting. - */ - public IntSetting setChecked(boolean checked) - { - if (getSetting() == null) - { - IntSetting setting = new IntSetting(getKey(), getSection(), getFile(), checked ? 1 : 0); - setSetting(setting); - return setting; + IntSetting setting = (IntSetting) getSetting(); + return setting.getValue() == 1; } - else - { - IntSetting setting = (IntSetting) getSetting(); - setting.setValue(checked ? 1 : 0); - return null; - } - } - @Override - public int getType() - { - return TYPE_CHECKBOX; - } + /** + * Write a value to the backing boolean. If that boolean was previously null, + * initializes a new one and returns it, so it can be added to the Hashmap. + * + * @param checked Pretty self explanatory. + * @return null if overwritten successfully; otherwise, a newly created BooleanSetting. + */ + public IntSetting setChecked(boolean checked) { + if (getSetting() == null) { + IntSetting setting = new IntSetting(getKey(), getSection(), getFile(), checked ? 1 : 0); + setSetting(setting); + return setting; + } else { + IntSetting setting = (IntSetting) getSetting(); + setting.setValue(checked ? 1 : 0); + return null; + } + } + + @Override + public int getType() { + return TYPE_CHECKBOX; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/DateTimeSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/DateTimeSetting.java index 1989104bf..f62f5bf3d 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/DateTimeSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/DateTimeSetting.java @@ -3,39 +3,32 @@ package org.citra.citra_android.model.settings.view; import org.citra.citra_android.model.settings.Setting; import org.citra.citra_android.model.settings.StringSetting; -public final class DateTimeSetting extends SettingsItem -{ +public final class DateTimeSetting extends SettingsItem { private String mDefaultValue; public DateTimeSetting(String key, String section, int file, int titleId, int descriptionId, - String defaultValue, Setting setting) - { + String defaultValue, Setting setting) { super(key, section, file, setting, titleId, descriptionId); mDefaultValue = defaultValue; } - public String getValue() - { - if (getSetting() != null) - { + public String getValue() { + if (getSetting() != null) { StringSetting setting = (StringSetting) getSetting(); return setting.getValue(); - } - else - { + } else { return mDefaultValue; } } - public StringSetting setSelectedValue(String datetime) - { + + public StringSetting setSelectedValue(String datetime) { StringSetting setting = new StringSetting(getKey(), getSection(), getFile(), datetime); setSetting(setting); return setting; } @Override - public int getType() - { + public int getType() { return TYPE_DATETIME_SETTING; } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/HeaderSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/HeaderSetting.java index 34c3c246a..3dc8a9fb5 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/HeaderSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/HeaderSetting.java @@ -2,16 +2,13 @@ package org.citra.citra_android.model.settings.view; import org.citra.citra_android.model.settings.Setting; -public final class HeaderSetting extends SettingsItem -{ - public HeaderSetting(String key, Setting setting, int titleId, int descriptionId) - { - super(key, null, 0, setting, titleId, descriptionId); - } +public final class HeaderSetting extends SettingsItem { + public HeaderSetting(String key, Setting setting, int titleId, int descriptionId) { + super(key, null, 0, setting, titleId, descriptionId); + } - @Override - public int getType() - { - return SettingsItem.TYPE_HEADER; - } + @Override + public int getType() { + return SettingsItem.TYPE_HEADER; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/InputBindingSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/InputBindingSetting.java index a8f3e873a..1a7f261e1 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/InputBindingSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/InputBindingSetting.java @@ -3,50 +3,41 @@ package org.citra.citra_android.model.settings.view; import org.citra.citra_android.model.settings.Setting; import org.citra.citra_android.model.settings.StringSetting; -public final class InputBindingSetting extends SettingsItem -{ - public InputBindingSetting(String key, String section, int file, int titleId, Setting setting) - { - super(key, section, file, setting, titleId, 0); - } - - public String getValue() - { - if (getSetting() == null) - { - return ""; +public final class InputBindingSetting extends SettingsItem { + public InputBindingSetting(String key, String section, int file, int titleId, Setting setting) { + super(key, section, file, setting, titleId, 0); } - StringSetting setting = (StringSetting) getSetting(); - return setting.getValue(); - } + public String getValue() { + if (getSetting() == null) { + return ""; + } - /** - * Write a value to the backing string. If that string was previously null, - * initializes a new one and returns it, so it can be added to the Hashmap. - * - * @param bind The input that will be bound - * @return null if overwritten successfully; otherwise, a newly created StringSetting. - */ - public StringSetting setValue(String bind) - { - if (getSetting() == null) - { - StringSetting setting = new StringSetting(getKey(), getSection(), getFile(), bind); - setSetting(setting); - return setting; + StringSetting setting = (StringSetting) getSetting(); + return setting.getValue(); } - else - { - StringSetting setting = (StringSetting) getSetting(); - setting.setValue(bind); - return null; - } - } - @Override - public int getType() - { - return TYPE_INPUT_BINDING; - } + /** + * Write a value to the backing string. If that string was previously null, + * initializes a new one and returns it, so it can be added to the Hashmap. + * + * @param bind The input that will be bound + * @return null if overwritten successfully; otherwise, a newly created StringSetting. + */ + public StringSetting setValue(String bind) { + if (getSetting() == null) { + StringSetting setting = new StringSetting(getKey(), getSection(), getFile(), bind); + setSetting(setting); + return setting; + } else { + StringSetting setting = (StringSetting) getSetting(); + setting.setValue(bind); + return null; + } + } + + @Override + public int getType() { + return TYPE_INPUT_BINDING; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SettingsItem.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SettingsItem.java index d77a9f972..1d5201e4b 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SettingsItem.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SettingsItem.java @@ -9,107 +9,98 @@ import org.citra.citra_android.model.settings.Setting; * and a few with none (Headers, for example, do not correspond to anything in the ini * file.) */ -public abstract class SettingsItem -{ - public static final int TYPE_HEADER = 0; - public static final int TYPE_CHECKBOX = 1; - public static final int TYPE_SINGLE_CHOICE = 2; - public static final int TYPE_SLIDER = 3; - public static final int TYPE_SUBMENU = 4; - public static final int TYPE_INPUT_BINDING = 5; - public static final int TYPE_DATETIME_SETTING = 6; +public abstract class SettingsItem { + public static final int TYPE_HEADER = 0; + public static final int TYPE_CHECKBOX = 1; + public static final int TYPE_SINGLE_CHOICE = 2; + public static final int TYPE_SLIDER = 3; + public static final int TYPE_SUBMENU = 4; + public static final int TYPE_INPUT_BINDING = 5; + public static final int TYPE_DATETIME_SETTING = 6; - private String mKey; - private String mSection; - private int mFile; + private String mKey; + private String mSection; + private int mFile; - private Setting mSetting; + private Setting mSetting; - private int mNameId; - private int mDescriptionId; + private int mNameId; + private int mDescriptionId; - /** - * Base constructor. Takes a key / section name in case the third parameter, the Setting, - * is null; in which case, one can be constructed and saved using the key / section. - * - * @param key Identifier for the Setting represented by this Item. - * @param section Section to which the Setting belongs. - * @param setting A possibly-null backing Setting, to be modified on UI events. - * @param nameId Resource ID for a text string to be displayed as this setting's name. - * @param descriptionId Resource ID for a text string to be displayed as this setting's description. - */ - public SettingsItem(String key, String section, int file, Setting setting, int nameId, - int descriptionId) - { - mKey = key; - mSection = section; - mFile = file; - mSetting = setting; - mNameId = nameId; - mDescriptionId = descriptionId; - } + /** + * Base constructor. Takes a key / section name in case the third parameter, the Setting, + * is null; in which case, one can be constructed and saved using the key / section. + * + * @param key Identifier for the Setting represented by this Item. + * @param section Section to which the Setting belongs. + * @param setting A possibly-null backing Setting, to be modified on UI events. + * @param nameId Resource ID for a text string to be displayed as this setting's name. + * @param descriptionId Resource ID for a text string to be displayed as this setting's description. + */ + public SettingsItem(String key, String section, int file, Setting setting, int nameId, + int descriptionId) { + mKey = key; + mSection = section; + mFile = file; + mSetting = setting; + mNameId = nameId; + mDescriptionId = descriptionId; + } - /** - * @return The identifier for the backing Setting. - */ - public String getKey() - { - return mKey; - } + /** + * @return The identifier for the backing Setting. + */ + public String getKey() { + return mKey; + } - /** - * @return The header under which the backing Setting belongs. - */ - public String getSection() - { - return mSection; - } + /** + * @return The header under which the backing Setting belongs. + */ + public String getSection() { + return mSection; + } - /** - * @return The file the backing Setting is saved to. - */ - public int getFile() - { - return mFile; - } + /** + * @return The file the backing Setting is saved to. + */ + public int getFile() { + return mFile; + } - /** - * @return The backing Setting, possibly null. - */ - public Setting getSetting() - { - return mSetting; - } + /** + * @return The backing Setting, possibly null. + */ + public Setting getSetting() { + return mSetting; + } - /** - * Replace the backing setting with a new one. Generally used in cases where - * the backing setting is null. - * - * @param setting A non-null Setting. - */ - public void setSetting(Setting setting) - { - mSetting = setting; - } + /** + * Replace the backing setting with a new one. Generally used in cases where + * the backing setting is null. + * + * @param setting A non-null Setting. + */ + public void setSetting(Setting setting) { + mSetting = setting; + } - /** - * @return A resource ID for a text string representing this Setting's name. - */ - public int getNameId() - { - return mNameId; - } + /** + * @return A resource ID for a text string representing this Setting's name. + */ + public int getNameId() { + return mNameId; + } - public int getDescriptionId() - { - return mDescriptionId; - } + public int getDescriptionId() { + return mDescriptionId; + } - /** - * Used by {@link org.citra.citra_android.ui.settings.SettingsAdapter}'s onCreateViewHolder() - * method to determine which type of ViewHolder should be created. - * - * @return An integer (ideally, one of the constants defined in this file) - */ - public abstract int getType(); + /** + * Used by {@link org.citra.citra_android.ui.settings.SettingsAdapter}'s onCreateViewHolder() + * method to determine which type of ViewHolder should be created. + * + * @return An integer (ideally, one of the constants defined in this file) + */ + public abstract int getType(); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SingleChoiceSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SingleChoiceSetting.java index 0d4975eab..647cb3e74 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SingleChoiceSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SingleChoiceSetting.java @@ -3,71 +3,58 @@ package org.citra.citra_android.model.settings.view; import org.citra.citra_android.model.settings.IntSetting; import org.citra.citra_android.model.settings.Setting; -public final class SingleChoiceSetting extends SettingsItem -{ - private int mDefaultValue; +public final class SingleChoiceSetting extends SettingsItem { + private int mDefaultValue; - private int mChoicesId; - private int mValuesId; + private int mChoicesId; + private int mValuesId; - public SingleChoiceSetting(String key, String section, int file, int titleId, int descriptionId, - int choicesId, int valuesId, int defaultValue, Setting setting) - { - super(key, section, file, setting, titleId, descriptionId); - mValuesId = valuesId; - mChoicesId = choicesId; - mDefaultValue = defaultValue; - } - - public int getChoicesId() - { - return mChoicesId; - } - - public int getValuesId() - { - return mValuesId; - } - - public int getSelectedValue() - { - if (getSetting() != null) - { - IntSetting setting = (IntSetting) getSetting(); - return setting.getValue(); + public SingleChoiceSetting(String key, String section, int file, int titleId, int descriptionId, + int choicesId, int valuesId, int defaultValue, Setting setting) { + super(key, section, file, setting, titleId, descriptionId); + mValuesId = valuesId; + mChoicesId = choicesId; + mDefaultValue = defaultValue; } - else - { - return mDefaultValue; - } - } - /** - * Write a value to the backing int. If that int was previously null, - * initializes a new one and returns it, so it can be added to the Hashmap. - * - * @param selection New value of the int. - * @return null if overwritten successfully otherwise; a newly created IntSetting. - */ - public IntSetting setSelectedValue(int selection) - { - if (getSetting() == null) - { - IntSetting setting = new IntSetting(getKey(), getSection(), getFile(), selection); - setSetting(setting); - return setting; + public int getChoicesId() { + return mChoicesId; } - else - { - IntSetting setting = (IntSetting) getSetting(); - setting.setValue(selection); - return null; - } - } - @Override - public int getType() - { - return TYPE_SINGLE_CHOICE; - } + public int getValuesId() { + return mValuesId; + } + + public int getSelectedValue() { + if (getSetting() != null) { + IntSetting setting = (IntSetting) getSetting(); + return setting.getValue(); + } else { + return mDefaultValue; + } + } + + /** + * Write a value to the backing int. If that int was previously null, + * initializes a new one and returns it, so it can be added to the Hashmap. + * + * @param selection New value of the int. + * @return null if overwritten successfully otherwise; a newly created IntSetting. + */ + public IntSetting setSelectedValue(int selection) { + if (getSetting() == null) { + IntSetting setting = new IntSetting(getKey(), getSection(), getFile(), selection); + setSetting(setting); + return setting; + } else { + IntSetting setting = (IntSetting) getSetting(); + setting.setValue(selection); + return null; + } + } + + @Override + public int getType() { + return TYPE_SINGLE_CHOICE; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SliderSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SliderSetting.java index a9ba6e110..68488f222 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SliderSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SliderSetting.java @@ -6,114 +6,91 @@ import org.citra.citra_android.model.settings.Setting; import org.citra.citra_android.utils.Log; import org.citra.citra_android.utils.SettingsFile; -public final class SliderSetting extends SettingsItem -{ - private int mMax; - private int mDefaultValue; +public final class SliderSetting extends SettingsItem { + private int mMax; + private int mDefaultValue; - private String mUnits; + private String mUnits; - public SliderSetting(String key, String section, int file, int titleId, int descriptionId, - int max, String units, int defaultValue, Setting setting) - { - super(key, section, file, setting, titleId, descriptionId); - mMax = max; - mUnits = units; - mDefaultValue = defaultValue; - } - - public int getMax() - { - return mMax; - } - - public int getSelectedValue() - { - Setting setting = getSetting(); - - if (setting == null) - { - return mDefaultValue; + public SliderSetting(String key, String section, int file, int titleId, int descriptionId, + int max, String units, int defaultValue, Setting setting) { + super(key, section, file, setting, titleId, descriptionId); + mMax = max; + mUnits = units; + mDefaultValue = defaultValue; } - if (setting instanceof IntSetting) - { - IntSetting intSetting = (IntSetting) setting; - return intSetting.getValue(); + public int getMax() { + return mMax; } - else if (setting instanceof FloatSetting) - { - FloatSetting floatSetting = (FloatSetting) setting; - if (floatSetting.getKey().equals(SettingsFile.KEY_FRAME_LIMIT)) - { - return Math.round(floatSetting.getValue() * 100); - } - else - { - return Math.round(floatSetting.getValue()); - } - } - else - { - Log.error("[SliderSetting] Error casting setting type."); - return -1; - } - } - /** - * Write a value to the backing int. If that int was previously null, - * initializes a new one and returns it, so it can be added to the Hashmap. - * - * @param selection New value of the int. - * @return null if overwritten successfully otherwise; a newly created IntSetting. - */ - public IntSetting setSelectedValue(int selection) - { - if (getSetting() == null) - { - IntSetting setting = new IntSetting(getKey(), getSection(), getFile(), selection); - setSetting(setting); - return setting; - } - else - { - IntSetting setting = (IntSetting) getSetting(); - setting.setValue(selection); - return null; - } - } + public int getSelectedValue() { + Setting setting = getSetting(); - /** - * Write a value to the backing float. If that float was previously null, - * initializes a new one and returns it, so it can be added to the Hashmap. - * - * @param selection New value of the float. - * @return null if overwritten successfully otherwise; a newly created FloatSetting. - */ - public FloatSetting setSelectedValue(float selection) - { - if (getSetting() == null) - { - FloatSetting setting = new FloatSetting(getKey(), getSection(), getFile(), selection); - setSetting(setting); - return setting; - } - else - { - FloatSetting setting = (FloatSetting) getSetting(); - setting.setValue(selection); - return null; - } - } + if (setting == null) { + return mDefaultValue; + } - public String getUnits() - { - return mUnits; - } + if (setting instanceof IntSetting) { + IntSetting intSetting = (IntSetting) setting; + return intSetting.getValue(); + } else if (setting instanceof FloatSetting) { + FloatSetting floatSetting = (FloatSetting) setting; + if (floatSetting.getKey().equals(SettingsFile.KEY_FRAME_LIMIT)) { + return Math.round(floatSetting.getValue() * 100); + } else { + return Math.round(floatSetting.getValue()); + } + } else { + Log.error("[SliderSetting] Error casting setting type."); + return -1; + } + } - @Override - public int getType() - { - return TYPE_SLIDER; - } + /** + * Write a value to the backing int. If that int was previously null, + * initializes a new one and returns it, so it can be added to the Hashmap. + * + * @param selection New value of the int. + * @return null if overwritten successfully otherwise; a newly created IntSetting. + */ + public IntSetting setSelectedValue(int selection) { + if (getSetting() == null) { + IntSetting setting = new IntSetting(getKey(), getSection(), getFile(), selection); + setSetting(setting); + return setting; + } else { + IntSetting setting = (IntSetting) getSetting(); + setting.setValue(selection); + return null; + } + } + + /** + * Write a value to the backing float. If that float was previously null, + * initializes a new one and returns it, so it can be added to the Hashmap. + * + * @param selection New value of the float. + * @return null if overwritten successfully otherwise; a newly created FloatSetting. + */ + public FloatSetting setSelectedValue(float selection) { + if (getSetting() == null) { + FloatSetting setting = new FloatSetting(getKey(), getSection(), getFile(), selection); + setSetting(setting); + return setting; + } else { + FloatSetting setting = (FloatSetting) getSetting(); + setting.setValue(selection); + return null; + } + } + + public String getUnits() { + return mUnits; + } + + @Override + public int getType() { + return TYPE_SLIDER; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SubmenuSetting.java b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SubmenuSetting.java index 710c964a9..75d1199fc 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SubmenuSetting.java +++ b/src/android/app/src/main/java/org/citra/citra_android/model/settings/view/SubmenuSetting.java @@ -2,24 +2,20 @@ package org.citra.citra_android.model.settings.view; import org.citra.citra_android.model.settings.Setting; -public final class SubmenuSetting extends SettingsItem -{ - private String mMenuKey; +public final class SubmenuSetting extends SettingsItem { + private String mMenuKey; - public SubmenuSetting(String key, Setting setting, int titleId, int descriptionId, String menuKey) - { - super(key, null, 0, setting, titleId, descriptionId); - mMenuKey = menuKey; - } + public SubmenuSetting(String key, Setting setting, int titleId, int descriptionId, String menuKey) { + super(key, null, 0, setting, titleId, descriptionId); + mMenuKey = menuKey; + } - public String getMenuKey() - { - return mMenuKey; - } + public String getMenuKey() { + return mMenuKey; + } - @Override - public int getType() - { - return TYPE_SUBMENU; - } + @Override + public int getType() { + return TYPE_SUBMENU; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlay.java b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlay.java index c3a13727f..b0b10c9fe 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlay.java +++ b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlay.java @@ -19,10 +19,10 @@ import android.graphics.drawable.Drawable; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.view.Display; import android.view.MotionEvent; import android.view.SurfaceView; import android.view.View; -import android.view.Display; import android.view.View.OnTouchListener; import org.citra.citra_android.NativeLibrary; @@ -37,871 +37,786 @@ import java.util.Set; * Draws the interactive input overlay on top of the * {@link SurfaceView} that is rendering emulation. */ -public final class InputOverlay extends SurfaceView implements OnTouchListener -{ - private final Set overlayButtons = new HashSet<>(); - private final Set overlayDpads = new HashSet<>(); - private final Set overlayJoysticks = new HashSet<>(); +public final class InputOverlay extends SurfaceView implements OnTouchListener { + private final Set overlayButtons = new HashSet<>(); + private final Set overlayDpads = new HashSet<>(); + private final Set overlayJoysticks = new HashSet<>(); - private boolean mIsInEditMode = false; - private InputOverlayDrawableButton mButtonBeingConfigured; - private InputOverlayDrawableDpad mDpadBeingConfigured; - private InputOverlayDrawableJoystick mJoystickBeingConfigured; + private boolean mIsInEditMode = false; + private InputOverlayDrawableButton mButtonBeingConfigured; + private InputOverlayDrawableDpad mDpadBeingConfigured; + private InputOverlayDrawableJoystick mJoystickBeingConfigured; - private SharedPreferences mPreferences; + private SharedPreferences mPreferences; - /** - * Resizes a {@link Bitmap} by a given scale factor - * - * @param context The current {@link Context} - * @param bitmap The {@link Bitmap} to scale. - * @param scale The scale factor for the bitmap. - * @return The scaled {@link Bitmap} - */ - public static Bitmap resizeBitmap(Context context, Bitmap bitmap, float scale) - { - // Determine the button size based on the smaller screen dimension. - // This makes sure the buttons are the same size in both portrait and landscape. - DisplayMetrics dm = context.getResources().getDisplayMetrics(); - int minDimension = Math.min(dm.widthPixels, dm.heightPixels); + /** + * Constructor + * + * @param context The current {@link Context}. + * @param attrs {@link AttributeSet} for parsing XML attributes. + */ + public InputOverlay(Context context, AttributeSet attrs) { + super(context, attrs); - return Bitmap.createScaledBitmap(bitmap, - (int) (minDimension * scale), - (int) (minDimension * scale), - true); - } + mPreferences = PreferenceManager.getDefaultSharedPreferences(getContext()); + if (!mPreferences.getBoolean("OverlayInit", false)) { + defaultOverlay(); + } - /** - * Constructor - * - * @param context The current {@link Context}. - * @param attrs {@link AttributeSet} for parsing XML attributes. - */ - public InputOverlay(Context context, AttributeSet attrs) - { - super(context, attrs); + // Load the controls. + refreshControls(); - mPreferences = PreferenceManager.getDefaultSharedPreferences(getContext()); - if (!mPreferences.getBoolean("OverlayInit", false)) { - defaultOverlay(); + // Set the on touch listener. + setOnTouchListener(this); + + // Force draw + setWillNotDraw(false); + + // Request focus for the overlay so it has priority on presses. + requestFocus(); } - // Load the controls. - refreshControls(); + /** + * Resizes a {@link Bitmap} by a given scale factor + * + * @param context The current {@link Context} + * @param bitmap The {@link Bitmap} to scale. + * @param scale The scale factor for the bitmap. + * @return The scaled {@link Bitmap} + */ + public static Bitmap resizeBitmap(Context context, Bitmap bitmap, float scale) { + // Determine the button size based on the smaller screen dimension. + // This makes sure the buttons are the same size in both portrait and landscape. + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + int minDimension = Math.min(dm.widthPixels, dm.heightPixels); - // Set the on touch listener. - setOnTouchListener(this); - - // Force draw - setWillNotDraw(false); - - // Request focus for the overlay so it has priority on presses. - requestFocus(); - } - - @Override - public void draw(Canvas canvas) - { - super.draw(canvas); - - for (InputOverlayDrawableButton button : overlayButtons) - { - button.draw(canvas); + return Bitmap.createScaledBitmap(bitmap, + (int) (minDimension * scale), + (int) (minDimension * scale), + true); } - for (InputOverlayDrawableDpad dpad : overlayDpads) - { - dpad.draw(canvas); + /** + * Initializes an InputOverlayDrawableButton, given by resId, with all of the + * parameters set for it to be properly shown on the InputOverlay. + *

+ * This works due to the way the X and Y coordinates are stored within + * the {@link SharedPreferences}. + *

+ * In the input overlay configuration menu, + * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay). + * the X and Y coordinates of the button at the END of its touch event + * (when you remove your finger/stylus from the touchscreen) are then stored + * within a SharedPreferences instance so that those values can be retrieved here. + *

+ * This has a few benefits over the conventional way of storing the values + * (ie. within the Dolphin ini file). + *

    + *
  • No native calls
  • + *
  • Keeps Android-only values inside the Android environment
  • + *
+ *

+ * Technically no modifications should need to be performed on the returned + * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait + * for Android to call the onDraw method. + * + * @param context The current {@link Context}. + * @param defaultResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Default State). + * @param pressedResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Pressed State). + * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. + * @return An {@link InputOverlayDrawableButton} with the correct drawing bounds set. + */ + private static InputOverlayDrawableButton initializeOverlayButton(Context context, + int defaultResId, int pressedResId, int buttonId, String orientation) { + // Resources handle for fetching the initial Drawable resource. + final Resources res = context.getResources(); + + // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. + final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); + + // Decide scale based on button ID and user preference + float scale; + + switch (buttonId) { + case ButtonType.BUTTON_HOME: + case ButtonType.BUTTON_START: + case ButtonType.BUTTON_SELECT: + scale = 0.0625f; + break; + case ButtonType.TRIGGER_L: + case ButtonType.TRIGGER_R: + case ButtonType.BUTTON_ZL: + case ButtonType.BUTTON_ZR: + scale = 0.25f; + break; + default: + scale = 0.125f; + break; + } + + scale *= (sPrefs.getInt("controlScale", 50) + 50); + scale /= 100; + + // Initialize the InputOverlayDrawableButton. + final Bitmap defaultStateBitmap = + resizeBitmap(context, BitmapFactory.decodeResource(res, defaultResId), scale); + final Bitmap pressedStateBitmap = + resizeBitmap(context, BitmapFactory.decodeResource(res, pressedResId), scale); + final InputOverlayDrawableButton overlayDrawable = + new InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId); + + // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. + // These were set in the input overlay configuration menu. + String xKey; + String yKey; + + xKey = buttonId + orientation + "-X"; + yKey = buttonId + orientation + "-Y"; + + int drawableX = (int) sPrefs.getFloat(xKey, 0f); + int drawableY = (int) sPrefs.getFloat(yKey, 0f); + + int width = overlayDrawable.getWidth(); + int height = overlayDrawable.getHeight(); + + // Now set the bounds for the InputOverlayDrawableButton. + // This will dictate where on the screen (and the what the size) the InputOverlayDrawableButton will be. + overlayDrawable.setBounds(drawableX, drawableY, drawableX + width, drawableY + height); + + // Need to set the image's position + overlayDrawable.setPosition(drawableX, drawableY); + + return overlayDrawable; } - for (InputOverlayDrawableJoystick joystick : overlayJoysticks) - { - joystick.draw(canvas); - } - } + /** + * Initializes an {@link InputOverlayDrawableDpad} + * + * @param context The current {@link Context}. + * @param defaultResId The {@link Bitmap} resource ID of the default sate. + * @param pressedOneDirectionResId The {@link Bitmap} resource ID of the pressed sate in one direction. + * @param pressedTwoDirectionsResId The {@link Bitmap} resource ID of the pressed sate in two directions. + * @param buttonUp Identifier for the up button. + * @param buttonDown Identifier for the down button. + * @param buttonLeft Identifier for the left button. + * @param buttonRight Identifier for the right button. + * @return the initialized {@link InputOverlayDrawableDpad} + */ + private static InputOverlayDrawableDpad initializeOverlayDpad(Context context, + int defaultResId, + int pressedOneDirectionResId, + int pressedTwoDirectionsResId, + int buttonUp, + int buttonDown, + int buttonLeft, + int buttonRight, + String orientation) { + // Resources handle for fetching the initial Drawable resource. + final Resources res = context.getResources(); - @Override - public boolean onTouch(View v, MotionEvent event) - { - if (isInEditMode()) - { - return onTouchWhileEditing(event); + // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad. + final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); + + // Decide scale based on button ID and user preference + float scale; + + switch (buttonUp) { + case ButtonType.DPAD_UP: + scale = 0.275f; + break; + default: + scale = 0.2125f; + break; + } + + scale *= (sPrefs.getInt("controlScale", 50) + 50); + scale /= 100; + + // Initialize the InputOverlayDrawableDpad. + final Bitmap defaultStateBitmap = + resizeBitmap(context, BitmapFactory.decodeResource(res, defaultResId), scale); + final Bitmap pressedOneDirectionStateBitmap = + resizeBitmap(context, BitmapFactory.decodeResource(res, pressedOneDirectionResId), + scale); + final Bitmap pressedTwoDirectionsStateBitmap = + resizeBitmap(context, BitmapFactory.decodeResource(res, pressedTwoDirectionsResId), + scale); + final InputOverlayDrawableDpad overlayDrawable = + new InputOverlayDrawableDpad(res, defaultStateBitmap, + pressedOneDirectionStateBitmap, pressedTwoDirectionsStateBitmap, + buttonUp, buttonDown, buttonLeft, buttonRight); + + // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. + // These were set in the input overlay configuration menu. + int drawableX = (int) sPrefs.getFloat(buttonUp + orientation + "-X", 0f); + int drawableY = (int) sPrefs.getFloat(buttonUp + orientation + "-Y", 0f); + + int width = overlayDrawable.getWidth(); + int height = overlayDrawable.getHeight(); + + // Now set the bounds for the InputOverlayDrawableDpad. + // This will dictate where on the screen (and the what the size) the InputOverlayDrawableDpad will be. + overlayDrawable.setBounds(drawableX, drawableY, drawableX + width, drawableY + height); + + // Need to set the image's position + overlayDrawable.setPosition(drawableX, drawableY); + + return overlayDrawable; } - int pointerIndex = event.getActionIndex(); + /** + * Initializes an {@link InputOverlayDrawableJoystick} + * + * @param context The current {@link Context} + * @param resOuter Resource ID for the outer image of the joystick (the static image that shows the circular bounds). + * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). + * @param pressedResInner Resource ID for the pressed inner image of the joystick. + * @param joystick Identifier for which joystick this is. + * @return the initialized {@link InputOverlayDrawableJoystick}. + */ + private static InputOverlayDrawableJoystick initializeOverlayJoystick(Context context, + int resOuter, int defaultResInner, int pressedResInner, int joystick, String orientation) { + // Resources handle for fetching the initial Drawable resource. + final Resources res = context.getResources(); - if (mPreferences.getBoolean("isTouchEnabled", true)) - { - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - NativeLibrary.onTouchEvent(event.getX(pointerIndex), event.getY(pointerIndex), true); - break; - case MotionEvent.ACTION_MOVE: - NativeLibrary.onTouchMoved(event.getX(), event.getY()); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - // We dont really care where the touch has been released. We only care whether it has been - // released or not. - NativeLibrary.onTouchEvent(0, 0, false); - break; - } + // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick. + final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); + + // Decide scale based on user preference + float scale = 0.275f; + scale *= (sPrefs.getInt("controlScale", 50) + 50); + scale /= 100; + + // Initialize the InputOverlayDrawableJoystick. + final Bitmap bitmapOuter = + resizeBitmap(context, BitmapFactory.decodeResource(res, resOuter), scale); + final Bitmap bitmapInnerDefault = BitmapFactory.decodeResource(res, defaultResInner); + final Bitmap bitmapInnerPressed = BitmapFactory.decodeResource(res, pressedResInner); + + // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. + // These were set in the input overlay configuration menu. + int drawableX = (int) sPrefs.getFloat(joystick + orientation + "-X", 0f); + int drawableY = (int) sPrefs.getFloat(joystick + orientation + "-Y", 0f); + + // Decide inner scale based on joystick ID + float innerScale; + + switch (joystick) { + case ButtonType.STICK_C: + innerScale = 1.833f; + break; + default: + innerScale = 1.375f; + break; + } + + // Now set the bounds for the InputOverlayDrawableJoystick. + // This will dictate where on the screen (and the what the size) the InputOverlayDrawableJoystick will be. + int outerSize = bitmapOuter.getWidth(); + Rect outerRect = new Rect(drawableX, drawableY, drawableX + outerSize, drawableY + outerSize); + Rect innerRect = new Rect(0, 0, (int) (outerSize / innerScale), (int) (outerSize / innerScale)); + + // Send the drawableId to the joystick so it can be referenced when saving control position. + final InputOverlayDrawableJoystick overlayDrawable + = new InputOverlayDrawableJoystick(res, bitmapOuter, + bitmapInnerDefault, bitmapInnerPressed, + outerRect, innerRect, joystick); + + // Need to set the image's position + overlayDrawable.setPosition(drawableX, drawableY); + + return overlayDrawable; } - for (InputOverlayDrawableButton button : overlayButtons) - { - // Determine the button state to apply based on the MotionEvent action flag. - switch (event.getAction() & MotionEvent.ACTION_MASK) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - // If a pointer enters the bounds of a button, press that button. - if (button.getBounds() - .contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) - { - button.setPressedState(true); - button.setTrackId(event.getPointerId(pointerIndex)); - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), - ButtonState.PRESSED); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - // If a pointer ends, release the button it was pressing. - if (button.getTrackId() == event.getPointerId(pointerIndex)) - { - button.setPressedState(false); - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), - ButtonState.RELEASED); - } - break; - } + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + for (InputOverlayDrawableButton button : overlayButtons) { + button.draw(canvas); + } + + for (InputOverlayDrawableDpad dpad : overlayDpads) { + dpad.draw(canvas); + } + + for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { + joystick.draw(canvas); + } } - for (InputOverlayDrawableDpad dpad : overlayDpads) - { - // Determine the button state to apply based on the MotionEvent action flag. - switch (event.getAction() & MotionEvent.ACTION_MASK) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - // If a pointer enters the bounds of a button, press that button. - if (dpad.getBounds() - .contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) - { - boolean up = false; - boolean down = false; - boolean left = false; - boolean right = false; - if (dpad.getBounds().top + (dpad.getHeight() / 3) > (int) event.getY(pointerIndex)) - { - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(0), - ButtonState.PRESSED); - up = true; + @Override + public boolean onTouch(View v, MotionEvent event) { + if (isInEditMode()) { + return onTouchWhileEditing(event); + } + + int pointerIndex = event.getActionIndex(); + + if (mPreferences.getBoolean("isTouchEnabled", true)) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + NativeLibrary.onTouchEvent(event.getX(pointerIndex), event.getY(pointerIndex), true); + break; + case MotionEvent.ACTION_MOVE: + NativeLibrary.onTouchMoved(event.getX(), event.getY()); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + // We dont really care where the touch has been released. We only care whether it has been + // released or not. + NativeLibrary.onTouchEvent(0, 0, false); + break; } - if (dpad.getBounds().bottom - (dpad.getHeight() / 3) < (int) event.getY(pointerIndex)) - { - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(1), - ButtonState.PRESSED); - down = true; + } + + for (InputOverlayDrawableButton button : overlayButtons) { + // Determine the button state to apply based on the MotionEvent action flag. + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + // If a pointer enters the bounds of a button, press that button. + if (button.getBounds() + .contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) { + button.setPressedState(true); + button.setTrackId(event.getPointerId(pointerIndex)); + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), + ButtonState.PRESSED); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + // If a pointer ends, release the button it was pressing. + if (button.getTrackId() == event.getPointerId(pointerIndex)) { + button.setPressedState(false); + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), + ButtonState.RELEASED); + } + break; } - if (dpad.getBounds().left + (dpad.getWidth() / 3) > (int) event.getX(pointerIndex)) - { - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(2), - ButtonState.PRESSED); - left = true; + } + + for (InputOverlayDrawableDpad dpad : overlayDpads) { + // Determine the button state to apply based on the MotionEvent action flag. + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + // If a pointer enters the bounds of a button, press that button. + if (dpad.getBounds() + .contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) { + boolean up = false; + boolean down = false; + boolean left = false; + boolean right = false; + if (dpad.getBounds().top + (dpad.getHeight() / 3) > (int) event.getY(pointerIndex)) { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(0), + ButtonState.PRESSED); + up = true; + } + if (dpad.getBounds().bottom - (dpad.getHeight() / 3) < (int) event.getY(pointerIndex)) { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(1), + ButtonState.PRESSED); + down = true; + } + if (dpad.getBounds().left + (dpad.getWidth() / 3) > (int) event.getX(pointerIndex)) { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(2), + ButtonState.PRESSED); + left = true; + } + if (dpad.getBounds().right - (dpad.getWidth() / 3) < (int) event.getX(pointerIndex)) { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(3), + ButtonState.PRESSED); + right = true; + } + + setDpadState(dpad, up, down, left, right); + dpad.setTrackId(event.getPointerId(pointerIndex)); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + // If a pointer ends, release the buttons. + if (dpad.getTrackId() == event.getPointerId(pointerIndex)) { + for (int i = 0; i < 4; i++) { + dpad.setState(InputOverlayDrawableDpad.STATE_DEFAULT); + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i), + ButtonState.RELEASED); + } + } + break; } - if (dpad.getBounds().right - (dpad.getWidth() / 3) < (int) event.getX(pointerIndex)) - { - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(3), - ButtonState.PRESSED); - right = true; + } + + for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { + joystick.TrackEvent(event); + int axisID = joystick.getId(); + float[] axises = joystick.getAxisValues(); + + NativeLibrary + .onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, axisID, axises[0], axises[1]); + } + + invalidate(); + + return true; + } + + public boolean onTouchWhileEditing(MotionEvent event) { + int pointerIndex = event.getActionIndex(); + int fingerPositionX = (int) event.getX(pointerIndex); + int fingerPositionY = (int) event.getY(pointerIndex); + + // Maybe combine Button and Joystick as subclasses of the same parent? + // Or maybe create an interface like IMoveableHUDControl? + + for (InputOverlayDrawableButton button : overlayButtons) { + // Determine the button state to apply based on the MotionEvent action flag. + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + // If no button is being moved now, remember the currently touched button to move. + if (mButtonBeingConfigured == null && + button.getBounds().contains(fingerPositionX, fingerPositionY)) { + mButtonBeingConfigured = button; + mButtonBeingConfigured.onConfigureTouch(event); + } + break; + case MotionEvent.ACTION_MOVE: + if (mButtonBeingConfigured != null) { + mButtonBeingConfigured.onConfigureTouch(event); + invalidate(); + return true; + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + if (mButtonBeingConfigured == button) { + // Persist button position by saving new place. + saveControlPosition(mButtonBeingConfigured.getId(), + mButtonBeingConfigured.getBounds().left, + mButtonBeingConfigured.getBounds().top); + mButtonBeingConfigured = null; + } + break; } + } - setDpadState(dpad, up, down, left, right); - dpad.setTrackId(event.getPointerId(pointerIndex)); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - // If a pointer ends, release the buttons. - if (dpad.getTrackId() == event.getPointerId(pointerIndex)) - { - for (int i = 0; i < 4; i++) - { - dpad.setState(InputOverlayDrawableDpad.STATE_DEFAULT); - NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i), - ButtonState.RELEASED); + for (InputOverlayDrawableDpad dpad : overlayDpads) { + // Determine the button state to apply based on the MotionEvent action flag. + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + // If no button is being moved now, remember the currently touched button to move. + if (mButtonBeingConfigured == null && + dpad.getBounds().contains(fingerPositionX, fingerPositionY)) { + mDpadBeingConfigured = dpad; + mDpadBeingConfigured.onConfigureTouch(event); + } + break; + case MotionEvent.ACTION_MOVE: + if (mDpadBeingConfigured != null) { + mDpadBeingConfigured.onConfigureTouch(event); + invalidate(); + return true; + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + if (mDpadBeingConfigured == dpad) { + // Persist button position by saving new place. + saveControlPosition(mDpadBeingConfigured.getId(0), + mDpadBeingConfigured.getBounds().left, mDpadBeingConfigured.getBounds().top); + mDpadBeingConfigured = null; + } + break; } - } - break; - } + } + + for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + if (mJoystickBeingConfigured == null && + joystick.getBounds().contains(fingerPositionX, fingerPositionY)) { + mJoystickBeingConfigured = joystick; + mJoystickBeingConfigured.onConfigureTouch(event); + } + break; + case MotionEvent.ACTION_MOVE: + if (mJoystickBeingConfigured != null) { + mJoystickBeingConfigured.onConfigureTouch(event); + invalidate(); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + if (mJoystickBeingConfigured != null) { + saveControlPosition(mJoystickBeingConfigured.getId(), + mJoystickBeingConfigured.getBounds().left, + mJoystickBeingConfigured.getBounds().top); + mJoystickBeingConfigured = null; + } + break; + } + } + + return true; } - for (InputOverlayDrawableJoystick joystick : overlayJoysticks) - { - joystick.TrackEvent(event); - int axisID = joystick.getId(); - float[] axises = joystick.getAxisValues(); - - NativeLibrary - .onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, axisID, axises[0], axises[1]); + private void setDpadState(InputOverlayDrawableDpad dpad, boolean up, boolean down, boolean left, + boolean right) { + if (up) { + if (left) + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_UP_LEFT); + else if (right) + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_UP_RIGHT); + else + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_UP); + } else if (down) { + if (left) + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_DOWN_LEFT); + else if (right) + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_DOWN_RIGHT); + else + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_DOWN); + } else if (left) { + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_LEFT); + } else if (right) { + dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_RIGHT); + } } - invalidate(); - - return true; - } - - public boolean onTouchWhileEditing(MotionEvent event) - { - int pointerIndex = event.getActionIndex(); - int fingerPositionX = (int) event.getX(pointerIndex); - int fingerPositionY = (int) event.getY(pointerIndex); - - // Maybe combine Button and Joystick as subclasses of the same parent? - // Or maybe create an interface like IMoveableHUDControl? - - for (InputOverlayDrawableButton button : overlayButtons) - { - // Determine the button state to apply based on the MotionEvent action flag. - switch (event.getAction() & MotionEvent.ACTION_MASK) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - // If no button is being moved now, remember the currently touched button to move. - if (mButtonBeingConfigured == null && - button.getBounds().contains(fingerPositionX, fingerPositionY)) - { - mButtonBeingConfigured = button; - mButtonBeingConfigured.onConfigureTouch(event); - } - break; - case MotionEvent.ACTION_MOVE: - if (mButtonBeingConfigured != null) - { - mButtonBeingConfigured.onConfigureTouch(event); - invalidate(); - return true; - } - break; - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - if (mButtonBeingConfigured == button) - { - // Persist button position by saving new place. - saveControlPosition(mButtonBeingConfigured.getId(), - mButtonBeingConfigured.getBounds().left, - mButtonBeingConfigured.getBounds().top); - mButtonBeingConfigured = null; - } - break; - } + private void addOverlayControls(String orientation) { + if (mPreferences.getBoolean("buttonToggle0", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_a, + R.drawable.button_a_pressed, ButtonType.BUTTON_A, orientation)); + } + if (mPreferences.getBoolean("buttonToggle1", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_b, + R.drawable.button_b_pressed, ButtonType.BUTTON_B, orientation)); + } + if (mPreferences.getBoolean("buttonToggle2", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_x, + R.drawable.button_x_pressed, ButtonType.BUTTON_X, orientation)); + } + if (mPreferences.getBoolean("buttonToggle3", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_y, + R.drawable.button_y_pressed, ButtonType.BUTTON_Y, orientation)); + } + if (mPreferences.getBoolean("buttonToggle4", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_l, + R.drawable.button_l_pressed, ButtonType.TRIGGER_L, orientation)); + } + if (mPreferences.getBoolean("buttonToggle5", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_r, + R.drawable.button_r_pressed, ButtonType.TRIGGER_R, orientation)); + } + if (mPreferences.getBoolean("buttonToggle6", false)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_zl, + R.drawable.button_zl_pressed, ButtonType.BUTTON_ZL, orientation)); + } + if (mPreferences.getBoolean("buttonToggle7", false)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_zr, + R.drawable.button_zr_pressed, ButtonType.BUTTON_ZR, orientation)); + } + if (mPreferences.getBoolean("buttonToggle8", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_start, + R.drawable.button_start_pressed, ButtonType.BUTTON_START, orientation)); + } + if (mPreferences.getBoolean("buttonToggle9", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_select, + R.drawable.button_select_pressed, ButtonType.BUTTON_SELECT, orientation)); + } + if (mPreferences.getBoolean("buttonToggle10", true)) { + overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_home, + R.drawable.button_home_pressed, ButtonType.BUTTON_HOME, orientation)); + } + if (mPreferences.getBoolean("buttonToggle11", false)) { + overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.dpad, + R.drawable.dpad_pressed_one_direction, + R.drawable.dpad_pressed_two_directions, + ButtonType.DPAD_UP, ButtonType.DPAD_DOWN, + ButtonType.DPAD_LEFT, ButtonType.DPAD_RIGHT, orientation)); + } + if (mPreferences.getBoolean("buttonToggle12", true)) { + overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.stick_main_range, + R.drawable.stick_main, R.drawable.stick_main_pressed, + ButtonType.STICK_LEFT, orientation)); + } + if (mPreferences.getBoolean("buttonToggle13", false)) { + overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.stick_main_range, + R.drawable.stick_c, R.drawable.stick_c_pressed, ButtonType.STICK_C, orientation)); + } } - for (InputOverlayDrawableDpad dpad : overlayDpads) - { - // Determine the button state to apply based on the MotionEvent action flag. - switch (event.getAction() & MotionEvent.ACTION_MASK) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - // If no button is being moved now, remember the currently touched button to move. - if (mButtonBeingConfigured == null && - dpad.getBounds().contains(fingerPositionX, fingerPositionY)) - { - mDpadBeingConfigured = dpad; - mDpadBeingConfigured.onConfigureTouch(event); - } - break; - case MotionEvent.ACTION_MOVE: - if (mDpadBeingConfigured != null) - { - mDpadBeingConfigured.onConfigureTouch(event); - invalidate(); - return true; - } - break; + public void refreshControls() { + // Remove all the overlay buttons from the HashSet. + overlayButtons.removeAll(overlayButtons); + overlayDpads.removeAll(overlayDpads); + overlayJoysticks.removeAll(overlayJoysticks); - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - if (mDpadBeingConfigured == dpad) - { - // Persist button position by saving new place. - saveControlPosition(mDpadBeingConfigured.getId(0), - mDpadBeingConfigured.getBounds().left, mDpadBeingConfigured.getBounds().top); - mDpadBeingConfigured = null; - } - break; - } + String orientation = + getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? + "-Portrait" : ""; + + if (mPreferences.getBoolean("showInputOverlay", true)) { + // Add all the enabled overlay items back to the HashSet. + addOverlayControls(orientation); + } + + invalidate(); } - for (InputOverlayDrawableJoystick joystick : overlayJoysticks) - { - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - if (mJoystickBeingConfigured == null && - joystick.getBounds().contains(fingerPositionX, fingerPositionY)) - { - mJoystickBeingConfigured = joystick; - mJoystickBeingConfigured.onConfigureTouch(event); - } - break; - case MotionEvent.ACTION_MOVE: - if (mJoystickBeingConfigured != null) - { - mJoystickBeingConfigured.onConfigureTouch(event); - invalidate(); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - if (mJoystickBeingConfigured != null) - { - saveControlPosition(mJoystickBeingConfigured.getId(), - mJoystickBeingConfigured.getBounds().left, - mJoystickBeingConfigured.getBounds().top); - mJoystickBeingConfigured = null; - } - break; - } + private void saveControlPosition(int sharedPrefsId, int x, int y) { + final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); + SharedPreferences.Editor sPrefsEditor = sPrefs.edit(); + sPrefsEditor.putFloat(sharedPrefsId + "-X", x); + sPrefsEditor.putFloat(sharedPrefsId + "-Y", y); + sPrefsEditor.apply(); } - return true; - } - - private void setDpadState(InputOverlayDrawableDpad dpad, boolean up, boolean down, boolean left, - boolean right) - { - if (up) - { - if (left) - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_UP_LEFT); - else if (right) - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_UP_RIGHT); - else - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_UP); - } - else if (down) - { - if (left) - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_DOWN_LEFT); - else if (right) - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_DOWN_RIGHT); - else - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_DOWN); - } - else if (left) - { - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_LEFT); - } - else if (right) - { - dpad.setState(InputOverlayDrawableDpad.STATE_PRESSED_RIGHT); - } - } - - private void addOverlayControls(String orientation) - { - if (mPreferences.getBoolean("buttonToggle0", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_a, - R.drawable.button_a_pressed, ButtonType.BUTTON_A, orientation)); - } - if (mPreferences.getBoolean("buttonToggle1", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_b, - R.drawable.button_b_pressed, ButtonType.BUTTON_B, orientation)); - } - if (mPreferences.getBoolean("buttonToggle2", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_x, - R.drawable.button_x_pressed, ButtonType.BUTTON_X, orientation)); - } - if (mPreferences.getBoolean("buttonToggle3", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_y, - R.drawable.button_y_pressed, ButtonType.BUTTON_Y, orientation)); - } - if (mPreferences.getBoolean("buttonToggle4", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_l, - R.drawable.button_l_pressed, ButtonType.TRIGGER_L, orientation)); - } - if (mPreferences.getBoolean("buttonToggle5", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_r, - R.drawable.button_r_pressed, ButtonType.TRIGGER_R, orientation)); - } - if (mPreferences.getBoolean("buttonToggle6", false)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_zl, - R.drawable.button_zl_pressed, ButtonType.BUTTON_ZL, orientation)); - } - if (mPreferences.getBoolean("buttonToggle7", false)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_zr, - R.drawable.button_zr_pressed, ButtonType.BUTTON_ZR, orientation)); - } - if (mPreferences.getBoolean("buttonToggle8", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_start, - R.drawable.button_start_pressed, ButtonType.BUTTON_START, orientation)); - } - if (mPreferences.getBoolean("buttonToggle9", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_select, - R.drawable.button_select_pressed, ButtonType.BUTTON_SELECT, orientation)); - } - if (mPreferences.getBoolean("buttonToggle10", true)) - { - overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_home, - R.drawable.button_home_pressed, ButtonType.BUTTON_HOME, orientation)); - } - if (mPreferences.getBoolean("buttonToggle11", false)) - { - overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.dpad, - R.drawable.dpad_pressed_one_direction, - R.drawable.dpad_pressed_two_directions, - ButtonType.DPAD_UP, ButtonType.DPAD_DOWN, - ButtonType.DPAD_LEFT, ButtonType.DPAD_RIGHT, orientation)); - } - if (mPreferences.getBoolean("buttonToggle12", true)) - { - overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.stick_main_range, - R.drawable.stick_main, R.drawable.stick_main_pressed, - ButtonType.STICK_LEFT, orientation)); - } - if (mPreferences.getBoolean("buttonToggle13", false)) - { - overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.stick_main_range, - R.drawable.stick_c, R.drawable.stick_c_pressed, ButtonType.STICK_C, orientation)); - } - } - - public void refreshControls() - { - // Remove all the overlay buttons from the HashSet. - overlayButtons.removeAll(overlayButtons); - overlayDpads.removeAll(overlayDpads); - overlayJoysticks.removeAll(overlayJoysticks); - - String orientation = - getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? - "-Portrait" : ""; - - if (mPreferences.getBoolean("showInputOverlay", true)) - { - // Add all the enabled overlay items back to the HashSet. - addOverlayControls(orientation); + public void setIsInEditMode(boolean isInEditMode) { + mIsInEditMode = isInEditMode; } - invalidate(); - } + private void defaultOverlay() { + if (!mPreferences.getBoolean("OverlayInit", false)) { + // It's possible that a user has created their overlay before this was added + // Only change the overlay if the 'A' button is not in the upper corner. + // GameCube + if (mPreferences.getFloat(ButtonType.BUTTON_A + "-X", 0f) == 0f) { + defaultOverlayLandscape(); + } + if (mPreferences.getFloat(ButtonType.BUTTON_A + "-Portrait" + "-X", 0f) == 0f) { + defaultOverlayPortrait(); + } + } - private void saveControlPosition(int sharedPrefsId, int x, int y) - { - final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - SharedPreferences.Editor sPrefsEditor = sPrefs.edit(); - sPrefsEditor.putFloat(sharedPrefsId + "-X", x); - sPrefsEditor.putFloat(sharedPrefsId + "-Y", y); - sPrefsEditor.apply(); - } - - /** - * Initializes an InputOverlayDrawableButton, given by resId, with all of the - * parameters set for it to be properly shown on the InputOverlay. - *

- * This works due to the way the X and Y coordinates are stored within - * the {@link SharedPreferences}. - *

- * In the input overlay configuration menu, - * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay). - * the X and Y coordinates of the button at the END of its touch event - * (when you remove your finger/stylus from the touchscreen) are then stored - * within a SharedPreferences instance so that those values can be retrieved here. - *

- * This has a few benefits over the conventional way of storing the values - * (ie. within the Dolphin ini file). - *

    - *
  • No native calls
  • - *
  • Keeps Android-only values inside the Android environment
  • - *
- *

- * Technically no modifications should need to be performed on the returned - * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait - * for Android to call the onDraw method. - * - * @param context The current {@link Context}. - * @param defaultResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Default State). - * @param pressedResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Pressed State). - * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. - * @return An {@link InputOverlayDrawableButton} with the correct drawing bounds set. - */ - private static InputOverlayDrawableButton initializeOverlayButton(Context context, - int defaultResId, int pressedResId, int buttonId, String orientation) - { - // Resources handle for fetching the initial Drawable resource. - final Resources res = context.getResources(); - - // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. - final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); - - // Decide scale based on button ID and user preference - float scale; - - switch (buttonId) - { - case ButtonType.BUTTON_HOME: - case ButtonType.BUTTON_START: - case ButtonType.BUTTON_SELECT: - scale = 0.0625f; - break; - case ButtonType.TRIGGER_L: - case ButtonType.TRIGGER_R: - case ButtonType.BUTTON_ZL: - case ButtonType.BUTTON_ZR: - scale = 0.25f; - break; - default: - scale = 0.125f; - break; + SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); + sPrefsEditor.putBoolean("OverlayInit", true); + sPrefsEditor.apply(); } - scale *= (sPrefs.getInt("controlScale", 50) + 50); - scale /= 100; + public void resetButtonPlacement() { + boolean isLandscape = + getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - // Initialize the InputOverlayDrawableButton. - final Bitmap defaultStateBitmap = - resizeBitmap(context, BitmapFactory.decodeResource(res, defaultResId), scale); - final Bitmap pressedStateBitmap = - resizeBitmap(context, BitmapFactory.decodeResource(res, pressedResId), scale); - final InputOverlayDrawableButton overlayDrawable = - new InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId); + if (isLandscape) { + defaultOverlayLandscape(); + } else { + defaultOverlayPortrait(); + } - // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. - // These were set in the input overlay configuration menu. - String xKey; - String yKey; - - xKey = buttonId + orientation + "-X"; - yKey = buttonId + orientation + "-Y"; - - int drawableX = (int) sPrefs.getFloat(xKey, 0f); - int drawableY = (int) sPrefs.getFloat(yKey, 0f); - - int width = overlayDrawable.getWidth(); - int height = overlayDrawable.getHeight(); - - // Now set the bounds for the InputOverlayDrawableButton. - // This will dictate where on the screen (and the what the size) the InputOverlayDrawableButton will be. - overlayDrawable.setBounds(drawableX, drawableY, drawableX + width, drawableY + height); - - // Need to set the image's position - overlayDrawable.setPosition(drawableX, drawableY); - - return overlayDrawable; - } - - /** - * Initializes an {@link InputOverlayDrawableDpad} - * - * @param context The current {@link Context}. - * @param defaultResId The {@link Bitmap} resource ID of the default sate. - * @param pressedOneDirectionResId The {@link Bitmap} resource ID of the pressed sate in one direction. - * @param pressedTwoDirectionsResId The {@link Bitmap} resource ID of the pressed sate in two directions. - * @param buttonUp Identifier for the up button. - * @param buttonDown Identifier for the down button. - * @param buttonLeft Identifier for the left button. - * @param buttonRight Identifier for the right button. - * @return the initialized {@link InputOverlayDrawableDpad} - */ - private static InputOverlayDrawableDpad initializeOverlayDpad(Context context, - int defaultResId, - int pressedOneDirectionResId, - int pressedTwoDirectionsResId, - int buttonUp, - int buttonDown, - int buttonLeft, - int buttonRight, - String orientation) - { - // Resources handle for fetching the initial Drawable resource. - final Resources res = context.getResources(); - - // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad. - final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); - - // Decide scale based on button ID and user preference - float scale; - - switch (buttonUp) - { - case ButtonType.DPAD_UP: - scale = 0.275f; - break; - default: - scale = 0.2125f; - break; + refreshControls(); } - scale *= (sPrefs.getInt("controlScale", 50) + 50); - scale /= 100; + private void defaultOverlayLandscape() { + SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); + // Get screen size + Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay(); + DisplayMetrics outMetrics = new DisplayMetrics(); + display.getMetrics(outMetrics); + float maxX = outMetrics.heightPixels; + float maxY = outMetrics.widthPixels; + // Height and width changes depending on orientation. Use the larger value for height. + if (maxY > maxX) { + float tmp = maxX; + maxX = maxY; + maxY = tmp; + } + Resources res = getResources(); - // Initialize the InputOverlayDrawableDpad. - final Bitmap defaultStateBitmap = - resizeBitmap(context, BitmapFactory.decodeResource(res, defaultResId), scale); - final Bitmap pressedOneDirectionStateBitmap = - resizeBitmap(context, BitmapFactory.decodeResource(res, pressedOneDirectionResId), - scale); - final Bitmap pressedTwoDirectionsStateBitmap = - resizeBitmap(context, BitmapFactory.decodeResource(res, pressedTwoDirectionsResId), - scale); - final InputOverlayDrawableDpad overlayDrawable = - new InputOverlayDrawableDpad(res, defaultStateBitmap, - pressedOneDirectionStateBitmap, pressedTwoDirectionsStateBitmap, - buttonUp, buttonDown, buttonLeft, buttonRight); + // Each value is a percent from max X/Y stored as an int. Have to bring that value down + // to a decimal before multiplying by MAX X/Y. + sPrefsEditor.putFloat(ButtonType.BUTTON_A + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_A_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_A + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_A_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_B + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_B_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_B + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_B_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_X + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_X_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_X + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_X_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_Y + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_Y_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_Y + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_Y_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZL_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZL_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZR_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZR_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.DPAD_UP + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_UP_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.DPAD_UP + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_UP_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_L + "-X", (((float) res.getInteger(R.integer.N3DS_TRIGGER_L_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_L + "-Y", (((float) res.getInteger(R.integer.N3DS_TRIGGER_L_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_R + "-X", (((float) res.getInteger(R.integer.N3DS_TRIGGER_R_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_R + "-Y", (((float) res.getInteger(R.integer.N3DS_TRIGGER_R_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_START + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_START_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_START + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_START_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_SELECT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_SELECT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_HOME_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_HOME_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.STICK_C + "-X", (((float) res.getInteger(R.integer.N3DS_STICK_C_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.STICK_C + "-Y", (((float) res.getInteger(R.integer.N3DS_STICK_C_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.STICK_LEFT + "-X", (((float) res.getInteger(R.integer.N3DS_STICK_MAIN_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.STICK_LEFT + "-Y", (((float) res.getInteger(R.integer.N3DS_STICK_MAIN_Y) / 1000) * maxY)); - // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. - // These were set in the input overlay configuration menu. - int drawableX = (int) sPrefs.getFloat(buttonUp + orientation + "-X", 0f); - int drawableY = (int) sPrefs.getFloat(buttonUp + orientation + "-Y", 0f); - - int width = overlayDrawable.getWidth(); - int height = overlayDrawable.getHeight(); - - // Now set the bounds for the InputOverlayDrawableDpad. - // This will dictate where on the screen (and the what the size) the InputOverlayDrawableDpad will be. - overlayDrawable.setBounds(drawableX, drawableY, drawableX + width, drawableY + height); - - // Need to set the image's position - overlayDrawable.setPosition(drawableX, drawableY); - - return overlayDrawable; - } - - /** - * Initializes an {@link InputOverlayDrawableJoystick} - * - * @param context The current {@link Context} - * @param resOuter Resource ID for the outer image of the joystick (the static image that shows the circular bounds). - * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). - * @param pressedResInner Resource ID for the pressed inner image of the joystick. - * @param joystick Identifier for which joystick this is. - * @return the initialized {@link InputOverlayDrawableJoystick}. - */ - private static InputOverlayDrawableJoystick initializeOverlayJoystick(Context context, - int resOuter, int defaultResInner, int pressedResInner, int joystick, String orientation) - { - // Resources handle for fetching the initial Drawable resource. - final Resources res = context.getResources(); - - // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick. - final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); - - // Decide scale based on user preference - float scale = 0.275f; - scale *= (sPrefs.getInt("controlScale", 50) + 50); - scale /= 100; - - // Initialize the InputOverlayDrawableJoystick. - final Bitmap bitmapOuter = - resizeBitmap(context, BitmapFactory.decodeResource(res, resOuter), scale); - final Bitmap bitmapInnerDefault = BitmapFactory.decodeResource(res, defaultResInner); - final Bitmap bitmapInnerPressed = BitmapFactory.decodeResource(res, pressedResInner); - - // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. - // These were set in the input overlay configuration menu. - int drawableX = (int) sPrefs.getFloat(joystick + orientation + "-X", 0f); - int drawableY = (int) sPrefs.getFloat(joystick + orientation + "-Y", 0f); - - // Decide inner scale based on joystick ID - float innerScale; - - switch (joystick) - { - case ButtonType.STICK_C: - innerScale = 1.833f; - break; - default: - innerScale = 1.375f; - break; + // We want to commit right away, otherwise the overlay could load before this is saved. + sPrefsEditor.commit(); } - // Now set the bounds for the InputOverlayDrawableJoystick. - // This will dictate where on the screen (and the what the size) the InputOverlayDrawableJoystick will be. - int outerSize = bitmapOuter.getWidth(); - Rect outerRect = new Rect(drawableX, drawableY, drawableX + outerSize, drawableY + outerSize); - Rect innerRect = new Rect(0, 0, (int) (outerSize / innerScale), (int) (outerSize / innerScale)); + private void defaultOverlayPortrait() { + SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); + // Get screen size + Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay(); + DisplayMetrics outMetrics = new DisplayMetrics(); + display.getMetrics(outMetrics); + float maxX = outMetrics.heightPixels; + float maxY = outMetrics.widthPixels; + // Height and width changes depending on orientation. Use the larger value for height. + if (maxY < maxX) { + float tmp = maxX; + maxX = maxY; + maxY = tmp; + } + Resources res = getResources(); + String portrait = "-Portrait"; - // Send the drawableId to the joystick so it can be referenced when saving control position. - final InputOverlayDrawableJoystick overlayDrawable - = new InputOverlayDrawableJoystick(res, bitmapOuter, - bitmapInnerDefault, bitmapInnerPressed, - outerRect, innerRect, joystick); + // Each value is a percent from max X/Y stored as an int. Have to bring that value down + // to a decimal before multiplying by MAX X/Y. + sPrefsEditor.putFloat(ButtonType.BUTTON_A + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_A_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_A + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_A_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_B + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_B_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_B + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_B_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_X + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_X_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_X + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_X_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_Y + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_Y_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_Y + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_Y_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZL_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZL_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZR_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_ZR_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.DPAD_UP + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_UP_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.DPAD_UP + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_UP_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_L + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_TRIGGER_L_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_L + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_TRIGGER_L_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_R + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_TRIGGER_R_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.TRIGGER_R + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_TRIGGER_R_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_START + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_START_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_START + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_START_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_SELECT_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_SELECT_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_BUTTON_HOME_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_BUTTON_HOME_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.STICK_C + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_STICK_C_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.STICK_C + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_STICK_C_PORTRAIT_Y) / 1000) * maxY)); + sPrefsEditor.putFloat(ButtonType.STICK_LEFT + portrait + "-X", (((float) res.getInteger(R.integer.N3DS_STICK_MAIN_PORTRAIT_X) / 1000) * maxX)); + sPrefsEditor.putFloat(ButtonType.STICK_LEFT + portrait + "-Y", (((float) res.getInteger(R.integer.N3DS_STICK_MAIN_PORTRAIT_Y) / 1000) * maxY)); - // Need to set the image's position - overlayDrawable.setPosition(drawableX, drawableY); - - return overlayDrawable; - } - - public void setIsInEditMode(boolean isInEditMode) - { - mIsInEditMode = isInEditMode; - } - - private void defaultOverlay() - { - if (!mPreferences.getBoolean("OverlayInit", false)) - { - // It's possible that a user has created their overlay before this was added - // Only change the overlay if the 'A' button is not in the upper corner. - // GameCube - if (mPreferences.getFloat(ButtonType.BUTTON_A + "-X", 0f) == 0f) - { - defaultOverlayLandscape(); - } - if (mPreferences.getFloat(ButtonType.BUTTON_A + "-Portrait" + "-X", 0f) == 0f) - { - defaultOverlayPortrait(); - } + // We want to commit right away, otherwise the overlay could load before this is saved. + sPrefsEditor.commit(); } - SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); - sPrefsEditor.putBoolean("OverlayInit", true); - sPrefsEditor.apply(); - } - - public void resetButtonPlacement() - { - boolean isLandscape = - getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - - if (isLandscape) { - defaultOverlayLandscape(); + public boolean isInEditMode() { + return mIsInEditMode; } - else { - defaultOverlayPortrait(); - } - - refreshControls(); - } - - private void defaultOverlayLandscape() - { - SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); - // Get screen size - Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay(); - DisplayMetrics outMetrics = new DisplayMetrics(); - display.getMetrics(outMetrics); - float maxX = outMetrics.heightPixels; - float maxY = outMetrics.widthPixels; - // Height and width changes depending on orientation. Use the larger value for height. - if (maxY > maxX) - { - float tmp = maxX; - maxX = maxY; - maxY = tmp; - } - Resources res = getResources(); - - // Each value is a percent from max X/Y stored as an int. Have to bring that value down - // to a decimal before multiplying by MAX X/Y. - sPrefsEditor.putFloat(ButtonType.BUTTON_A + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_A_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_A + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_A_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_B + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_B_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_B + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_B_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_X + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_X_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_X + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_X_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_Y + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_Y_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_Y + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_Y_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZL_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZL_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZR_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZR_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.DPAD_UP + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_UP_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.DPAD_UP + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_UP_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_L + "-X", (((float)res.getInteger(R.integer.N3DS_TRIGGER_L_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_L + "-Y", (((float)res.getInteger(R.integer.N3DS_TRIGGER_L_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_R + "-X", (((float)res.getInteger(R.integer.N3DS_TRIGGER_R_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_R + "-Y", (((float)res.getInteger(R.integer.N3DS_TRIGGER_R_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_START + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_START_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_START + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_START_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_SELECT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_SELECT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_HOME_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_HOME_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.STICK_C + "-X", (((float)res.getInteger(R.integer.N3DS_STICK_C_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.STICK_C + "-Y", (((float)res.getInteger(R.integer.N3DS_STICK_C_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.STICK_LEFT + "-X", (((float)res.getInteger(R.integer.N3DS_STICK_MAIN_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.STICK_LEFT + "-Y", (((float)res.getInteger(R.integer.N3DS_STICK_MAIN_Y) / 1000) * maxY)); - - // We want to commit right away, otherwise the overlay could load before this is saved. - sPrefsEditor.commit(); - } - - private void defaultOverlayPortrait() - { - SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); - // Get screen size - Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay(); - DisplayMetrics outMetrics = new DisplayMetrics(); - display.getMetrics(outMetrics); - float maxX = outMetrics.heightPixels; - float maxY = outMetrics.widthPixels; - // Height and width changes depending on orientation. Use the larger value for height. - if (maxY < maxX) - { - float tmp = maxX; - maxX = maxY; - maxY = tmp; - } - Resources res = getResources(); - String portrait = "-Portrait"; - - // Each value is a percent from max X/Y stored as an int. Have to bring that value down - // to a decimal before multiplying by MAX X/Y. - sPrefsEditor.putFloat(ButtonType.BUTTON_A + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_A_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_A + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_A_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_B + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_B_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_B + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_B_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_X + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_X_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_X + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_X_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_Y + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_Y_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_Y + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_Y_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZL_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZL + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZL_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZR_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_ZR + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_ZR_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.DPAD_UP + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_UP_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.DPAD_UP + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_UP_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_L + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_TRIGGER_L_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_L + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_TRIGGER_L_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_R + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_TRIGGER_R_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.TRIGGER_R + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_TRIGGER_R_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_START + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_START_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_START + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_START_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_SELECT_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_SELECT + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_SELECT_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_BUTTON_HOME_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_BUTTON_HOME_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.STICK_C + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_STICK_C_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.STICK_C + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_STICK_C_PORTRAIT_Y) / 1000) * maxY)); - sPrefsEditor.putFloat(ButtonType.STICK_LEFT + portrait + "-X", (((float)res.getInteger(R.integer.N3DS_STICK_MAIN_PORTRAIT_X) / 1000) * maxX)); - sPrefsEditor.putFloat(ButtonType.STICK_LEFT + portrait + "-Y", (((float)res.getInteger(R.integer.N3DS_STICK_MAIN_PORTRAIT_Y) / 1000) * maxY)); - - // We want to commit right away, otherwise the overlay could load before this is saved. - sPrefsEditor.commit(); - } - - public boolean isInEditMode() - { - return mIsInEditMode; - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableButton.java b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableButton.java index ec9f81c3c..9b3e58805 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableButton.java +++ b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableButton.java @@ -17,121 +17,106 @@ import android.view.MotionEvent; * Custom {@link BitmapDrawable} that is capable * of storing it's own ID. */ -public final class InputOverlayDrawableButton -{ - // The ID identifying what type of button this Drawable represents. - private int mButtonType; - private int mTrackId; - private int mPreviousTouchX, mPreviousTouchY; - private int mControlPositionX, mControlPositionY; - private int mWidth; - private int mHeight; - private BitmapDrawable mDefaultStateBitmap; - private BitmapDrawable mPressedStateBitmap; - private boolean mPressedState = false; +public final class InputOverlayDrawableButton { + // The ID identifying what type of button this Drawable represents. + private int mButtonType; + private int mTrackId; + private int mPreviousTouchX, mPreviousTouchY; + private int mControlPositionX, mControlPositionY; + private int mWidth; + private int mHeight; + private BitmapDrawable mDefaultStateBitmap; + private BitmapDrawable mPressedStateBitmap; + private boolean mPressedState = false; - /** - * Constructor - * - * @param res {@link Resources} instance. - * @param defaultStateBitmap {@link Bitmap} to use with the default state Drawable. - * @param pressedStateBitmap {@link Bitmap} to use with the pressed state Drawable. - * @param buttonType Identifier for this type of button. - */ - public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap, - Bitmap pressedStateBitmap, int buttonType) - { - mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); - mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap); - mButtonType = buttonType; - - mWidth = mDefaultStateBitmap.getIntrinsicWidth(); - mHeight = mDefaultStateBitmap.getIntrinsicHeight(); - } - - /** - * Gets this InputOverlayDrawableButton's button ID. - * - * @return this InputOverlayDrawableButton's button ID. - */ - public int getId() - { - return mButtonType; - } - - public void setTrackId(int trackId) - { - mTrackId = trackId; - } - - public int getTrackId() - { - return mTrackId; - } - - public boolean onConfigureTouch(MotionEvent event) - { - int pointerIndex = event.getActionIndex(); - int fingerPositionX = (int) event.getX(pointerIndex); - int fingerPositionY = (int) event.getY(pointerIndex); - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - mPreviousTouchX = fingerPositionX; - mPreviousTouchY = fingerPositionY; - break; - case MotionEvent.ACTION_MOVE: - mControlPositionX += fingerPositionX - mPreviousTouchX; - mControlPositionY += fingerPositionY - mPreviousTouchY; - setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX, - getHeight() + mControlPositionY); - mPreviousTouchX = fingerPositionX; - mPreviousTouchY = fingerPositionY; - break; + /** + * Constructor + * + * @param res {@link Resources} instance. + * @param defaultStateBitmap {@link Bitmap} to use with the default state Drawable. + * @param pressedStateBitmap {@link Bitmap} to use with the pressed state Drawable. + * @param buttonType Identifier for this type of button. + */ + public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap, + Bitmap pressedStateBitmap, int buttonType) { + mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); + mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap); + mButtonType = buttonType; + mWidth = mDefaultStateBitmap.getIntrinsicWidth(); + mHeight = mDefaultStateBitmap.getIntrinsicHeight(); } - return true; - } - public void setPosition(int x, int y) - { - mControlPositionX = x; - mControlPositionY = y; - } + /** + * Gets this InputOverlayDrawableButton's button ID. + * + * @return this InputOverlayDrawableButton's button ID. + */ + public int getId() { + return mButtonType; + } - public void draw(Canvas canvas) - { - getCurrentStateBitmapDrawable().draw(canvas); - } + public int getTrackId() { + return mTrackId; + } - private BitmapDrawable getCurrentStateBitmapDrawable() - { - return mPressedState ? mPressedStateBitmap : mDefaultStateBitmap; - } + public void setTrackId(int trackId) { + mTrackId = trackId; + } - public void setBounds(int left, int top, int right, int bottom) - { - mDefaultStateBitmap.setBounds(left, top, right, bottom); - mPressedStateBitmap.setBounds(left, top, right, bottom); - } + public boolean onConfigureTouch(MotionEvent event) { + int pointerIndex = event.getActionIndex(); + int fingerPositionX = (int) event.getX(pointerIndex); + int fingerPositionY = (int) event.getY(pointerIndex); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; + case MotionEvent.ACTION_MOVE: + mControlPositionX += fingerPositionX - mPreviousTouchX; + mControlPositionY += fingerPositionY - mPreviousTouchY; + setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX, + getHeight() + mControlPositionY); + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; - public Rect getBounds() - { - return mDefaultStateBitmap.getBounds(); - } + } + return true; + } - public int getWidth() - { - return mWidth; - } + public void setPosition(int x, int y) { + mControlPositionX = x; + mControlPositionY = y; + } - public int getHeight() - { - return mHeight; - } + public void draw(Canvas canvas) { + getCurrentStateBitmapDrawable().draw(canvas); + } - public void setPressedState(boolean isPressed) - { - mPressedState = isPressed; - } + private BitmapDrawable getCurrentStateBitmapDrawable() { + return mPressedState ? mPressedStateBitmap : mDefaultStateBitmap; + } + + public void setBounds(int left, int top, int right, int bottom) { + mDefaultStateBitmap.setBounds(left, top, right, bottom); + mPressedStateBitmap.setBounds(left, top, right, bottom); + } + + public Rect getBounds() { + return mDefaultStateBitmap.getBounds(); + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + public void setPressedState(boolean isPressed) { + mPressedState = isPressed; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableDpad.java b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableDpad.java index 1b0ee6829..77d81e118 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableDpad.java +++ b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableDpad.java @@ -17,190 +17,174 @@ import android.view.MotionEvent; * Custom {@link BitmapDrawable} that is capable * of storing it's own ID. */ -public final class InputOverlayDrawableDpad -{ - // The ID identifying what type of button this Drawable represents. - private int[] mButtonType = new int[4]; - private int mTrackId; - private int mPreviousTouchX, mPreviousTouchY; - private int mControlPositionX, mControlPositionY; - private int mWidth; - private int mHeight; - private BitmapDrawable mDefaultStateBitmap; - private BitmapDrawable mPressedOneDirectionStateBitmap; - private BitmapDrawable mPressedTwoDirectionsStateBitmap; - private int mPressState = STATE_DEFAULT; +public final class InputOverlayDrawableDpad { + public static final int STATE_DEFAULT = 0; + public static final int STATE_PRESSED_UP = 1; + public static final int STATE_PRESSED_DOWN = 2; + public static final int STATE_PRESSED_LEFT = 3; + public static final int STATE_PRESSED_RIGHT = 4; + public static final int STATE_PRESSED_UP_LEFT = 5; + public static final int STATE_PRESSED_UP_RIGHT = 6; + public static final int STATE_PRESSED_DOWN_LEFT = 7; + public static final int STATE_PRESSED_DOWN_RIGHT = 8; + // The ID identifying what type of button this Drawable represents. + private int[] mButtonType = new int[4]; + private int mTrackId; + private int mPreviousTouchX, mPreviousTouchY; + private int mControlPositionX, mControlPositionY; + private int mWidth; + private int mHeight; + private BitmapDrawable mDefaultStateBitmap; + private BitmapDrawable mPressedOneDirectionStateBitmap; + private BitmapDrawable mPressedTwoDirectionsStateBitmap; + private int mPressState = STATE_DEFAULT; - public static final int STATE_DEFAULT = 0; - public static final int STATE_PRESSED_UP = 1; - public static final int STATE_PRESSED_DOWN = 2; - public static final int STATE_PRESSED_LEFT = 3; - public static final int STATE_PRESSED_RIGHT = 4; - public static final int STATE_PRESSED_UP_LEFT = 5; - public static final int STATE_PRESSED_UP_RIGHT = 6; - public static final int STATE_PRESSED_DOWN_LEFT = 7; - public static final int STATE_PRESSED_DOWN_RIGHT = 8; + /** + * Constructor + * + * @param res {@link Resources} instance. + * @param defaultStateBitmap {@link Bitmap} of the default state. + * @param pressedOneDirectionStateBitmap {@link Bitmap} of the pressed state in one direction. + * @param pressedTwoDirectionsStateBitmap {@link Bitmap} of the pressed state in two direction. + * @param buttonUp Identifier for the up button. + * @param buttonDown Identifier for the down button. + * @param buttonLeft Identifier for the left button. + * @param buttonRight Identifier for the right button. + */ + public InputOverlayDrawableDpad(Resources res, + Bitmap defaultStateBitmap, + Bitmap pressedOneDirectionStateBitmap, + Bitmap pressedTwoDirectionsStateBitmap, + int buttonUp, int buttonDown, + int buttonLeft, int buttonRight) { + mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); + mPressedOneDirectionStateBitmap = new BitmapDrawable(res, pressedOneDirectionStateBitmap); + mPressedTwoDirectionsStateBitmap = new BitmapDrawable(res, pressedTwoDirectionsStateBitmap); - /** - * Constructor - * - * @param res {@link Resources} instance. - * @param defaultStateBitmap {@link Bitmap} of the default state. - * @param pressedOneDirectionStateBitmap {@link Bitmap} of the pressed state in one direction. - * @param pressedTwoDirectionsStateBitmap {@link Bitmap} of the pressed state in two direction. - * @param buttonUp Identifier for the up button. - * @param buttonDown Identifier for the down button. - * @param buttonLeft Identifier for the left button. - * @param buttonRight Identifier for the right button. - */ - public InputOverlayDrawableDpad(Resources res, - Bitmap defaultStateBitmap, - Bitmap pressedOneDirectionStateBitmap, - Bitmap pressedTwoDirectionsStateBitmap, - int buttonUp, int buttonDown, - int buttonLeft, int buttonRight) - { - mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); - mPressedOneDirectionStateBitmap = new BitmapDrawable(res, pressedOneDirectionStateBitmap); - mPressedTwoDirectionsStateBitmap = new BitmapDrawable(res, pressedTwoDirectionsStateBitmap); + mWidth = mDefaultStateBitmap.getIntrinsicWidth(); + mHeight = mDefaultStateBitmap.getIntrinsicHeight(); - mWidth = mDefaultStateBitmap.getIntrinsicWidth(); - mHeight = mDefaultStateBitmap.getIntrinsicHeight(); - - mButtonType[0] = buttonUp; - mButtonType[1] = buttonDown; - mButtonType[2] = buttonLeft; - mButtonType[3] = buttonRight; - } - - public void draw(Canvas canvas) - { - int px = mControlPositionX + (getWidth() / 2); - int py = mControlPositionY + (getHeight() / 2); - switch (mPressState) - { - case STATE_DEFAULT: - mDefaultStateBitmap.draw(canvas); - break; - case STATE_PRESSED_UP: - mPressedOneDirectionStateBitmap.draw(canvas); - break; - case STATE_PRESSED_RIGHT: - canvas.save(); - canvas.rotate(90, px, py); - mPressedOneDirectionStateBitmap.draw(canvas); - canvas.restore(); - break; - case STATE_PRESSED_DOWN: - canvas.save(); - canvas.rotate(180, px, py); - mPressedOneDirectionStateBitmap.draw(canvas); - canvas.restore(); - break; - case STATE_PRESSED_LEFT: - canvas.save(); - canvas.rotate(270, px, py); - mPressedOneDirectionStateBitmap.draw(canvas); - canvas.restore(); - break; - case STATE_PRESSED_UP_LEFT: - mPressedTwoDirectionsStateBitmap.draw(canvas); - break; - case STATE_PRESSED_UP_RIGHT: - canvas.save(); - canvas.rotate(90, px, py); - mPressedTwoDirectionsStateBitmap.draw(canvas); - canvas.restore(); - break; - case STATE_PRESSED_DOWN_RIGHT: - canvas.save(); - canvas.rotate(180, px, py); - mPressedTwoDirectionsStateBitmap.draw(canvas); - canvas.restore(); - break; - case STATE_PRESSED_DOWN_LEFT: - canvas.save(); - canvas.rotate(270, px, py); - mPressedTwoDirectionsStateBitmap.draw(canvas); - canvas.restore(); - break; + mButtonType[0] = buttonUp; + mButtonType[1] = buttonDown; + mButtonType[2] = buttonLeft; + mButtonType[3] = buttonRight; } - } - - /** - * Gets one of the InputOverlayDrawableDpad's button IDs. - * - * @return the requested InputOverlayDrawableDpad's button ID. - */ - public int getId(int direction) - { - return mButtonType[direction]; - } - - public void setTrackId(int trackId) - { - mTrackId = trackId; - } - - public int getTrackId() - { - return mTrackId; - } - - public boolean onConfigureTouch(MotionEvent event) - { - int pointerIndex = event.getActionIndex(); - int fingerPositionX = (int) event.getX(pointerIndex); - int fingerPositionY = (int) event.getY(pointerIndex); - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - mPreviousTouchX = fingerPositionX; - mPreviousTouchY = fingerPositionY; - break; - case MotionEvent.ACTION_MOVE: - mControlPositionX += fingerPositionX - mPreviousTouchX; - mControlPositionY += fingerPositionY - mPreviousTouchY; - setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX, - getHeight() + mControlPositionY); - mPreviousTouchX = fingerPositionX; - mPreviousTouchY = fingerPositionY; - break; + public void draw(Canvas canvas) { + int px = mControlPositionX + (getWidth() / 2); + int py = mControlPositionY + (getHeight() / 2); + switch (mPressState) { + case STATE_DEFAULT: + mDefaultStateBitmap.draw(canvas); + break; + case STATE_PRESSED_UP: + mPressedOneDirectionStateBitmap.draw(canvas); + break; + case STATE_PRESSED_RIGHT: + canvas.save(); + canvas.rotate(90, px, py); + mPressedOneDirectionStateBitmap.draw(canvas); + canvas.restore(); + break; + case STATE_PRESSED_DOWN: + canvas.save(); + canvas.rotate(180, px, py); + mPressedOneDirectionStateBitmap.draw(canvas); + canvas.restore(); + break; + case STATE_PRESSED_LEFT: + canvas.save(); + canvas.rotate(270, px, py); + mPressedOneDirectionStateBitmap.draw(canvas); + canvas.restore(); + break; + case STATE_PRESSED_UP_LEFT: + mPressedTwoDirectionsStateBitmap.draw(canvas); + break; + case STATE_PRESSED_UP_RIGHT: + canvas.save(); + canvas.rotate(90, px, py); + mPressedTwoDirectionsStateBitmap.draw(canvas); + canvas.restore(); + break; + case STATE_PRESSED_DOWN_RIGHT: + canvas.save(); + canvas.rotate(180, px, py); + mPressedTwoDirectionsStateBitmap.draw(canvas); + canvas.restore(); + break; + case STATE_PRESSED_DOWN_LEFT: + canvas.save(); + canvas.rotate(270, px, py); + mPressedTwoDirectionsStateBitmap.draw(canvas); + canvas.restore(); + break; + } } - return true; - } - public void setPosition(int x, int y) - { - mControlPositionX = x; - mControlPositionY = y; - } + /** + * Gets one of the InputOverlayDrawableDpad's button IDs. + * + * @return the requested InputOverlayDrawableDpad's button ID. + */ + public int getId(int direction) { + return mButtonType[direction]; + } - public void setBounds(int left, int top, int right, int bottom) - { - mDefaultStateBitmap.setBounds(left, top, right, bottom); - mPressedOneDirectionStateBitmap.setBounds(left, top, right, bottom); - mPressedTwoDirectionsStateBitmap.setBounds(left, top, right, bottom); - } + public int getTrackId() { + return mTrackId; + } - public Rect getBounds() - { - return mDefaultStateBitmap.getBounds(); - } + public void setTrackId(int trackId) { + mTrackId = trackId; + } - public int getWidth() - { - return mWidth; - } + public boolean onConfigureTouch(MotionEvent event) { + int pointerIndex = event.getActionIndex(); + int fingerPositionX = (int) event.getX(pointerIndex); + int fingerPositionY = (int) event.getY(pointerIndex); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; + case MotionEvent.ACTION_MOVE: + mControlPositionX += fingerPositionX - mPreviousTouchX; + mControlPositionY += fingerPositionY - mPreviousTouchY; + setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX, + getHeight() + mControlPositionY); + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; - public int getHeight() - { - return mHeight; - } + } + return true; + } - public void setState(int pressState) - { - mPressState = pressState; - } + public void setPosition(int x, int y) { + mControlPositionX = x; + mControlPositionY = y; + } + + public void setBounds(int left, int top, int right, int bottom) { + mDefaultStateBitmap.setBounds(left, top, right, bottom); + mPressedOneDirectionStateBitmap.setBounds(left, top, right, bottom); + mPressedTwoDirectionsStateBitmap.setBounds(left, top, right, bottom); + } + + public Rect getBounds() { + return mDefaultStateBitmap.getBounds(); + } + + public int getWidth() { + return mWidth; + } + + public int getHeight() { + return mHeight; + } + + public void setState(int pressState) { + mPressState = pressState; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableJoystick.java b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableJoystick.java index c1f65b1d8..54040085b 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableJoystick.java +++ b/src/android/app/src/main/java/org/citra/citra_android/overlay/InputOverlayDrawableJoystick.java @@ -17,248 +17,224 @@ import android.view.MotionEvent; * Custom {@link BitmapDrawable} that is capable * of storing it's own ID. */ -public final class InputOverlayDrawableJoystick -{ - private final int[] axisIDs = {0, 0, 0, 0}; - private final float[] axises = {0f, 0f}; - private int trackId = -1; - private int mJoystickType; - private int mControlPositionX, mControlPositionY; - private int mPreviousTouchX, mPreviousTouchY; - private int mWidth; - private int mHeight; - private Rect mVirtBounds; - private Rect mOrigBounds; - private BitmapDrawable mOuterBitmap; - private BitmapDrawable mDefaultStateInnerBitmap; - private BitmapDrawable mPressedStateInnerBitmap; - private BitmapDrawable mBoundsBoxBitmap; - private boolean mPressedState = false; +public final class InputOverlayDrawableJoystick { + private final int[] axisIDs = {0, 0, 0, 0}; + private final float[] axises = {0f, 0f}; + private int trackId = -1; + private int mJoystickType; + private int mControlPositionX, mControlPositionY; + private int mPreviousTouchX, mPreviousTouchY; + private int mWidth; + private int mHeight; + private Rect mVirtBounds; + private Rect mOrigBounds; + private BitmapDrawable mOuterBitmap; + private BitmapDrawable mDefaultStateInnerBitmap; + private BitmapDrawable mPressedStateInnerBitmap; + private BitmapDrawable mBoundsBoxBitmap; + private boolean mPressedState = false; - /** - * Constructor - * - * @param res {@link Resources} instance. - * @param bitmapOuter {@link Bitmap} which represents the outer non-movable part of the joystick. - * @param bitmapInnerDefault {@link Bitmap} which represents the default inner movable part of the joystick. - * @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick. - * @param rectOuter {@link Rect} which represents the outer joystick bounds. - * @param rectInner {@link Rect} which represents the inner joystick bounds. - * @param joystick Identifier for which joystick this is. - */ - public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, - Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed, - Rect rectOuter, Rect rectInner, int joystick) - { - axisIDs[0] = joystick + 1; // Up - axisIDs[1] = joystick + 2; // Down - axisIDs[2] = joystick + 3; // Left - axisIDs[3] = joystick + 4; // Right - mJoystickType = joystick; + /** + * Constructor + * + * @param res {@link Resources} instance. + * @param bitmapOuter {@link Bitmap} which represents the outer non-movable part of the joystick. + * @param bitmapInnerDefault {@link Bitmap} which represents the default inner movable part of the joystick. + * @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick. + * @param rectOuter {@link Rect} which represents the outer joystick bounds. + * @param rectInner {@link Rect} which represents the inner joystick bounds. + * @param joystick Identifier for which joystick this is. + */ + public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, + Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed, + Rect rectOuter, Rect rectInner, int joystick) { + axisIDs[0] = joystick + 1; // Up + axisIDs[1] = joystick + 2; // Down + axisIDs[2] = joystick + 3; // Left + axisIDs[3] = joystick + 4; // Right + mJoystickType = joystick; - mOuterBitmap = new BitmapDrawable(res, bitmapOuter); - mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault); - mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed); - mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter); - mWidth = bitmapOuter.getWidth(); - mHeight = bitmapOuter.getHeight(); - - setBounds(rectOuter); - mDefaultStateInnerBitmap.setBounds(rectInner); - mPressedStateInnerBitmap.setBounds(rectInner); - mVirtBounds = getBounds(); - mOrigBounds = mOuterBitmap.copyBounds(); - mBoundsBoxBitmap.setAlpha(0); - mBoundsBoxBitmap.setBounds(getVirtBounds()); - SetInnerBounds(); - } - - /** - * Gets this InputOverlayDrawableJoystick's button ID. - * - * @return this InputOverlayDrawableJoystick's button ID. - */ - public int getId() - { - return mJoystickType; - } - - public void draw(Canvas canvas) - { - mOuterBitmap.draw(canvas); - getCurrentStateBitmapDrawable().draw(canvas); - mBoundsBoxBitmap.draw(canvas); - } - - public void TrackEvent(MotionEvent event) - { - int pointerIndex = event.getActionIndex(); - - switch (event.getAction() & MotionEvent.ACTION_MASK) - { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - if (getBounds().contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) - { - mPressedState = true; - mOuterBitmap.setAlpha(0); - mBoundsBoxBitmap.setAlpha(255); - getVirtBounds().offset((int) event.getX(pointerIndex) - getVirtBounds().centerX(), - (int) event.getY(pointerIndex) - getVirtBounds().centerY()); - mBoundsBoxBitmap.setBounds(getVirtBounds()); - trackId = event.getPointerId(pointerIndex); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - if (trackId == event.getPointerId(pointerIndex)) - { - mPressedState = false; - axises[0] = axises[1] = 0.0f; - mOuterBitmap.setAlpha(255); - mBoundsBoxBitmap.setAlpha(0); - setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, - mOrigBounds.bottom)); - setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, - mOrigBounds.bottom)); - SetInnerBounds(); - trackId = -1; - } - break; - } - - if (trackId == -1) - return; - - for (int i = 0; i < event.getPointerCount(); i++) - { - if (trackId == event.getPointerId(i)) - { - float touchX = event.getX(i); - float touchY = event.getY(i); - float maxY = getVirtBounds().bottom; - float maxX = getVirtBounds().right; - touchX -= getVirtBounds().centerX(); - maxX -= getVirtBounds().centerX(); - touchY -= getVirtBounds().centerY(); - maxY -= getVirtBounds().centerY(); - final float AxisX = touchX / maxX; - final float AxisY = touchY / maxY; - axises[0] = AxisX; - axises[1] = AxisY; + mOuterBitmap = new BitmapDrawable(res, bitmapOuter); + mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault); + mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed); + mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter); + mWidth = bitmapOuter.getWidth(); + mHeight = bitmapOuter.getHeight(); + setBounds(rectOuter); + mDefaultStateInnerBitmap.setBounds(rectInner); + mPressedStateInnerBitmap.setBounds(rectInner); + mVirtBounds = getBounds(); + mOrigBounds = mOuterBitmap.copyBounds(); + mBoundsBoxBitmap.setAlpha(0); + mBoundsBoxBitmap.setBounds(getVirtBounds()); SetInnerBounds(); - } } - } - public boolean onConfigureTouch(MotionEvent event) - { - int pointerIndex = event.getActionIndex(); - int fingerPositionX = (int) event.getX(pointerIndex); - int fingerPositionY = (int) event.getY(pointerIndex); - switch (event.getAction()) - { - case MotionEvent.ACTION_DOWN: - mPreviousTouchX = fingerPositionX; - mPreviousTouchY = fingerPositionY; - break; - case MotionEvent.ACTION_MOVE: - int deltaX = fingerPositionX - mPreviousTouchX; - int deltaY = fingerPositionY - mPreviousTouchY; - mControlPositionX += deltaX; - mControlPositionY += deltaY; - setBounds(new Rect(mControlPositionX, mControlPositionY, - mOuterBitmap.getIntrinsicWidth() + mControlPositionX, - mOuterBitmap.getIntrinsicHeight() + mControlPositionY)); - setVirtBounds(new Rect(mControlPositionX, mControlPositionY, - mOuterBitmap.getIntrinsicWidth() + mControlPositionX, - mOuterBitmap.getIntrinsicHeight() + mControlPositionY)); - SetInnerBounds(); - setOrigBounds(new Rect(new Rect(mControlPositionX, mControlPositionY, - mOuterBitmap.getIntrinsicWidth() + mControlPositionX, - mOuterBitmap.getIntrinsicHeight() + mControlPositionY))); - mPreviousTouchX = fingerPositionX; - mPreviousTouchY = fingerPositionY; - break; + /** + * Gets this InputOverlayDrawableJoystick's button ID. + * + * @return this InputOverlayDrawableJoystick's button ID. + */ + public int getId() { + return mJoystickType; + } + + public void draw(Canvas canvas) { + mOuterBitmap.draw(canvas); + getCurrentStateBitmapDrawable().draw(canvas); + mBoundsBoxBitmap.draw(canvas); + } + + public void TrackEvent(MotionEvent event) { + int pointerIndex = event.getActionIndex(); + + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + if (getBounds().contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) { + mPressedState = true; + mOuterBitmap.setAlpha(0); + mBoundsBoxBitmap.setAlpha(255); + getVirtBounds().offset((int) event.getX(pointerIndex) - getVirtBounds().centerX(), + (int) event.getY(pointerIndex) - getVirtBounds().centerY()); + mBoundsBoxBitmap.setBounds(getVirtBounds()); + trackId = event.getPointerId(pointerIndex); + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + if (trackId == event.getPointerId(pointerIndex)) { + mPressedState = false; + axises[0] = axises[1] = 0.0f; + mOuterBitmap.setAlpha(255); + mBoundsBoxBitmap.setAlpha(0); + setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, + mOrigBounds.bottom)); + setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, + mOrigBounds.bottom)); + SetInnerBounds(); + trackId = -1; + } + break; + } + + if (trackId == -1) + return; + + for (int i = 0; i < event.getPointerCount(); i++) { + if (trackId == event.getPointerId(i)) { + float touchX = event.getX(i); + float touchY = event.getY(i); + float maxY = getVirtBounds().bottom; + float maxX = getVirtBounds().right; + touchX -= getVirtBounds().centerX(); + maxX -= getVirtBounds().centerX(); + touchY -= getVirtBounds().centerY(); + maxY -= getVirtBounds().centerY(); + final float AxisX = touchX / maxX; + final float AxisY = touchY / maxY; + axises[0] = AxisX; + axises[1] = AxisY; + + SetInnerBounds(); + } + } + } + + public boolean onConfigureTouch(MotionEvent event) { + int pointerIndex = event.getActionIndex(); + int fingerPositionX = (int) event.getX(pointerIndex); + int fingerPositionY = (int) event.getY(pointerIndex); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; + case MotionEvent.ACTION_MOVE: + int deltaX = fingerPositionX - mPreviousTouchX; + int deltaY = fingerPositionY - mPreviousTouchY; + mControlPositionX += deltaX; + mControlPositionY += deltaY; + setBounds(new Rect(mControlPositionX, mControlPositionY, + mOuterBitmap.getIntrinsicWidth() + mControlPositionX, + mOuterBitmap.getIntrinsicHeight() + mControlPositionY)); + setVirtBounds(new Rect(mControlPositionX, mControlPositionY, + mOuterBitmap.getIntrinsicWidth() + mControlPositionX, + mOuterBitmap.getIntrinsicHeight() + mControlPositionY)); + SetInnerBounds(); + setOrigBounds(new Rect(new Rect(mControlPositionX, mControlPositionY, + mOuterBitmap.getIntrinsicWidth() + mControlPositionX, + mOuterBitmap.getIntrinsicHeight() + mControlPositionY))); + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; + } + return true; } - return true; - } - public float[] getAxisValues() - { - return axises; - } + public float[] getAxisValues() { + return axises; + } - public int[] getAxisIDs() - { - return axisIDs; - } + public int[] getAxisIDs() { + return axisIDs; + } - private void SetInnerBounds() - { - int X = getVirtBounds().centerX() + (int) ((axises[0]) * (getVirtBounds().width() / 2)); - int Y = getVirtBounds().centerY() + (int) ((axises[1]) * (getVirtBounds().height() / 2)); + private void SetInnerBounds() { + int X = getVirtBounds().centerX() + (int) ((axises[0]) * (getVirtBounds().width() / 2)); + int Y = getVirtBounds().centerY() + (int) ((axises[1]) * (getVirtBounds().height() / 2)); - if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2)) - X = getVirtBounds().centerX() + (getVirtBounds().width() / 2); - if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2)) - X = getVirtBounds().centerX() - (getVirtBounds().width() / 2); - if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2)) - Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2); - if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2)) - Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2); + if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2)) + X = getVirtBounds().centerX() + (getVirtBounds().width() / 2); + if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2)) + X = getVirtBounds().centerX() - (getVirtBounds().width() / 2); + if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2)) + Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2); + if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2)) + Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2); - int width = mPressedStateInnerBitmap.getBounds().width() / 2; - int height = mPressedStateInnerBitmap.getBounds().height() / 2; - mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height); - mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds()); - } + int width = mPressedStateInnerBitmap.getBounds().width() / 2; + int height = mPressedStateInnerBitmap.getBounds().height() / 2; + mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height); + mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds()); + } - public void setPosition(int x, int y) - { - mControlPositionX = x; - mControlPositionY = y; - } + public void setPosition(int x, int y) { + mControlPositionX = x; + mControlPositionY = y; + } - private BitmapDrawable getCurrentStateBitmapDrawable() - { - return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap; - } + private BitmapDrawable getCurrentStateBitmapDrawable() { + return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap; + } - public void setBounds(Rect bounds) - { - mOuterBitmap.setBounds(bounds); - } + public Rect getBounds() { + return mOuterBitmap.getBounds(); + } - public Rect getBounds() - { - return mOuterBitmap.getBounds(); - } + public void setBounds(Rect bounds) { + mOuterBitmap.setBounds(bounds); + } - private void setVirtBounds(Rect bounds) - { - mVirtBounds = bounds; - } + private void setOrigBounds(Rect bounds) { + mOrigBounds = bounds; + } - private void setOrigBounds(Rect bounds) - { - mOrigBounds = bounds; - } + private Rect getVirtBounds() { + return mVirtBounds; + } - private Rect getVirtBounds() - { - return mVirtBounds; - } + private void setVirtBounds(Rect bounds) { + mVirtBounds = bounds; + } - public int getWidth() - { - return mWidth; - } + public int getWidth() { + return mWidth; + } - public int getHeight() - { - return mHeight; - } + public int getHeight() { + return mHeight; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/services/DirectoryInitializationService.java b/src/android/app/src/main/java/org/citra/citra_android/services/DirectoryInitializationService.java index a658261fc..bf4463976 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/services/DirectoryInitializationService.java +++ b/src/android/app/src/main/java/org/citra/citra_android/services/DirectoryInitializationService.java @@ -30,204 +30,166 @@ import java.util.concurrent.atomic.AtomicBoolean; * A service that spawns its own thread in order to copy several binary and shader files * from the Dolphin APK to the external file system. */ -public final class DirectoryInitializationService extends IntentService -{ - public static final String BROADCAST_ACTION = "org.citra.citra_android.BROADCAST"; +public final class DirectoryInitializationService extends IntentService { + public static final String BROADCAST_ACTION = "org.citra.citra_android.BROADCAST"; - public static final String EXTRA_STATE = "directoryState"; - private static volatile DirectoryInitializationState directoryState = null; - private static String userPath; - private static AtomicBoolean isDolphinDirectoryInitializationRunning = new AtomicBoolean(false); + public static final String EXTRA_STATE = "directoryState"; + private static volatile DirectoryInitializationState directoryState = null; + private static String userPath; + private static AtomicBoolean isDolphinDirectoryInitializationRunning = new AtomicBoolean(false); - public enum DirectoryInitializationState - { - DOLPHIN_DIRECTORIES_INITIALIZED, - EXTERNAL_STORAGE_PERMISSION_NEEDED, - CANT_FIND_EXTERNAL_STORAGE - } + public DirectoryInitializationService() { + // Superclass constructor is called to name the thread on which this service executes. + super("DirectoryInitializationService"); + } - public DirectoryInitializationService() - { - // Superclass constructor is called to name the thread on which this service executes. - super("DirectoryInitializationService"); - } + public static void startService(Context context) { + Intent intent = new Intent(context, DirectoryInitializationService.class); + context.startService(intent); + } - public static void startService(Context context) - { - Intent intent = new Intent(context, DirectoryInitializationService.class); - context.startService(intent); - } - - @Override - protected void onHandleIntent(Intent intent) - { - isDolphinDirectoryInitializationRunning.set(true); - - if (directoryState != DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) - { - if (PermissionsHandler.hasWriteAccess(this)) - { - if (setDolphinUserDirectory()) - { - initializeInternalStorage(); - CreateUserDirectories(); - NativeLibrary.CreateConfigFile(); - directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; + private static void deleteDirectoryRecursively(File file) { + if (file.isDirectory()) { + for (File child : file.listFiles()) + deleteDirectoryRecursively(child); } - else - { - directoryState = DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE; + file.delete(); + } + + public static boolean areDolphinDirectoriesReady() { + return directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; + } + + public static String getUserDirectory() { + if (directoryState == null) { + throw new IllegalStateException("DirectoryInitializationService has to run at least once!"); + } else if (isDolphinDirectoryInitializationRunning.get()) { + throw new IllegalStateException( + "DirectoryInitializationService has to finish running first!"); } - } - else - { - directoryState = DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED; - } - } - - isDolphinDirectoryInitializationRunning.set(false); - sendBroadcastState(directoryState); - } - - private boolean setDolphinUserDirectory() - { - if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) - { - File externalPath = Environment.getExternalStorageDirectory(); - if (externalPath != null) - { - userPath = externalPath.getAbsolutePath() + "/citra-emu"; - Log.debug("[DirectoryInitializationService] User Dir: " + userPath); - // NativeLibrary.SetUserDirectory(userPath); - return true; - } + return userPath; } - return false; - } + private static native void CreateUserDirectories(); - private void initializeInternalStorage() - { - File sysDirectory = new File(getFilesDir(), "Sys"); + private static native void SetSysDirectory(String path); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - String revision = NativeLibrary.GetGitRevision(); - if (!preferences.getString("sysDirectoryVersion", "").equals(revision)) - { - // There is no extracted Sys directory, or there is a Sys directory from another - // version of Dolphin that might contain outdated files. Let's (re-)extract Sys. - deleteDirectoryRecursively(sysDirectory); - copyAssetFolder("Sys", sysDirectory, true); + @Override + protected void onHandleIntent(Intent intent) { + isDolphinDirectoryInitializationRunning.set(true); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString("sysDirectoryVersion", revision); - editor.apply(); - } - - // Let the native code know where the Sys directory is. - SetSysDirectory(sysDirectory.getPath()); - } - - private static void deleteDirectoryRecursively(File file) - { - if (file.isDirectory()) - { - for (File child : file.listFiles()) - deleteDirectoryRecursively(child); - } - file.delete(); - } - - public static boolean areDolphinDirectoriesReady() - { - return directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; - } - - public static String getUserDirectory() - { - if (directoryState == null) - { - throw new IllegalStateException("DirectoryInitializationService has to run at least once!"); - } - else if (isDolphinDirectoryInitializationRunning.get()) - { - throw new IllegalStateException( - "DirectoryInitializationService has to finish running first!"); - } - return userPath; - - } - - private void sendBroadcastState(DirectoryInitializationState state) - { - Intent localIntent = - new Intent(BROADCAST_ACTION) - .putExtra(EXTRA_STATE, state); - LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); - } - - private void copyAsset(String asset, File output, Boolean overwrite) - { - Log.verbose("[DirectoryInitializationService] Copying File " + asset + " to " + output); - - try - { - if (!output.exists() || overwrite) - { - InputStream in = getAssets().open(asset); - OutputStream out = new FileOutputStream(output); - copyFile(in, out); - in.close(); - out.close(); - } - } - catch (IOException e) - { - Log.error("[DirectoryInitializationService] Failed to copy asset file: " + asset + - e.getMessage()); - } - } - - private void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite) - { - Log.verbose("[DirectoryInitializationService] Copying Folder " + assetFolder + " to " + - outputFolder); - - try - { - boolean createdFolder = false; - for (String file : getAssets().list(assetFolder)) - { - if (!createdFolder) - { - outputFolder.mkdir(); - createdFolder = true; + if (directoryState != DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) { + if (PermissionsHandler.hasWriteAccess(this)) { + if (setDolphinUserDirectory()) { + initializeInternalStorage(); + CreateUserDirectories(); + NativeLibrary.CreateConfigFile(); + directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; + } else { + directoryState = DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE; + } + } else { + directoryState = DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED; + } } - copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file), - overwrite); - copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite); - } + + isDolphinDirectoryInitializationRunning.set(false); + sendBroadcastState(directoryState); } - catch (IOException e) - { - Log.error("[DirectoryInitializationService] Failed to copy asset folder: " + assetFolder + - e.getMessage()); + + private boolean setDolphinUserDirectory() { + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + File externalPath = Environment.getExternalStorageDirectory(); + if (externalPath != null) { + userPath = externalPath.getAbsolutePath() + "/citra-emu"; + Log.debug("[DirectoryInitializationService] User Dir: " + userPath); + // NativeLibrary.SetUserDirectory(userPath); + return true; + } + + } + + return false; } - } - private void copyFile(InputStream in, OutputStream out) throws IOException - { - byte[] buffer = new byte[1024]; - int read; + private void initializeInternalStorage() { + File sysDirectory = new File(getFilesDir(), "Sys"); - while ((read = in.read(buffer)) != -1) - { - out.write(buffer, 0, read); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + String revision = NativeLibrary.GetGitRevision(); + if (!preferences.getString("sysDirectoryVersion", "").equals(revision)) { + // There is no extracted Sys directory, or there is a Sys directory from another + // version of Dolphin that might contain outdated files. Let's (re-)extract Sys. + deleteDirectoryRecursively(sysDirectory); + copyAssetFolder("Sys", sysDirectory, true); + + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("sysDirectoryVersion", revision); + editor.apply(); + } + + // Let the native code know where the Sys directory is. + SetSysDirectory(sysDirectory.getPath()); } - } - private static native void CreateUserDirectories(); + private void sendBroadcastState(DirectoryInitializationState state) { + Intent localIntent = + new Intent(BROADCAST_ACTION) + .putExtra(EXTRA_STATE, state); + LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); + } - private static native void SetSysDirectory(String path); + private void copyAsset(String asset, File output, Boolean overwrite) { + Log.verbose("[DirectoryInitializationService] Copying File " + asset + " to " + output); + + try { + if (!output.exists() || overwrite) { + InputStream in = getAssets().open(asset); + OutputStream out = new FileOutputStream(output); + copyFile(in, out); + in.close(); + out.close(); + } + } catch (IOException e) { + Log.error("[DirectoryInitializationService] Failed to copy asset file: " + asset + + e.getMessage()); + } + } + + private void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite) { + Log.verbose("[DirectoryInitializationService] Copying Folder " + assetFolder + " to " + + outputFolder); + + try { + boolean createdFolder = false; + for (String file : getAssets().list(assetFolder)) { + if (!createdFolder) { + outputFolder.mkdir(); + createdFolder = true; + } + copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file), + overwrite); + copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite); + } + } catch (IOException e) { + Log.error("[DirectoryInitializationService] Failed to copy asset folder: " + assetFolder + + e.getMessage()); + } + } + + private void copyFile(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[1024]; + int read; + + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + } + + public enum DirectoryInitializationState { + DOLPHIN_DIRECTORIES_INITIALIZED, + EXTERNAL_STORAGE_PERMISSION_NEEDED, + CANT_FIND_EXTERNAL_STORAGE + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/services/USBPermService.java b/src/android/app/src/main/java/org/citra/citra_android/services/USBPermService.java index 975857815..f1c0d4375 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/services/USBPermService.java +++ b/src/android/app/src/main/java/org/citra/citra_android/services/USBPermService.java @@ -3,17 +3,14 @@ package org.citra.citra_android.services; import android.app.IntentService; import android.content.Intent; -public final class USBPermService extends IntentService -{ - public USBPermService() - { - super("USBPermService"); - } +public final class USBPermService extends IntentService { + public USBPermService() { + super("USBPermService"); + } - // Needed when extending IntentService. - // We don't care about the results of the intent handler for this. - @Override - protected void onHandleIntent(Intent intent) - { - } + // Needed when extending IntentService. + // We don't care about the results of the intent handler for this. + @Override + protected void onHandleIntent(Intent intent) { + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/DividerItemDecoration.java b/src/android/app/src/main/java/org/citra/citra_android/ui/DividerItemDecoration.java index 355cbd7f5..b75d09d47 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/DividerItemDecoration.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/DividerItemDecoration.java @@ -14,144 +14,116 @@ import android.view.View; * Implementation from: * https://gist.github.com/lapastillaroja/858caf1a82791b6c1a36 */ -public final class DividerItemDecoration extends RecyclerView.ItemDecoration -{ +public final class DividerItemDecoration extends RecyclerView.ItemDecoration { - private Drawable mDivider; - private boolean mShowFirstDivider = false; - private boolean mShowLastDivider = false; + private Drawable mDivider; + private boolean mShowFirstDivider = false; + private boolean mShowLastDivider = false; - public DividerItemDecoration(Context context, AttributeSet attrs) - { - final TypedArray a = context - .obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider}); - mDivider = a.getDrawable(0); - a.recycle(); - } - - public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider, - boolean showLastDivider) - { - this(context, attrs); - mShowFirstDivider = showFirstDivider; - mShowLastDivider = showLastDivider; - } - - public DividerItemDecoration(Drawable divider) - { - mDivider = divider; - } - - public DividerItemDecoration(Drawable divider, boolean showFirstDivider, - boolean showLastDivider) - { - this(divider); - mShowFirstDivider = showFirstDivider; - mShowLastDivider = showLastDivider; - } - - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, - RecyclerView.State state) - { - super.getItemOffsets(outRect, view, parent, state); - if (mDivider == null) - { - return; - } - if (parent.getChildPosition(view) < 1) - { - return; + public DividerItemDecoration(Context context, AttributeSet attrs) { + final TypedArray a = context + .obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider}); + mDivider = a.getDrawable(0); + a.recycle(); } - if (getOrientation(parent) == LinearLayoutManager.VERTICAL) - { - outRect.top = mDivider.getIntrinsicHeight(); - } - else - { - outRect.left = mDivider.getIntrinsicWidth(); - } - } - - @Override - public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) - { - if (mDivider == null) - { - super.onDrawOver(c, parent, state); - return; + public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider, + boolean showLastDivider) { + this(context, attrs); + mShowFirstDivider = showFirstDivider; + mShowLastDivider = showLastDivider; } - // Initialization needed to avoid compiler warning - int left = 0, right = 0, top = 0, bottom = 0, size; - int orientation = getOrientation(parent); - int childCount = parent.getChildCount(); - - if (orientation == LinearLayoutManager.VERTICAL) - { - size = mDivider.getIntrinsicHeight(); - left = parent.getPaddingLeft(); - right = parent.getWidth() - parent.getPaddingRight(); - } - else - { //horizontal - size = mDivider.getIntrinsicWidth(); - top = parent.getPaddingTop(); - bottom = parent.getHeight() - parent.getPaddingBottom(); + public DividerItemDecoration(Drawable divider) { + mDivider = divider; } - for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) - { - View child = parent.getChildAt(i); - RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); - - if (orientation == LinearLayoutManager.VERTICAL) - { - top = child.getTop() - params.topMargin; - bottom = top + size; - } - else - { //horizontal - left = child.getLeft() - params.leftMargin; - right = left + size; - } - mDivider.setBounds(left, top, right, bottom); - mDivider.draw(c); + public DividerItemDecoration(Drawable divider, boolean showFirstDivider, + boolean showLastDivider) { + this(divider); + mShowFirstDivider = showFirstDivider; + mShowLastDivider = showLastDivider; } - // show last divider - if (mShowLastDivider && childCount > 0) - { - View child = parent.getChildAt(childCount - 1); - RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); - if (orientation == LinearLayoutManager.VERTICAL) - { - top = child.getBottom() + params.bottomMargin; - bottom = top + size; - } - else - { // horizontal - left = child.getRight() + params.rightMargin; - right = left + size; - } - mDivider.setBounds(left, top, right, bottom); - mDivider.draw(c); - } - } + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, + RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + if (mDivider == null) { + return; + } + if (parent.getChildPosition(view) < 1) { + return; + } - private int getOrientation(RecyclerView parent) - { - if (parent.getLayoutManager() instanceof LinearLayoutManager) - { - LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager(); - return layoutManager.getOrientation(); + if (getOrientation(parent) == LinearLayoutManager.VERTICAL) { + outRect.top = mDivider.getIntrinsicHeight(); + } else { + outRect.left = mDivider.getIntrinsicWidth(); + } } - else - { - throw new IllegalStateException( - "DividerItemDecoration can only be used with a LinearLayoutManager."); + + @Override + public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { + if (mDivider == null) { + super.onDrawOver(c, parent, state); + return; + } + + // Initialization needed to avoid compiler warning + int left = 0, right = 0, top = 0, bottom = 0, size; + int orientation = getOrientation(parent); + int childCount = parent.getChildCount(); + + if (orientation == LinearLayoutManager.VERTICAL) { + size = mDivider.getIntrinsicHeight(); + left = parent.getPaddingLeft(); + right = parent.getWidth() - parent.getPaddingRight(); + } else { //horizontal + size = mDivider.getIntrinsicWidth(); + top = parent.getPaddingTop(); + bottom = parent.getHeight() - parent.getPaddingBottom(); + } + + for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) { + View child = parent.getChildAt(i); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + + if (orientation == LinearLayoutManager.VERTICAL) { + top = child.getTop() - params.topMargin; + bottom = top + size; + } else { //horizontal + left = child.getLeft() - params.leftMargin; + right = left + size; + } + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + + // show last divider + if (mShowLastDivider && childCount > 0) { + View child = parent.getChildAt(childCount - 1); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + if (orientation == LinearLayoutManager.VERTICAL) { + top = child.getBottom() + params.bottomMargin; + bottom = top + size; + } else { // horizontal + left = child.getRight() + params.rightMargin; + right = left + size; + } + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + } + + private int getOrientation(RecyclerView parent) { + if (parent.getLayoutManager() instanceof LinearLayoutManager) { + LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager(); + return layoutManager.getOrientation(); + } else { + throw new IllegalStateException( + "DividerItemDecoration can only be used with a LinearLayoutManager."); + } } - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/NVidiaShieldWorkaroundView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/NVidiaShieldWorkaroundView.java index 44c6b82a4..8cdde8353 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/NVidiaShieldWorkaroundView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/NVidiaShieldWorkaroundView.java @@ -13,13 +13,11 @@ import android.view.View; /** * Work around a bug with the nVidia Shield. */ -public final class NVidiaShieldWorkaroundView extends View -{ - public NVidiaShieldWorkaroundView(Context context, AttributeSet attrs) - { - super(context, attrs); +public final class NVidiaShieldWorkaroundView extends View { + public NVidiaShieldWorkaroundView(Context context, AttributeSet attrs) { + super(context, attrs); - // Setting this seems to workaround the bug - setWillNotDraw(false); - } + // Setting this seems to workaround the bug + setWillNotDraw(false); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragment.java b/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragment.java index 8eb509c79..e729a72ba 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragment.java @@ -1,6 +1,5 @@ package org.citra.citra_android.ui.input.gamecube; -public class ControllerFragment -{ +public class ControllerFragment { } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentPresenter.java index 036ca4ac8..c28b77eb2 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentPresenter.java @@ -1,6 +1,5 @@ package org.citra.citra_android.ui.input.gamecube; -public class ControllerFragmentPresenter -{ +public class ControllerFragmentPresenter { } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentView.java index 83ab52bb2..13278c6ae 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/input/gamecube/ControllerFragmentView.java @@ -1,6 +1,5 @@ package org.citra.citra_android.ui.input.gamecube; -public interface ControllerFragmentView -{ +public interface ControllerFragmentView { } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/main/CustomTitleView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/main/CustomTitleView.java index cc54d50b4..f00c05f62 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/main/CustomTitleView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/main/CustomTitleView.java @@ -11,72 +11,59 @@ import android.widget.TextView; import org.citra.citra_android.R; -public class CustomTitleView extends LinearLayout implements TitleViewAdapter.Provider -{ - private final TextView mTitleView; - private final View mBadgeView; +public class CustomTitleView extends LinearLayout implements TitleViewAdapter.Provider { + private final TextView mTitleView; + private final View mBadgeView; - private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter() - { - @Override - public View getSearchAffordanceView() - { - return null; + private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter() { + @Override + public View getSearchAffordanceView() { + return null; + } + + @Override + public void setTitle(CharSequence titleText) { + CustomTitleView.this.setTitle(titleText); + } + + @Override + public void setBadgeDrawable(Drawable drawable) { + CustomTitleView.this.setBadgeDrawable(drawable); + } + }; + + public CustomTitleView(Context context) { + this(context, null); + } + + public CustomTitleView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CustomTitleView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + View root = LayoutInflater.from(context).inflate(R.layout.tv_title, this); + mTitleView = root.findViewById(R.id.title); + mBadgeView = root.findViewById(R.id.badge); + } + + public void setTitle(CharSequence title) { + if (title != null) { + mTitleView.setText(title); + mTitleView.setVisibility(View.VISIBLE); + mBadgeView.setVisibility(View.VISIBLE); + } + } + + public void setBadgeDrawable(Drawable drawable) { + if (drawable != null) { + mTitleView.setVisibility(View.GONE); + mBadgeView.setVisibility(View.VISIBLE); + } } @Override - public void setTitle(CharSequence titleText) - { - CustomTitleView.this.setTitle(titleText); + public TitleViewAdapter getTitleViewAdapter() { + return mTitleViewAdapter; } - - @Override - public void setBadgeDrawable(Drawable drawable) - { - CustomTitleView.this.setBadgeDrawable(drawable); - } - }; - - public CustomTitleView(Context context) - { - this(context, null); - } - - public CustomTitleView(Context context, AttributeSet attrs) - { - this(context, attrs, 0); - } - - public CustomTitleView(Context context, AttributeSet attrs, int defStyle) - { - super(context, attrs, defStyle); - View root = LayoutInflater.from(context).inflate(R.layout.tv_title, this); - mTitleView = root.findViewById(R.id.title); - mBadgeView = root.findViewById(R.id.badge); - } - - public void setTitle(CharSequence title) - { - if (title != null) - { - mTitleView.setText(title); - mTitleView.setVisibility(View.VISIBLE); - mBadgeView.setVisibility(View.VISIBLE); - } - } - - public void setBadgeDrawable(Drawable drawable) - { - if (drawable != null) - { - mTitleView.setVisibility(View.GONE); - mBadgeView.setVisibility(View.VISIBLE); - } - } - - @Override - public TitleViewAdapter getTitleViewAdapter() - { - return mTitleViewAdapter; - } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java index 23e528d75..d0e81019e 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java @@ -28,186 +28,161 @@ import org.citra.citra_android.utils.StartupHandler; * The main Activity of the Lollipop style UI. Manages several PlatformGamesFragments, which * individually display a grid of available games for each Fragment, in a tabbed layout. */ -public final class MainActivity extends AppCompatActivity implements MainView -{ - private Toolbar mToolbar; - private int mFrameLayoutId; - private PlatformGamesFragment mPlatformGamesFragment; - private FloatingActionButton mFab; +public final class MainActivity extends AppCompatActivity implements MainView { + private Toolbar mToolbar; + private int mFrameLayoutId; + private PlatformGamesFragment mPlatformGamesFragment; + private FloatingActionButton mFab; - private MainPresenter mPresenter = new MainPresenter(this); + private MainPresenter mPresenter = new MainPresenter(this); - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); - findViews(); + findViews(); - setSupportActionBar(mToolbar); + setSupportActionBar(mToolbar); - mFrameLayoutId = R.id.games_platform_frame; + mFrameLayoutId = R.id.games_platform_frame; - // Set up the FAB. - mFab.setOnClickListener(view -> mPresenter.onFabClick()); + // Set up the FAB. + mFab.setOnClickListener(view -> mPresenter.onFabClick()); - mPresenter.onCreate(); + mPresenter.onCreate(); - // Stuff in this block only happens when this activity is newly created (i.e. not a rotation) - if (savedInstanceState == null) - StartupHandler.HandleInit(this); + // Stuff in this block only happens when this activity is newly created (i.e. not a rotation) + if (savedInstanceState == null) + StartupHandler.HandleInit(this); - if (PermissionsHandler.hasWriteAccess(this)) - { - mPlatformGamesFragment = new PlatformGamesFragment(); - getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment) - .commit(); - } - } - - @Override - protected void onResume() - { - super.onResume(); - mPresenter.addDirIfNeeded(new AddDirectoryHelper(this)); - } - - // TODO: Replace with a ButterKnife injection. - private void findViews() - { - mToolbar = findViewById(R.id.toolbar_main); - mFab = findViewById(R.id.button_add_directory); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_game_grid, menu); - return true; - } - - /** - * MainView - */ - - @Override - public void setVersionString(String version) - { - mToolbar.setSubtitle(version); - } - - @Override - public void refresh() - { - getContentResolver().insert(GameProvider.URI_REFRESH, null); - refreshFragment(); - } - - @Override - public void refreshFragmentScreenshot(int fragmentPosition) - { - // Invalidate Picasso image so that the new screenshot is animated in. - PlatformGamesView fragment = getPlatformGamesView(); - - if (fragment != null) - { - fragment.refreshScreenshotAtPosition(fragmentPosition); - } - } - - @Override - public void launchSettingsActivity(String menuTag) - { - SettingsActivity.launch(this, menuTag, ""); - } - - @Override - public void launchFileListActivity() - { - FileBrowserHelper.openDirectoryPicker(this); - } - - @Override - public void showGames(Cursor games) - { - // no-op. Handled by PlatformGamesFragment. - } - - /** - * @param requestCode An int describing whether the Activity that is returning did so successfully. - * @param resultCode An int describing what Activity is giving us this callback. - * @param result The information the returning Activity is providing us. - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent result) - { - switch (requestCode) - { - case MainPresenter.REQUEST_ADD_DIRECTORY: - // If the user picked a file, as opposed to just backing out. - if (resultCode == MainActivity.RESULT_OK) - { - mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); + if (PermissionsHandler.hasWriteAccess(this)) { + mPlatformGamesFragment = new PlatformGamesFragment(); + getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment) + .commit(); } - break; - - case MainPresenter.REQUEST_EMULATE_GAME: - mPresenter.refreshFragmentScreenshot(resultCode); - break; } - } - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) - { - switch (requestCode) - { - case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) - { - DirectoryInitializationService.startService(this); + @Override + protected void onResume() { + super.onResume(); + mPresenter.addDirIfNeeded(new AddDirectoryHelper(this)); + } - mPlatformGamesFragment = new PlatformGamesFragment(); - getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment) - .commit(); + // TODO: Replace with a ButterKnife injection. + private void findViews() { + mToolbar = findViewById(R.id.toolbar_main); + mFab = findViewById(R.id.button_add_directory); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_game_grid, menu); + return true; + } + + /** + * MainView + */ + + @Override + public void setVersionString(String version) { + mToolbar.setSubtitle(version); + } + + @Override + public void refresh() { + getContentResolver().insert(GameProvider.URI_REFRESH, null); + refreshFragment(); + } + + @Override + public void refreshFragmentScreenshot(int fragmentPosition) { + // Invalidate Picasso image so that the new screenshot is animated in. + PlatformGamesView fragment = getPlatformGamesView(); + + if (fragment != null) { + fragment.refreshScreenshotAtPosition(fragmentPosition); } - else - { - Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) - .show(); + } + + @Override + public void launchSettingsActivity(String menuTag) { + SettingsActivity.launch(this, menuTag, ""); + } + + @Override + public void launchFileListActivity() { + FileBrowserHelper.openDirectoryPicker(this); + } + + @Override + public void showGames(Cursor games) { + // no-op. Handled by PlatformGamesFragment. + } + + /** + * @param requestCode An int describing whether the Activity that is returning did so successfully. + * @param resultCode An int describing what Activity is giving us this callback. + * @param result The information the returning Activity is providing us. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent result) { + switch (requestCode) { + case MainPresenter.REQUEST_ADD_DIRECTORY: + // If the user picked a file, as opposed to just backing out. + if (resultCode == MainActivity.RESULT_OK) { + mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); + } + break; + + case MainPresenter.REQUEST_EMULATE_GAME: + mPresenter.refreshFragmentScreenshot(resultCode); + break; } - break; - default: - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - break; } - } - /** - * Called by the framework whenever any actionbar/toolbar icon is clicked. - * - * @param item The icon that was clicked on. - * @return True if the event was handled, false to bubble it up to the OS. - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - return mPresenter.handleOptionSelection(item.getItemId()); - } + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + switch (requestCode) { + case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + DirectoryInitializationService.startService(this); - private void refreshFragment() - { - if (mPlatformGamesFragment != null) - { - mPlatformGamesFragment.refresh(); + mPlatformGamesFragment = new PlatformGamesFragment(); + getSupportFragmentManager().beginTransaction().add(mFrameLayoutId, mPlatformGamesFragment) + .commit(); + } else { + Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show(); + } + break; + default: + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + break; + } } - } - @Nullable - private PlatformGamesView getPlatformGamesView() - { - return (PlatformGamesView) getSupportFragmentManager().findFragmentById(mFrameLayoutId); - } + /** + * Called by the framework whenever any actionbar/toolbar icon is clicked. + * + * @param item The icon that was clicked on. + * @return True if the event was handled, false to bubble it up to the OS. + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return mPresenter.handleOptionSelection(item.getItemId()); + } + + private void refreshFragment() { + if (mPlatformGamesFragment != null) { + mPlatformGamesFragment.refresh(); + } + } + + @Nullable + private PlatformGamesView getPlatformGamesView() { + return (PlatformGamesView) getSupportFragmentManager().findFragmentById(mFrameLayoutId); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainPresenter.java index ed9419afd..80256481a 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainPresenter.java @@ -11,80 +11,69 @@ import org.citra.citra_android.utils.SettingsFile; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; -public final class MainPresenter -{ - public static final int REQUEST_ADD_DIRECTORY = 1; - public static final int REQUEST_EMULATE_GAME = 2; +public final class MainPresenter { + public static final int REQUEST_ADD_DIRECTORY = 1; + public static final int REQUEST_EMULATE_GAME = 2; - private final MainView mView; - private String mDirToAdd; + private final MainView mView; + private String mDirToAdd; - public MainPresenter(MainView view) - { - mView = view; - } + public MainPresenter(MainView view) { + mView = view; + } - public void onCreate() - { - String versionName = BuildConfig.VERSION_NAME; - mView.setVersionString(versionName); - } + public void onCreate() { + String versionName = BuildConfig.VERSION_NAME; + mView.setVersionString(versionName); + } - public void onFabClick() - { - mView.launchFileListActivity(); - } - - public boolean handleOptionSelection(int itemId) - { - switch (itemId) - { - case R.id.menu_settings_core: - mView.launchSettingsActivity(SettingsFile.FILE_NAME_CONFIG); - return true; - - case R.id.menu_refresh: - GameDatabase databaseHelper = DolphinApplication.databaseHelper; - databaseHelper.scanLibrary(databaseHelper.getWritableDatabase()); - mView.refresh(); - return true; - - case R.id.button_add_directory: + public void onFabClick() { mView.launchFileListActivity(); - return true; } - return false; - } + public boolean handleOptionSelection(int itemId) { + switch (itemId) { + case R.id.menu_settings_core: + mView.launchSettingsActivity(SettingsFile.FILE_NAME_CONFIG); + return true; - public void addDirIfNeeded(AddDirectoryHelper helper) - { - if (mDirToAdd != null) - { - helper.addDirectory(mDirToAdd, mView::refresh); + case R.id.menu_refresh: + GameDatabase databaseHelper = DolphinApplication.databaseHelper; + databaseHelper.scanLibrary(databaseHelper.getWritableDatabase()); + mView.refresh(); + return true; - mDirToAdd = null; + case R.id.button_add_directory: + mView.launchFileListActivity(); + return true; + } + + return false; } - } - public void onDirectorySelected(String dir) - { - mDirToAdd = dir; - } + public void addDirIfNeeded(AddDirectoryHelper helper) { + if (mDirToAdd != null) { + helper.addDirectory(mDirToAdd, mView::refresh); - public void refreshFragmentScreenshot(int resultCode) - { - mView.refreshFragmentScreenshot(resultCode); - } + mDirToAdd = null; + } + } + + public void onDirectorySelected(String dir) { + mDirToAdd = dir; + } + + public void refreshFragmentScreenshot(int resultCode) { + mView.refreshFragmentScreenshot(resultCode); + } - public void loadGames() - { - GameDatabase databaseHelper = DolphinApplication.databaseHelper; + public void loadGames() { + GameDatabase databaseHelper = DolphinApplication.databaseHelper; - databaseHelper.getGames() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(games -> mView.showGames(games)); - } + databaseHelper.getGames() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(games -> mView.showGames(games)); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainView.java index 7a2c3338a..01858d318 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainView.java @@ -7,39 +7,38 @@ import android.database.Cursor; * Implementations will differ primarily to target touch-screen * or non-touch screen devices. */ -public interface MainView -{ - /** - * Pass the view the native library's version string. Displaying - * it is optional. - * - * @param version A string pulled from native code. - */ - void setVersionString(String version); +public interface MainView { + /** + * Pass the view the native library's version string. Displaying + * it is optional. + * + * @param version A string pulled from native code. + */ + void setVersionString(String version); - /** - * Tell the view to refresh its contents. - */ - void refresh(); + /** + * Tell the view to refresh its contents. + */ + void refresh(); - /** - * Tell the view to tell the currently displayed {@link android.support.v4.app.Fragment} - * to refresh the screenshot at the given position in its list of games. - * - * @param fragmentPosition An index corresponding to the list or grid of games. - */ - void refreshFragmentScreenshot(int fragmentPosition); + /** + * Tell the view to tell the currently displayed {@link android.support.v4.app.Fragment} + * to refresh the screenshot at the given position in its list of games. + * + * @param fragmentPosition An index corresponding to the list or grid of games. + */ + void refreshFragmentScreenshot(int fragmentPosition); - void launchSettingsActivity(String menuTag); + void launchSettingsActivity(String menuTag); - void launchFileListActivity(); + void launchFileListActivity(); - /** - * To be called when an asynchronous database read completes. Passes the - * result, in this case a {@link Cursor} to the view. - * - * @param games A Cursor containing the games read from the database. - */ - void showGames(Cursor games); + /** + * To be called when an asynchronous database read completes. Passes the + * result, in this case a {@link Cursor} to the view. + * + * @param games A Cursor containing the games read from the database. + */ + void showGames(Cursor games); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/main/TvMainActivity.java b/src/android/app/src/main/java/org/citra/citra_android/ui/main/TvMainActivity.java index 4065ffe42..6c59c58d9 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/main/TvMainActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/main/TvMainActivity.java @@ -31,239 +31,208 @@ import org.citra.citra_android.utils.PermissionsHandler; import org.citra.citra_android.utils.StartupHandler; import org.citra.citra_android.viewholders.TvGameViewHolder; -public final class TvMainActivity extends FragmentActivity implements MainView -{ - private MainPresenter mPresenter = new MainPresenter(this); +public final class TvMainActivity extends FragmentActivity implements MainView { + private MainPresenter mPresenter = new MainPresenter(this); - private BrowseSupportFragment mBrowseFragment; + private BrowseSupportFragment mBrowseFragment; - private ArrayObjectAdapter mRowsAdapter; + private ArrayObjectAdapter mRowsAdapter; - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_tv_main); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_tv_main); - setupUI(); + setupUI(); - mPresenter.onCreate(); + mPresenter.onCreate(); - // Stuff in this block only happens when this activity is newly created (i.e. not a rotation) - if (savedInstanceState == null) - StartupHandler.HandleInit(this); - } - - @Override - protected void onResume() - { - super.onResume(); - mPresenter.addDirIfNeeded(new AddDirectoryHelper(this)); - } - - void setupUI() - { - final FragmentManager fragmentManager = getSupportFragmentManager(); - mBrowseFragment = new BrowseSupportFragment(); - fragmentManager - .beginTransaction() - .add(R.id.content, mBrowseFragment, "BrowseFragment") - .commit(); - - // Set display parameters for the BrowseFragment - mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED); - mBrowseFragment.setBrandColor(ContextCompat.getColor(this, R.color.citra_orange_dark)); - buildRowsAdapter(); - - mBrowseFragment.setOnItemViewClickedListener( - (itemViewHolder, item, rowViewHolder, row) -> - { - // Special case: user clicked on a settings row item. - if (item instanceof TvSettingsItem) - { - TvSettingsItem settingsItem = (TvSettingsItem) item; - mPresenter.handleOptionSelection(settingsItem.getItemId()); - } - else - { - TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; - - // Start the emulation activity and send the path of the clicked ISO to it. - EmulationActivity.launch(TvMainActivity.this, - holder.path, - holder.title, - holder.screenshotPath, - -1, - holder.imageScreenshot); - } - }); - } - - /** - * MainView - */ - - @Override - public void setVersionString(String version) - { - mBrowseFragment.setTitle(version); - } - - @Override - public void refresh() - { - recreate(); - } - - @Override - public void refreshFragmentScreenshot(int fragmentPosition) - { - mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size()); - } - - @Override - public void launchSettingsActivity(String menuTag) - { - SettingsActivity.launch(this, menuTag, ""); - } - - @Override - public void launchFileListActivity() - { - FileBrowserHelper.openDirectoryPicker(this); - } - - @Override - public void showGames(Cursor games) - { - ListRow row = buildGamesRow(games); - - // Add row to the adapter only if it is not empty. - if (row != null) - { - mRowsAdapter.add(games); + // Stuff in this block only happens when this activity is newly created (i.e. not a rotation) + if (savedInstanceState == null) + StartupHandler.HandleInit(this); } - } - /** - * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. - * - * @param requestCode An int describing whether the Activity that is returning did so successfully. - * @param resultCode An int describing what Activity is giving us this callback. - * @param result The information the returning Activity is providing us. - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent result) - { - switch (requestCode) - { - case MainPresenter.REQUEST_ADD_DIRECTORY: - // If the user picked a file, as opposed to just backing out. - if (resultCode == MainActivity.RESULT_OK) - { - mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); + @Override + protected void onResume() { + super.onResume(); + mPresenter.addDirIfNeeded(new AddDirectoryHelper(this)); + } + + void setupUI() { + final FragmentManager fragmentManager = getSupportFragmentManager(); + mBrowseFragment = new BrowseSupportFragment(); + fragmentManager + .beginTransaction() + .add(R.id.content, mBrowseFragment, "BrowseFragment") + .commit(); + + // Set display parameters for the BrowseFragment + mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED); + mBrowseFragment.setBrandColor(ContextCompat.getColor(this, R.color.citra_orange_dark)); + buildRowsAdapter(); + + mBrowseFragment.setOnItemViewClickedListener( + (itemViewHolder, item, rowViewHolder, row) -> + { + // Special case: user clicked on a settings row item. + if (item instanceof TvSettingsItem) { + TvSettingsItem settingsItem = (TvSettingsItem) item; + mPresenter.handleOptionSelection(settingsItem.getItemId()); + } else { + TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; + + // Start the emulation activity and send the path of the clicked ISO to it. + EmulationActivity.launch(TvMainActivity.this, + holder.path, + holder.title, + holder.screenshotPath, + -1, + holder.imageScreenshot); + } + }); + } + + /** + * MainView + */ + + @Override + public void setVersionString(String version) { + mBrowseFragment.setTitle(version); + } + + @Override + public void refresh() { + recreate(); + } + + @Override + public void refreshFragmentScreenshot(int fragmentPosition) { + mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size()); + } + + @Override + public void launchSettingsActivity(String menuTag) { + SettingsActivity.launch(this, menuTag, ""); + } + + @Override + public void launchFileListActivity() { + FileBrowserHelper.openDirectoryPicker(this); + } + + @Override + public void showGames(Cursor games) { + ListRow row = buildGamesRow(games); + + // Add row to the adapter only if it is not empty. + if (row != null) { + mRowsAdapter.add(games); } - break; - - case MainPresenter.REQUEST_EMULATE_GAME: - mPresenter.refreshFragmentScreenshot(resultCode); - break; } - } - @Override - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) - { - switch (requestCode) - { - case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) - { - DirectoryInitializationService.startService(this); - loadGames(); + /** + * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity. + * + * @param requestCode An int describing whether the Activity that is returning did so successfully. + * @param resultCode An int describing what Activity is giving us this callback. + * @param result The information the returning Activity is providing us. + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent result) { + switch (requestCode) { + case MainPresenter.REQUEST_ADD_DIRECTORY: + // If the user picked a file, as opposed to just backing out. + if (resultCode == MainActivity.RESULT_OK) { + mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); + } + break; + + case MainPresenter.REQUEST_EMULATE_GAME: + mPresenter.refreshFragmentScreenshot(resultCode); + break; } - else - { - Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) - .show(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + switch (requestCode) { + case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { + DirectoryInitializationService.startService(this); + loadGames(); + } else { + Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show(); + } + break; + default: + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + break; } - break; - default: - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - break; - } - } - - private void buildRowsAdapter() - { - mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); - - if (PermissionsHandler.hasWriteAccess(this)) - { - loadGames(); } - mRowsAdapter.add(buildSettingsRow()); + private void buildRowsAdapter() { + mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); - mBrowseFragment.setAdapter(mRowsAdapter); - } + if (PermissionsHandler.hasWriteAccess(this)) { + loadGames(); + } - private void loadGames() - { - mPresenter.loadGames(); - } + mRowsAdapter.add(buildSettingsRow()); - private ListRow buildGamesRow(Cursor games) - { - // Create an adapter for this row. - CursorObjectAdapter row = new CursorObjectAdapter(new GameRowPresenter()); - - // If cursor is empty, don't return a Row. - if (!games.moveToFirst()) - { - return null; + mBrowseFragment.setAdapter(mRowsAdapter); } - row.changeCursor(games); - row.setMapper(new CursorMapper() - { - @Override - protected void bindColumns(Cursor cursor) - { - // No-op? Not sure what this does. - } + private void loadGames() { + mPresenter.loadGames(); + } - @Override - protected Object bind(Cursor cursor) - { - return Game.fromCursor(cursor); - } - }); + private ListRow buildGamesRow(Cursor games) { + // Create an adapter for this row. + CursorObjectAdapter row = new CursorObjectAdapter(new GameRowPresenter()); - // Create the row, passing it the filled adapter and the header, and give it to the master adapter. - return new ListRow(null, row); - } + // If cursor is empty, don't return a Row. + if (!games.moveToFirst()) { + return null; + } - private ListRow buildSettingsRow() - { - ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter()); + row.changeCursor(games); + row.setMapper(new CursorMapper() { + @Override + protected void bindColumns(Cursor cursor) { + // No-op? Not sure what this does. + } - rowItems.add(new TvSettingsItem(R.id.menu_settings_core, - R.drawable.ic_settings_core_tv, - R.string.grid_menu_core_settings)); + @Override + protected Object bind(Cursor cursor) { + return Game.fromCursor(cursor); + } + }); - rowItems.add(new TvSettingsItem(R.id.button_add_directory, - R.drawable.ic_add_tv, - R.string.add_directory_title)); + // Create the row, passing it the filled adapter and the header, and give it to the master adapter. + return new ListRow(null, row); + } - rowItems.add(new TvSettingsItem(R.id.menu_refresh, - R.drawable.ic_refresh_tv, - R.string.grid_menu_refresh)); + private ListRow buildSettingsRow() { + ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter()); - // Create a header for this row. - HeaderItem header = - new HeaderItem(R.string.preferences_settings, getString(R.string.preferences_settings)); + rowItems.add(new TvSettingsItem(R.id.menu_settings_core, + R.drawable.ic_settings_core_tv, + R.string.grid_menu_core_settings)); - return new ListRow(header, rowItems); - } + rowItems.add(new TvSettingsItem(R.id.button_add_directory, + R.drawable.ic_add_tv, + R.string.add_directory_title)); + + rowItems.add(new TvSettingsItem(R.id.menu_refresh, + R.drawable.ic_refresh_tv, + R.string.grid_menu_refresh)); + + // Create a header for this row. + HeaderItem header = + new HeaderItem(R.string.preferences_settings, getString(R.string.preferences_settings)); + + return new ListRow(header, rowItems); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesFragment.java b/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesFragment.java index e87012130..c7e210c9f 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesFragment.java @@ -13,76 +13,66 @@ import android.view.ViewGroup; import org.citra.citra_android.R; import org.citra.citra_android.adapters.GameAdapter; -public final class PlatformGamesFragment extends Fragment implements PlatformGamesView -{ - private static final String ARG_PLATFORM = "platform"; +public final class PlatformGamesFragment extends Fragment implements PlatformGamesView { + private static final String ARG_PLATFORM = "platform"; - private PlatformGamesPresenter mPresenter = new PlatformGamesPresenter(this); + private PlatformGamesPresenter mPresenter = new PlatformGamesPresenter(this); - private GameAdapter mAdapter; - private RecyclerView mRecyclerView; + private GameAdapter mAdapter; + private RecyclerView mRecyclerView; - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); - } - - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) - { - View rootView = inflater.inflate(R.layout.fragment_grid, container, false); - - findViews(rootView); - - mPresenter.onCreateView(); - - return rootView; - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) - { - int columns = getResources().getInteger(R.integer.game_grid_columns); - RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns); - mAdapter = new GameAdapter(); - - mRecyclerView.setLayoutManager(layoutManager); - mRecyclerView.setAdapter(mAdapter); - - mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); - } - - @Override - public void refreshScreenshotAtPosition(int position) - { - mAdapter.notifyItemChanged(position); - } - - @Override - public void refresh() - { - mPresenter.refresh(); - } - - @Override - public void onItemClick(String gameId) - { - // No-op for now - } - - @Override - public void showGames(Cursor games) - { - if (mAdapter != null) - { - mAdapter.swapCursor(games); + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); } - } - private void findViews(View root) - { - mRecyclerView = root.findViewById(R.id.grid_games); - } + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_grid, container, false); + + findViews(rootView); + + mPresenter.onCreateView(); + + return rootView; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + int columns = getResources().getInteger(R.integer.game_grid_columns); + RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns); + mAdapter = new GameAdapter(); + + mRecyclerView.setLayoutManager(layoutManager); + mRecyclerView.setAdapter(mAdapter); + + mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); + } + + @Override + public void refreshScreenshotAtPosition(int position) { + mAdapter.notifyItemChanged(position); + } + + @Override + public void refresh() { + mPresenter.refresh(); + } + + @Override + public void onItemClick(String gameId) { + // No-op for now + } + + @Override + public void showGames(Cursor games) { + if (mAdapter != null) { + mAdapter.swapCursor(games); + } + } + + private void findViews(View root) { + mRecyclerView = root.findViewById(R.id.grid_games); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesPresenter.java index 513717ba5..6addc7d9e 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesPresenter.java @@ -8,40 +8,35 @@ import org.citra.citra_android.utils.Log; import rx.android.schedulers.AndroidSchedulers; import rx.schedulers.Schedulers; -public final class PlatformGamesPresenter -{ - private final PlatformGamesView mView; +public final class PlatformGamesPresenter { + private final PlatformGamesView mView; - public PlatformGamesPresenter(PlatformGamesView view) - { - mView = view; - } + public PlatformGamesPresenter(PlatformGamesView view) { + mView = view; + } - public void onCreateView() - { - loadGames(); - } + public void onCreateView() { + loadGames(); + } - public void refresh() - { - Log.debug("[PlatformGamesPresenter] : Refreshing..."); - loadGames(); - } + public void refresh() { + Log.debug("[PlatformGamesPresenter] : Refreshing..."); + loadGames(); + } - private void loadGames() - { - Log.debug("[PlatformGamesPresenter] : Loading games..."); + private void loadGames() { + Log.debug("[PlatformGamesPresenter] : Loading games..."); - GameDatabase databaseHelper = DolphinApplication.databaseHelper; + GameDatabase databaseHelper = DolphinApplication.databaseHelper; - databaseHelper.getGames() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(games -> - { - Log.debug("[PlatformGamesPresenter] : Load finished, swapping cursor..."); + databaseHelper.getGames() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(games -> + { + Log.debug("[PlatformGamesPresenter] : Load finished, swapping cursor..."); - mView.showGames(games); - }); - } + mView.showGames(games); + }); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesView.java index 6551b5af2..339bfb7a3 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/platform/PlatformGamesView.java @@ -5,34 +5,33 @@ import android.database.Cursor; /** * Abstraction for a screen representing a single platform's games. */ -public interface PlatformGamesView -{ - /** - * Tell the view to refresh its contents. - */ - void refresh(); +public interface PlatformGamesView { + /** + * Tell the view to refresh its contents. + */ + void refresh(); - /** - * Tell the view that a certain game's screenshot has been updated, - * and should be redrawn on-screen. - * - * @param position The index of the game that should be redrawn. - */ - void refreshScreenshotAtPosition(int position); + /** + * Tell the view that a certain game's screenshot has been updated, + * and should be redrawn on-screen. + * + * @param position The index of the game that should be redrawn. + */ + void refreshScreenshotAtPosition(int position); - /** - * Pass a click event to the view's Presenter. Typically called from the - * view's list adapter. - * - * @param gameId The ID of the game that was clicked. - */ - void onItemClick(String gameId); + /** + * Pass a click event to the view's Presenter. Typically called from the + * view's list adapter. + * + * @param gameId The ID of the game that was clicked. + */ + void onItemClick(String gameId); - /** - * To be called when an asynchronous database read completes. Passes the - * result, in this case a {@link Cursor}, to the view. - * - * @param games A Cursor containing the games read from the database. - */ - void showGames(Cursor games); + /** + * To be called when an asynchronous database read completes. Passes the + * result, in this case a {@link Cursor}, to the view. + * + * @param games A Cursor containing the games read from the database. + */ + void showGames(Cursor games); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivity.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivity.java index 1a18ebc7c..8fe4a83f7 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivity.java @@ -18,249 +18,215 @@ import org.citra.citra_android.R; import org.citra.citra_android.model.settings.SettingSection; import org.citra.citra_android.services.DirectoryInitializationService; import org.citra.citra_android.utils.DirectoryStateReceiver; -import org.citra.citra_android.utils.Log; import java.util.ArrayList; import java.util.HashMap; -public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView -{ - private static final String ARG_FILE_NAME = "file_name"; - private static final String ARG_GAME_ID = "game_id"; +public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView { + private static final String ARG_FILE_NAME = "file_name"; + private static final String ARG_GAME_ID = "game_id"; - private static final String FRAGMENT_TAG = "settings"; - private SettingsActivityPresenter mPresenter = new SettingsActivityPresenter(this); + private static final String FRAGMENT_TAG = "settings"; + private SettingsActivityPresenter mPresenter = new SettingsActivityPresenter(this); - private ProgressDialog dialog; + private ProgressDialog dialog; - public static void launch(Context context, String menuTag, String gameId) - { - Intent settings = new Intent(context, SettingsActivity.class); - settings.putExtra(ARG_FILE_NAME, menuTag); - settings.putExtra(ARG_GAME_ID, gameId); + public static void launch(Context context, String menuTag, String gameId) { + Intent settings = new Intent(context, SettingsActivity.class); + settings.putExtra(ARG_FILE_NAME, menuTag); + settings.putExtra(ARG_GAME_ID, gameId); - context.startActivity(settings); - } + context.startActivity(settings); + } - @Override - protected void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - setContentView(R.layout.activity_settings); + setContentView(R.layout.activity_settings); - Intent launcher = getIntent(); - String filename = launcher.getStringExtra(ARG_FILE_NAME); - String gameID = launcher.getStringExtra(ARG_GAME_ID); + Intent launcher = getIntent(); + String filename = launcher.getStringExtra(ARG_FILE_NAME); + String gameID = launcher.getStringExtra(ARG_GAME_ID); - mPresenter.onCreate(savedInstanceState, filename, gameID); - } + mPresenter.onCreate(savedInstanceState, filename, gameID); + } - @Override - public boolean onCreateOptionsMenu(Menu menu) - { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_settings, menu); + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_settings, menu); - return true; - } + return true; + } - @Override - public boolean onOptionsItemSelected(MenuItem item) - { - return mPresenter.handleOptionsItem(item.getItemId()); - } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return mPresenter.handleOptionsItem(item.getItemId()); + } - @Override - protected void onSaveInstanceState(Bundle outState) - { - // Critical: If super method is not called, rotations will be busted. - super.onSaveInstanceState(outState); - mPresenter.saveState(outState); - } + @Override + protected void onSaveInstanceState(Bundle outState) { + // Critical: If super method is not called, rotations will be busted. + super.onSaveInstanceState(outState); + mPresenter.saveState(outState); + } - @Override - protected void onStart() - { - super.onStart(); - mPresenter.onStart(); - } + @Override + protected void onStart() { + super.onStart(); + mPresenter.onStart(); + } - /** - * If this is called, the user has left the settings screen (potentially through the - * home button) and will expect their changes to be persisted. So we kick off an - * IntentService which will do so on a background thread. - */ - @Override - protected void onStop() - { - super.onStop(); + /** + * If this is called, the user has left the settings screen (potentially through the + * home button) and will expect their changes to be persisted. So we kick off an + * IntentService which will do so on a background thread. + */ + @Override + protected void onStop() { + super.onStop(); - mPresenter.onStop(isFinishing()); - } + mPresenter.onStop(isFinishing()); + } - @Override - public void onBackPressed() - { - mPresenter.onBackPressed(); - } + @Override + public void onBackPressed() { + mPresenter.onBackPressed(); + } - @Override - public void showSettingsFragment(String menuTag, boolean addToStack, String gameID) - { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + @Override + public void showSettingsFragment(String menuTag, boolean addToStack, String gameID) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - if (addToStack) - { - if (areSystemAnimationsEnabled()) - { - transaction.setCustomAnimations( - R.animator.settings_enter, - R.animator.settings_exit, - R.animator.settings_pop_enter, - R.animator.setttings_pop_exit); - } + if (addToStack) { + if (areSystemAnimationsEnabled()) { + transaction.setCustomAnimations( + R.animator.settings_enter, + R.animator.settings_exit, + R.animator.settings_pop_enter, + R.animator.setttings_pop_exit); + } - transaction.addToBackStack(null); - mPresenter.addToStack(); - } - transaction.replace(R.id.frame_content, SettingsFragment.newInstance(menuTag, gameID), FRAGMENT_TAG); + transaction.addToBackStack(null); + mPresenter.addToStack(); + } + transaction.replace(R.id.frame_content, SettingsFragment.newInstance(menuTag, gameID), FRAGMENT_TAG); - transaction.commit(); - } + transaction.commit(); + } - private boolean areSystemAnimationsEnabled() - { - float duration = Settings.Global.getFloat( - getContentResolver(), - Settings.Global.ANIMATOR_DURATION_SCALE, 1); - float transition = Settings.Global.getFloat( - getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE, 1); - return duration != 0 && transition != 0; - } + private boolean areSystemAnimationsEnabled() { + float duration = Settings.Global.getFloat( + getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE, 1); + float transition = Settings.Global.getFloat( + getContentResolver(), + Settings.Global.TRANSITION_ANIMATION_SCALE, 1); + return duration != 0 && transition != 0; + } - @Override - public void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter) - { - LocalBroadcastManager.getInstance(this).registerReceiver( - receiver, - filter); - DirectoryInitializationService.startService(this); - } + @Override + public void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter) { + LocalBroadcastManager.getInstance(this).registerReceiver( + receiver, + filter); + DirectoryInitializationService.startService(this); + } - @Override - public void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver) - { - LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); - } + @Override + public void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver) { + LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); + } - @Override - public void showLoading() - { - if (dialog == null) - { - dialog = new ProgressDialog(this); - dialog.setMessage(getString(R.string.load_settings)); - dialog.setIndeterminate(true); - } + @Override + public void showLoading() { + if (dialog == null) { + dialog = new ProgressDialog(this); + dialog.setMessage(getString(R.string.load_settings)); + dialog.setIndeterminate(true); + } - dialog.show(); - } + dialog.show(); + } - @Override - public void hideLoading() - { - dialog.dismiss(); - } + @Override + public void hideLoading() { + dialog.dismiss(); + } - @Override - public void showPermissionNeededHint() - { - Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) - .show(); - } + @Override + public void showPermissionNeededHint() { + Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show(); + } - @Override - public void showExternalStorageNotMountedHint() - { - Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT) - .show(); - } + @Override + public void showExternalStorageNotMountedHint() { + Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT) + .show(); + } - @Override - public HashMap getSettings(int file) - { - return mPresenter.getSettings(file); - } + @Override + public HashMap getSettings(int file) { + return mPresenter.getSettings(file); + } - @Override - public void setSettings(ArrayList> settings) - { - mPresenter.setSettings(settings); - } + @Override + public void setSettings(ArrayList> settings) { + mPresenter.setSettings(settings); + } - @Override - public void onSettingsFileLoaded(ArrayList> settings) - { - SettingsFragmentView fragment = getFragment(); + @Override + public void onSettingsFileLoaded(ArrayList> settings) { + SettingsFragmentView fragment = getFragment(); - if (fragment != null) - { - fragment.onSettingsFileLoaded(settings); - } - } + if (fragment != null) { + fragment.onSettingsFileLoaded(settings); + } + } - @Override - public void onSettingsFileNotFound() - { - SettingsFragmentView fragment = getFragment(); + @Override + public void onSettingsFileNotFound() { + SettingsFragmentView fragment = getFragment(); - if (fragment != null) - { - fragment.loadDefaultSettings(); - } - } + if (fragment != null) { + fragment.loadDefaultSettings(); + } + } - @Override - public void showToastMessage(String message) - { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } + @Override + public void showToastMessage(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } - @Override - public void popBackStack() - { - getSupportFragmentManager().popBackStackImmediate(); - } + @Override + public void popBackStack() { + getSupportFragmentManager().popBackStackImmediate(); + } - @Override - public void onSettingChanged() - { - mPresenter.onSettingChanged(); - } + @Override + public void onSettingChanged() { + mPresenter.onSettingChanged(); + } - @Override - public void onGcPadSettingChanged(String key, int value) - { - mPresenter.onGcPadSettingChanged(key, value); - } + @Override + public void onGcPadSettingChanged(String key, int value) { + mPresenter.onGcPadSettingChanged(key, value); + } - @Override - public void onWiimoteSettingChanged(String section, int value) - { - mPresenter.onWiimoteSettingChanged(section, value); - } + @Override + public void onWiimoteSettingChanged(String section, int value) { + mPresenter.onWiimoteSettingChanged(section, value); + } - @Override - public void onExtensionSettingChanged(String key, int value) - { - mPresenter.onExtensionSettingChanged(key, value); - } + @Override + public void onExtensionSettingChanged(String key, int value) { + mPresenter.onExtensionSettingChanged(key, value); + } - private SettingsFragment getFragment() - { - return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); - } + private SettingsFragment getFragment() { + return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityPresenter.java index ce96ef7de..840dd361b 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityPresenter.java @@ -4,7 +4,6 @@ import android.content.IntentFilter; import android.os.Bundle; import android.text.TextUtils; -import org.citra.citra_android.NativeLibrary; import org.citra.citra_android.R; import org.citra.citra_android.model.settings.SettingSection; import org.citra.citra_android.services.DirectoryInitializationService; @@ -17,204 +16,167 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; -public final class SettingsActivityPresenter -{ - private static final String KEY_SHOULD_SAVE = "should_save"; +public final class SettingsActivityPresenter { + private static final String KEY_SHOULD_SAVE = "should_save"; - private SettingsActivityView mView; + private SettingsActivityView mView; - private ArrayList> mSettings = new ArrayList<>(); + private ArrayList> mSettings = new ArrayList<>(); - private int mStackCount; + private int mStackCount; - private boolean mShouldSave; + private boolean mShouldSave; - private DirectoryStateReceiver directoryStateReceiver; + private DirectoryStateReceiver directoryStateReceiver; - private String menuTag; - private String gameId; + private String menuTag; + private String gameId; - public SettingsActivityPresenter(SettingsActivityView view) - { - mView = view; - } + public SettingsActivityPresenter(SettingsActivityView view) { + mView = view; + } - public void onCreate(Bundle savedInstanceState, String menuTag, String gameId) - { - if (savedInstanceState == null) - { - this.menuTag = menuTag; - this.gameId = gameId; - } - else - { - mShouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE); - } - } + public void onCreate(Bundle savedInstanceState, String menuTag, String gameId) { + if (savedInstanceState == null) { + this.menuTag = menuTag; + this.gameId = gameId; + } else { + mShouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE); + } + } - public void onStart() - { - prepareDolphinDirectoriesIfNeeded(); - } + public void onStart() { + prepareDolphinDirectoriesIfNeeded(); + } - void loadSettingsUI() - { - if (mSettings.isEmpty()) - { - if (!TextUtils.isEmpty(gameId)) - { - mSettings.add(SettingsFile.SETTINGS_DOLPHIN, SettingsFile.readFile("../GameSettings/" + gameId, mView)); - } - else - { - mSettings.add(SettingsFile.SETTINGS_DOLPHIN, SettingsFile.readFile(SettingsFile.FILE_NAME_CONFIG, mView)); - } - } + void loadSettingsUI() { + if (mSettings.isEmpty()) { + if (!TextUtils.isEmpty(gameId)) { + mSettings.add(SettingsFile.SETTINGS_DOLPHIN, SettingsFile.readFile("../GameSettings/" + gameId, mView)); + } else { + mSettings.add(SettingsFile.SETTINGS_DOLPHIN, SettingsFile.readFile(SettingsFile.FILE_NAME_CONFIG, mView)); + } + } - mView.showSettingsFragment(menuTag, false, gameId); - mView.onSettingsFileLoaded(mSettings); - } + mView.showSettingsFragment(menuTag, false, gameId); + mView.onSettingsFileLoaded(mSettings); + } - private void prepareDolphinDirectoriesIfNeeded() - { - File configFile = new File(DirectoryInitializationService.getUserDirectory() + "/config/"+SettingsFile.FILE_NAME_CONFIG + ".ini"); - if(!configFile.exists()) { + private void prepareDolphinDirectoriesIfNeeded() { + File configFile = new File(DirectoryInitializationService.getUserDirectory() + "/config/" + SettingsFile.FILE_NAME_CONFIG + ".ini"); + if (!configFile.exists()) { - } - if (DirectoryInitializationService.areDolphinDirectoriesReady()) { - loadSettingsUI(); - } else { - mView.showLoading(); - IntentFilter statusIntentFilter = new IntentFilter( - DirectoryInitializationService.BROADCAST_ACTION); + } + if (DirectoryInitializationService.areDolphinDirectoriesReady()) { + loadSettingsUI(); + } else { + mView.showLoading(); + IntentFilter statusIntentFilter = new IntentFilter( + DirectoryInitializationService.BROADCAST_ACTION); - directoryStateReceiver = - new DirectoryStateReceiver(directoryInitializationState -> - { - if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) - { - mView.hideLoading(); - loadSettingsUI(); - } - else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) - { - mView.showPermissionNeededHint(); - mView.hideLoading(); - } - else if (directoryInitializationState == DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) - { - mView.showExternalStorageNotMountedHint(); - mView.hideLoading(); - } - }); + directoryStateReceiver = + new DirectoryStateReceiver(directoryInitializationState -> + { + if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) { + mView.hideLoading(); + loadSettingsUI(); + } else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) { + mView.showPermissionNeededHint(); + mView.hideLoading(); + } else if (directoryInitializationState == DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) { + mView.showExternalStorageNotMountedHint(); + mView.hideLoading(); + } + }); - mView.startDirectoryInitializationService(directoryStateReceiver, statusIntentFilter); - } - } + mView.startDirectoryInitializationService(directoryStateReceiver, statusIntentFilter); + } + } - public void setSettings(ArrayList> settings) - { - mSettings = settings; - } + public void setSettings(ArrayList> settings) { + mSettings = settings; + } - public HashMap getSettings(int file) - { - return mSettings.get(file); - } + public HashMap getSettings(int file) { + return mSettings.get(file); + } - public void onStop(boolean finishing) - { - if (directoryStateReceiver != null) - { - mView.stopListeningToDirectoryInitializationService(directoryStateReceiver); - directoryStateReceiver = null; - } + public void onStop(boolean finishing) { + if (directoryStateReceiver != null) { + mView.stopListeningToDirectoryInitializationService(directoryStateReceiver); + directoryStateReceiver = null; + } - if (mSettings != null && finishing && mShouldSave) - { - if (!TextUtils.isEmpty(gameId)) { - Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI..."); - // Needed workaround for now due to an odd bug in how it handles saving two different settings sections to the same file. It won't save GFX settings if it follows the normal saving pattern - if (menuTag.equals("Dolphin")) - { - SettingsFile.saveFile("../GameSettings/" + gameId, mSettings.get(SettingsFile.SETTINGS_DOLPHIN), mView); - } - mView.showToastMessage("Saved settings for " + gameId); - } else { - Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI..."); - SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG, mSettings.get(SettingsFile.SETTINGS_DOLPHIN), mView); - mView.showToastMessage("Saved settings to INI files"); - } - } - } + if (mSettings != null && finishing && mShouldSave) { + if (!TextUtils.isEmpty(gameId)) { + Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI..."); + // Needed workaround for now due to an odd bug in how it handles saving two different settings sections to the same file. It won't save GFX settings if it follows the normal saving pattern + if (menuTag.equals("Dolphin")) { + SettingsFile.saveFile("../GameSettings/" + gameId, mSettings.get(SettingsFile.SETTINGS_DOLPHIN), mView); + } + mView.showToastMessage("Saved settings for " + gameId); + } else { + Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI..."); + SettingsFile.saveFile(SettingsFile.FILE_NAME_CONFIG, mSettings.get(SettingsFile.SETTINGS_DOLPHIN), mView); + mView.showToastMessage("Saved settings to INI files"); + } + } + } - public void addToStack() - { - mStackCount++; - } + public void addToStack() { + mStackCount++; + } - public void onBackPressed() - { - if (mStackCount > 0) - { - mView.popBackStack(); - mStackCount--; - } - else - { - mView.finish(); - } - } + public void onBackPressed() { + if (mStackCount > 0) { + mView.popBackStack(); + mStackCount--; + } else { + mView.finish(); + } + } - public boolean handleOptionsItem(int itemId) - { - switch (itemId) - { - case R.id.menu_save_exit: - mView.finish(); - return true; - } + public boolean handleOptionsItem(int itemId) { + switch (itemId) { + case R.id.menu_save_exit: + mView.finish(); + return true; + } - return false; - } + return false; + } - public void onSettingChanged() - { - mShouldSave = true; - } + public void onSettingChanged() { + mShouldSave = true; + } - public void saveState(Bundle outState) - { - outState.putBoolean(KEY_SHOULD_SAVE, mShouldSave); - } + public void saveState(Bundle outState) { + outState.putBoolean(KEY_SHOULD_SAVE, mShouldSave); + } - public void onGcPadSettingChanged(String key, int value) - { - if (value != 0) // Not disabled - { - mView.showSettingsFragment(key + (value / 6), true, gameId); - } - } + public void onGcPadSettingChanged(String key, int value) { + if (value != 0) // Not disabled + { + mView.showSettingsFragment(key + (value / 6), true, gameId); + } + } - public void onWiimoteSettingChanged(String section, int value) - { - switch (value) - { - case 1: - mView.showSettingsFragment(section, true, gameId); - break; + public void onWiimoteSettingChanged(String section, int value) { + switch (value) { + case 1: + mView.showSettingsFragment(section, true, gameId); + break; - case 2: - mView.showToastMessage("Please make sure Continuous Scanning is enabled in Core Settings."); - break; - } - } + case 2: + mView.showToastMessage("Please make sure Continuous Scanning is enabled in Core Settings."); + break; + } + } - public void onExtensionSettingChanged(String key, int value) - { - if (value != 0) // None - { - mView.showSettingsFragment(key + value, true, gameId); - } - } + public void onExtensionSettingChanged(String key, int value) { + if (value != 0) // None + { + mView.showSettingsFragment(key + value, true, gameId); + } + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityView.java index 38bfc91da..f0cc650ea 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsActivityView.java @@ -11,130 +11,129 @@ import java.util.HashMap; /** * Abstraction for the Activity that manages SettingsFragments. */ -public interface SettingsActivityView -{ - /** - * Show a new SettingsFragment. - * - * @param menuTag Identifier for the settings group that should be displayed. - * @param addToStack Whether or not this fragment should replace a previous one. - */ - void showSettingsFragment(String menuTag, boolean addToStack, String gameId); +public interface SettingsActivityView { + /** + * Show a new SettingsFragment. + * + * @param menuTag Identifier for the settings group that should be displayed. + * @param addToStack Whether or not this fragment should replace a previous one. + */ + void showSettingsFragment(String menuTag, boolean addToStack, String gameId); - /** - * Called by a contained Fragment to get access to the Setting HashMap - * loaded from disk, so that each Fragment doesn't need to perform its own - * read operation. - * - * @param file The settings file to load. - * @return A possibly null HashMap of Settings. - */ - HashMap getSettings(int file); + /** + * Called by a contained Fragment to get access to the Setting HashMap + * loaded from disk, so that each Fragment doesn't need to perform its own + * read operation. + * + * @param file The settings file to load. + * @return A possibly null HashMap of Settings. + */ + HashMap getSettings(int file); - /** - * Used to provide the Activity with Settings HashMaps if a Fragment already - * has one; for example, if a rotation occurs, the Fragment will not be killed, - * but the Activity will, so the Activity needs to have its HashMaps resupplied. - * - * @param settings The ArrayList of all the Settings HashMaps. - */ - void setSettings(ArrayList> settings); + /** + * Used to provide the Activity with Settings HashMaps if a Fragment already + * has one; for example, if a rotation occurs, the Fragment will not be killed, + * but the Activity will, so the Activity needs to have its HashMaps resupplied. + * + * @param settings The ArrayList of all the Settings HashMaps. + */ + void setSettings(ArrayList> settings); - /** - * Called when an asynchronous load operation completes. - * - * @param settings The (possibly null) result of the ini load operation. - */ - void onSettingsFileLoaded(ArrayList> settings); + /** + * Called when an asynchronous load operation completes. + * + * @param settings The (possibly null) result of the ini load operation. + */ + void onSettingsFileLoaded(ArrayList> settings); - /** - * Called when an asynchronous load operation fails. - */ - void onSettingsFileNotFound(); + /** + * Called when an asynchronous load operation fails. + */ + void onSettingsFileNotFound(); - /** - * Display a popup text message on screen. - * - * @param message The contents of the onscreen message. - */ - void showToastMessage(String message); + /** + * Display a popup text message on screen. + * + * @param message The contents of the onscreen message. + */ + void showToastMessage(String message); - /** - * Show the previous fragment. - */ - void popBackStack(); + /** + * Show the previous fragment. + */ + void popBackStack(); - /** - * End the activity. - */ - void finish(); + /** + * End the activity. + */ + void finish(); - /** - * Called by a containing Fragment to tell the Activity that a setting was changed; - * unless this has been called, the Activity will not save to disk. - */ - void onSettingChanged(); + /** + * Called by a containing Fragment to tell the Activity that a setting was changed; + * unless this has been called, the Activity will not save to disk. + */ + void onSettingChanged(); - /** - * Called by a containing Fragment to tell the containing Activity that a GCPad's setting - * was modified. - * - * @param key Identifier for the GCPad that was modified. - * @param value New setting for the GCPad. - */ - void onGcPadSettingChanged(String key, int value); + /** + * Called by a containing Fragment to tell the containing Activity that a GCPad's setting + * was modified. + * + * @param key Identifier for the GCPad that was modified. + * @param value New setting for the GCPad. + */ + void onGcPadSettingChanged(String key, int value); - /** - * Called by a containing Fragment to tell the containing Activity that a Wiimote's setting - * was modified. - * - * @param section Identifier for Wiimote that was modified; Wiimotes are identified by their section, - * not their key. - * @param value New setting for the Wiimote. - */ - void onWiimoteSettingChanged(String section, int value); + /** + * Called by a containing Fragment to tell the containing Activity that a Wiimote's setting + * was modified. + * + * @param section Identifier for Wiimote that was modified; Wiimotes are identified by their section, + * not their key. + * @param value New setting for the Wiimote. + */ + void onWiimoteSettingChanged(String section, int value); - /** - * Called by a containing Fragment to tell the containing Activity that an extension setting - * was modified. - * - * @param key Identifier for the extension that was modified. - * @param value New setting for the extension. - */ - void onExtensionSettingChanged(String key, int value); + /** + * Called by a containing Fragment to tell the containing Activity that an extension setting + * was modified. + * + * @param key Identifier for the extension that was modified. + * @param value New setting for the extension. + */ + void onExtensionSettingChanged(String key, int value); - /** - * Show loading dialog while loading the settings - */ - void showLoading(); + /** + * Show loading dialog while loading the settings + */ + void showLoading(); - /** - * Hide the loading the dialog - */ - void hideLoading(); + /** + * Hide the loading the dialog + */ + void hideLoading(); - /** - * Show a hint to the user that the app needs write to external storage access - */ - void showPermissionNeededHint(); + /** + * Show a hint to the user that the app needs write to external storage access + */ + void showPermissionNeededHint(); - /** - * Show a hint to the user that the app needs the external storage to be mounted - */ - void showExternalStorageNotMountedHint(); + /** + * Show a hint to the user that the app needs the external storage to be mounted + */ + void showExternalStorageNotMountedHint(); - /** - * Start the DirectoryInitializationService and listen for the result. - * - * @param receiver the broadcast receiver for the DirectoryInitializationService - * @param filter the Intent broadcasts to be received. - */ - void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter); + /** + * Start the DirectoryInitializationService and listen for the result. + * + * @param receiver the broadcast receiver for the DirectoryInitializationService + * @param filter the Intent broadcasts to be received. + */ + void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter); - /** - * Stop listening to the DirectoryInitializationService. - * - * @param receiver The broadcast receiver to unregister. - */ - void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver); + /** + * Stop listening to the DirectoryInitializationService. + * + * @param receiver The broadcast receiver to unregister. + */ + void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsAdapter.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsAdapter.java index 30ed52962..e7da7f837 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsAdapter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsAdapter.java @@ -40,140 +40,125 @@ import org.citra.citra_android.utils.SettingsFile; import java.util.ArrayList; public final class SettingsAdapter extends RecyclerView.Adapter - implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener -{ - private SettingsFragmentView mView; - private Context mContext; - private ArrayList mSettings; + implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener { + private SettingsFragmentView mView; + private Context mContext; + private ArrayList mSettings; - private SettingsItem mClickedItem; - private int mSeekbarProgress; + private SettingsItem mClickedItem; + private int mSeekbarProgress; - private AlertDialog mDialog; - private TextView mTextSliderValue; + private AlertDialog mDialog; + private TextView mTextSliderValue; - public SettingsAdapter(SettingsFragmentView view, Context context) - { - mView = view; - mContext = context; - } + public SettingsAdapter(SettingsFragmentView view, Context context) { + mView = view; + mContext = context; + } - @Override - public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) - { - View view; - LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + @Override + public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view; + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); - switch (viewType) - { - case SettingsItem.TYPE_HEADER: - view = inflater.inflate(R.layout.list_item_settings_header, parent, false); - return new HeaderViewHolder(view, this); + switch (viewType) { + case SettingsItem.TYPE_HEADER: + view = inflater.inflate(R.layout.list_item_settings_header, parent, false); + return new HeaderViewHolder(view, this); - case SettingsItem.TYPE_CHECKBOX: - view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false); - return new CheckBoxSettingViewHolder(view, this); + case SettingsItem.TYPE_CHECKBOX: + view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false); + return new CheckBoxSettingViewHolder(view, this); - case SettingsItem.TYPE_SINGLE_CHOICE: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new SingleChoiceViewHolder(view, this); + case SettingsItem.TYPE_SINGLE_CHOICE: + view = inflater.inflate(R.layout.list_item_setting, parent, false); + return new SingleChoiceViewHolder(view, this); - case SettingsItem.TYPE_SLIDER: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new SliderViewHolder(view, this); + case SettingsItem.TYPE_SLIDER: + view = inflater.inflate(R.layout.list_item_setting, parent, false); + return new SliderViewHolder(view, this); - case SettingsItem.TYPE_SUBMENU: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new SubmenuViewHolder(view, this); + case SettingsItem.TYPE_SUBMENU: + view = inflater.inflate(R.layout.list_item_setting, parent, false); + return new SubmenuViewHolder(view, this); - case SettingsItem.TYPE_INPUT_BINDING: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new InputBindingSettingViewHolder(view, this, mContext); + case SettingsItem.TYPE_INPUT_BINDING: + view = inflater.inflate(R.layout.list_item_setting, parent, false); + return new InputBindingSettingViewHolder(view, this, mContext); - case SettingsItem.TYPE_DATETIME_SETTING: - view = inflater.inflate(R.layout.list_item_setting, parent, false); - return new DateTimeViewHolder(view, this); + case SettingsItem.TYPE_DATETIME_SETTING: + view = inflater.inflate(R.layout.list_item_setting, parent, false); + return new DateTimeViewHolder(view, this); - default: - Log.error("[SettingsAdapter] Invalid view type: " + viewType); - return null; - } - } + default: + Log.error("[SettingsAdapter] Invalid view type: " + viewType); + return null; + } + } - @Override - public void onBindViewHolder(SettingViewHolder holder, int position) - { - holder.bind(getItem(position)); - } + @Override + public void onBindViewHolder(SettingViewHolder holder, int position) { + holder.bind(getItem(position)); + } - private SettingsItem getItem(int position) - { - return mSettings.get(position); - } + private SettingsItem getItem(int position) { + return mSettings.get(position); + } - @Override - public int getItemCount() - { - if (mSettings != null) - { - return mSettings.size(); - } - else - { - return 0; - } - } + @Override + public int getItemCount() { + if (mSettings != null) { + return mSettings.size(); + } else { + return 0; + } + } - @Override - public int getItemViewType(int position) - { - return getItem(position).getType(); - } + @Override + public int getItemViewType(int position) { + return getItem(position).getType(); + } - public void setSettings(ArrayList settings) - { - mSettings = settings; - notifyDataSetChanged(); - } + public void setSettings(ArrayList settings) { + mSettings = settings; + notifyDataSetChanged(); + } - public void onBooleanClick(CheckBoxSetting item, int position, boolean checked) - { - IntSetting setting = item.setChecked(checked); - notifyItemChanged(position); + public void onBooleanClick(CheckBoxSetting item, int position, boolean checked) { + IntSetting setting = item.setChecked(checked); + notifyItemChanged(position); - if (setting != null) - { - mView.putSetting(setting); - } + if (setting != null) { + mView.putSetting(setting); + } - mView.onSettingChanged(); - } + mView.onSettingChanged(); + } - public void onSingleChoiceClick(SingleChoiceSetting item) - { - mClickedItem = item; + public void onSingleChoiceClick(SingleChoiceSetting item) { + mClickedItem = item; - int value = getSelectionForSingleChoiceValue(item); + int value = getSelectionForSingleChoiceValue(item); - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); - builder.setTitle(item.getNameId()); - builder.setSingleChoiceItems(item.getChoicesId(), value, this); + builder.setTitle(item.getNameId()); + builder.setSingleChoiceItems(item.getChoicesId(), value, this); - mDialog = builder.show(); - } + mDialog = builder.show(); + } - public void onDateTimeClick(DateTimeSetting item){ - mClickedItem = item; + public void onDateTimeClick(DateTimeSetting item) { + mClickedItem = item; - AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); + AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity()); - LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); - View view = inflater.inflate(R.layout.sysclock_datetime_picker, null); + LayoutInflater inflater = LayoutInflater.from(mView.getActivity()); + View view = inflater.inflate(R.layout.sysclock_datetime_picker, null); - DatePicker dp = (DatePicker) view.findViewById(R.id.date_picker); - TimePicker tp = (TimePicker) view.findViewById(R.id.time_picker); + DatePicker dp = view.findViewById(R.id.date_picker); + TimePicker tp = view.findViewById(R.id.time_picker); //set date and time to substrings of settingValue; format = 2018-12-24 04:20:69 (alright maybe not that 69) String settingValue = item.getValue(); @@ -188,7 +173,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter - { - item.setValue(""); + public void onSubmenuClick(SubmenuSetting item) { + mView.loadSubMenu(item.getMenuKey()); + } - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.remove(item.getKey()); - editor.apply(); - }); - dialog.setOnDismissListener(dialog1 -> - { - StringSetting setting = new StringSetting(item.getKey(), item.getSection(), item.getFile(), item.getValue()); - notifyItemChanged(position); + public void onInputBindingClick(final InputBindingSetting item, final int position) { + final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item); + dialog.setTitle(R.string.input_binding); + dialog.setMessage(String.format(mContext.getString(R.string.input_binding_descrip), mContext.getString(item.getNameId()))); + dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this); + dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), (dialogInterface, i) -> + { + item.setValue(""); - if (setting != null) - { - mView.putSetting(setting); - } + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.remove(item.getKey()); + editor.apply(); + }); + dialog.setOnDismissListener(dialog1 -> + { + StringSetting setting = new StringSetting(item.getKey(), item.getSection(), item.getFile(), item.getValue()); + notifyItemChanged(position); - mView.onSettingChanged(); - }); - dialog.setCanceledOnTouchOutside(false); - dialog.show(); - } + if (setting != null) { + mView.putSetting(setting); + } - @Override - public void onClick(DialogInterface dialog, int which) - { - if (mClickedItem instanceof SingleChoiceSetting) - { - SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem; + mView.onSettingChanged(); + }); + dialog.setCanceledOnTouchOutside(false); + dialog.show(); + } - int value = getValueForSingleChoiceSelection(scSetting, which); + @Override + public void onClick(DialogInterface dialog, int which) { + if (mClickedItem instanceof SingleChoiceSetting) { + SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem; - // Get the backing Setting, which may be null (if for example it was missing from the file) - IntSetting setting = scSetting.setSelectedValue(value); - if (setting != null) - { - mView.putSetting(setting); - } + int value = getValueForSingleChoiceSelection(scSetting, which); - closeDialog(); - } - else if (mClickedItem instanceof SliderSetting) - { - SliderSetting sliderSetting = (SliderSetting) mClickedItem; - if (sliderSetting.getSetting() instanceof FloatSetting) - { - float value; + // Get the backing Setting, which may be null (if for example it was missing from the file) + IntSetting setting = scSetting.setSelectedValue(value); + if (setting != null) { + mView.putSetting(setting); + } - if (sliderSetting.getKey().equals(SettingsFile.KEY_FRAME_LIMIT)) - { - value = mSeekbarProgress / 100.0f; - } - else - { - value = (float) mSeekbarProgress; - } + closeDialog(); + } else if (mClickedItem instanceof SliderSetting) { + SliderSetting sliderSetting = (SliderSetting) mClickedItem; + if (sliderSetting.getSetting() instanceof FloatSetting) { + float value; - FloatSetting setting = sliderSetting.setSelectedValue(value); - if (setting != null) - { - mView.putSetting(setting); - } - } - else - { - IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress); - if (setting != null) - { - mView.putSetting(setting); - } - } - } + if (sliderSetting.getKey().equals(SettingsFile.KEY_FRAME_LIMIT)) { + value = mSeekbarProgress / 100.0f; + } else { + value = (float) mSeekbarProgress; + } - mView.onSettingChanged(); - mClickedItem = null; - mSeekbarProgress = -1; - } + FloatSetting setting = sliderSetting.setSelectedValue(value); + if (setting != null) { + mView.putSetting(setting); + } + } else { + IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress); + if (setting != null) { + mView.putSetting(setting); + } + } + } - public void closeDialog() - { - if (mDialog != null) - { - mDialog.dismiss(); - mDialog = null; - } - } + mView.onSettingChanged(); + mClickedItem = null; + mSeekbarProgress = -1; + } - @Override - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) - { - mSeekbarProgress = progress; - mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); - } + public void closeDialog() { + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + } - @Override - public void onStartTrackingTouch(SeekBar seekBar) - { - } + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + mSeekbarProgress = progress; + mTextSliderValue.setText(String.valueOf(mSeekbarProgress)); + } - @Override - public void onStopTrackingTouch(SeekBar seekBar) - { - } + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + } - private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) - { - int valuesId = item.getValuesId(); + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + } - if (valuesId > 0) - { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - return valuesArray[which]; - } - else - { - return which; - } - } + private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which) { + int valuesId = item.getValuesId(); - private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) - { - int value = item.getSelectedValue(); - int valuesId = item.getValuesId(); + if (valuesId > 0) { + int[] valuesArray = mContext.getResources().getIntArray(valuesId); + return valuesArray[which]; + } else { + return which; + } + } - if (valuesId > 0) - { - int[] valuesArray = mContext.getResources().getIntArray(valuesId); - for (int index = 0; index < valuesArray.length; index++) - { - int current = valuesArray[index]; - if (current == value) - { - return index; - } - } - } - else - { - return value; - } + private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) { + int value = item.getSelectedValue(); + int valuesId = item.getValuesId(); - return -1; - } + if (valuesId > 0) { + int[] valuesArray = mContext.getResources().getIntArray(valuesId); + for (int index = 0; index < valuesArray.length; index++) { + int current = valuesArray[index]; + if (current == value) { + return index; + } + } + } else { + return value; + } + + return -1; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragment.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragment.java index 7224f51b3..9db08e057 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragment.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragment.java @@ -21,169 +21,148 @@ import org.citra.citra_android.utils.SettingsFile; import java.util.ArrayList; import java.util.HashMap; -public final class SettingsFragment extends Fragment implements SettingsFragmentView -{ - private static final String ARGUMENT_MENU_TAG = "menu_tag"; - private static final String ARGUMENT_GAME_ID = "game_id"; +public final class SettingsFragment extends Fragment implements SettingsFragmentView { + private static final String ARGUMENT_MENU_TAG = "menu_tag"; + private static final String ARGUMENT_GAME_ID = "game_id"; - private SettingsFragmentPresenter mPresenter = new SettingsFragmentPresenter(this); - private SettingsActivityView mActivity; + private SettingsFragmentPresenter mPresenter = new SettingsFragmentPresenter(this); + private SettingsActivityView mActivity; - private SettingsAdapter mAdapter; + private SettingsAdapter mAdapter; - public static Fragment newInstance(String menuTag, String gameId) - { - SettingsFragment fragment = new SettingsFragment(); + public static Fragment newInstance(String menuTag, String gameId) { + SettingsFragment fragment = new SettingsFragment(); - Bundle arguments = new Bundle(); - arguments.putString(ARGUMENT_MENU_TAG, menuTag); - arguments.putString(ARGUMENT_GAME_ID, gameId); + Bundle arguments = new Bundle(); + arguments.putString(ARGUMENT_MENU_TAG, menuTag); + arguments.putString(ARGUMENT_GAME_ID, gameId); - fragment.setArguments(arguments); - return fragment; - } + fragment.setArguments(arguments); + return fragment; + } - @Override - public void onAttach(Context context) - { - super.onAttach(context); + @Override + public void onAttach(Context context) { + super.onAttach(context); - mActivity = (SettingsActivityView) context; - mPresenter.onAttach(); - } + mActivity = (SettingsActivityView) context; + mPresenter.onAttach(); + } - /** - * This version of onAttach is needed for versions below Marshmallow. - * - * @param activity - */ - @Override - public void onAttach(Activity activity) - { - super.onAttach(activity); + /** + * This version of onAttach is needed for versions below Marshmallow. + * + * @param activity + */ + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); - mActivity = (SettingsActivityView) activity; - mPresenter.onAttach(); - } + mActivity = (SettingsActivityView) activity; + mPresenter.onAttach(); + } - @Override - public void onCreate(Bundle savedInstanceState) - { - super.onCreate(savedInstanceState); + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - setRetainInstance(true); - String menuTag = getArguments().getString(ARGUMENT_MENU_TAG); - String gameId = getArguments().getString(ARGUMENT_GAME_ID); + setRetainInstance(true); + String menuTag = getArguments().getString(ARGUMENT_MENU_TAG); + String gameId = getArguments().getString(ARGUMENT_GAME_ID); - mAdapter = new SettingsAdapter(this, getActivity()); + mAdapter = new SettingsAdapter(this, getActivity()); - mPresenter.onCreate(menuTag, gameId); - } + mPresenter.onCreate(menuTag, gameId); + } - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) - { - return inflater.inflate(R.layout.fragment_settings, container, false); - } + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_settings, container, false); + } - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) - { - LinearLayoutManager manager = new LinearLayoutManager(getActivity()); + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + LinearLayoutManager manager = new LinearLayoutManager(getActivity()); - RecyclerView recyclerView = view.findViewById(R.id.list_settings); + RecyclerView recyclerView = view.findViewById(R.id.list_settings); - recyclerView.setAdapter(mAdapter); - recyclerView.setLayoutManager(manager); - recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null)); + recyclerView.setAdapter(mAdapter); + recyclerView.setLayoutManager(manager); + recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null)); - SettingsActivityView activity = (SettingsActivityView) getActivity(); + SettingsActivityView activity = (SettingsActivityView) getActivity(); - ArrayList> settings = new ArrayList<>(); - settings.add(SettingsFile.SETTINGS_DOLPHIN, activity.getSettings(SettingsFile.SETTINGS_DOLPHIN)); - mPresenter.onViewCreated(settings); - } + ArrayList> settings = new ArrayList<>(); + settings.add(SettingsFile.SETTINGS_DOLPHIN, activity.getSettings(SettingsFile.SETTINGS_DOLPHIN)); + mPresenter.onViewCreated(settings); + } - @Override - public void onDetach() - { - super.onDetach(); - mActivity = null; + @Override + public void onDetach() { + super.onDetach(); + mActivity = null; - if (mAdapter != null) - { - mAdapter.closeDialog(); - } - } + if (mAdapter != null) { + mAdapter.closeDialog(); + } + } - @Override - public void onSettingsFileLoaded(ArrayList> settings) - { - mPresenter.setSettings(settings); - } + @Override + public void onSettingsFileLoaded(ArrayList> settings) { + mPresenter.setSettings(settings); + } - @Override - public void passSettingsToActivity(ArrayList> settings) - { - if (mActivity != null) - { - mActivity.setSettings(settings); - } - } + @Override + public void passSettingsToActivity(ArrayList> settings) { + if (mActivity != null) { + mActivity.setSettings(settings); + } + } - @Override - public void showSettingsList(ArrayList settingsList) - { - mAdapter.setSettings(settingsList); - } + @Override + public void showSettingsList(ArrayList settingsList) { + mAdapter.setSettings(settingsList); + } - @Override - public void loadDefaultSettings() - { - mPresenter.loadDefaultSettings(); - } + @Override + public void loadDefaultSettings() { + mPresenter.loadDefaultSettings(); + } - @Override - public void loadSubMenu(String menuKey) - { - mActivity.showSettingsFragment(menuKey, true, getArguments().getString(ARGUMENT_GAME_ID)); - } + @Override + public void loadSubMenu(String menuKey) { + mActivity.showSettingsFragment(menuKey, true, getArguments().getString(ARGUMENT_GAME_ID)); + } - @Override - public void showToastMessage(String message) - { - mActivity.showToastMessage(message); - } + @Override + public void showToastMessage(String message) { + mActivity.showToastMessage(message); + } - @Override - public void putSetting(Setting setting) - { - mPresenter.putSetting(setting); - } + @Override + public void putSetting(Setting setting) { + mPresenter.putSetting(setting); + } - @Override - public void onSettingChanged() - { - mActivity.onSettingChanged(); - } + @Override + public void onSettingChanged() { + mActivity.onSettingChanged(); + } - @Override - public void onGcPadSettingChanged(String key, int value) - { - mActivity.onGcPadSettingChanged(key, value); - } + @Override + public void onGcPadSettingChanged(String key, int value) { + mActivity.onGcPadSettingChanged(key, value); + } - @Override - public void onWiimoteSettingChanged(String section, int value) - { - mActivity.onWiimoteSettingChanged(section, value); - } + @Override + public void onWiimoteSettingChanged(String section, int value) { + mActivity.onWiimoteSettingChanged(section, value); + } - @Override - public void onExtensionSettingChanged(String key, int value) - { - mActivity.onExtensionSettingChanged(key, value); - } + @Override + public void onExtensionSettingChanged(String key, int value) { + mActivity.onExtensionSettingChanged(key, value); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentPresenter.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentPresenter.java index fdd29e5b1..35a43322b 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentPresenter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentPresenter.java @@ -18,205 +18,178 @@ import org.citra.citra_android.utils.SettingsFile; import java.util.ArrayList; import java.util.HashMap; -public final class SettingsFragmentPresenter -{ - private SettingsFragmentView mView; +public final class SettingsFragmentPresenter { + private SettingsFragmentView mView; - private String mMenuTag; - private String mGameID; + private String mMenuTag; + private String mGameID; - private ArrayList> mSettings; - private ArrayList mSettingsList; + private ArrayList> mSettings; + private ArrayList mSettingsList; - private int mControllerNumber; - private int mControllerType; + private int mControllerNumber; + private int mControllerType; - public SettingsFragmentPresenter(SettingsFragmentView view) - { - mView = view; - } + public SettingsFragmentPresenter(SettingsFragmentView view) { + mView = view; + } - public void onCreate(String menuTag, String gameId) - { - mGameID = gameId; - mMenuTag = menuTag; + public void onCreate(String menuTag, String gameId) { + mGameID = gameId; + mMenuTag = menuTag; - } + } - public void onViewCreated(ArrayList> settings) - { - setSettings(settings); - } + public void onViewCreated(ArrayList> settings) { + setSettings(settings); + } - /** - * If the screen is rotated, the Activity will forget the settings map. This fragment - * won't, though; so rather than have the Activity reload from disk, have the fragment pass - * the settings map back to the Activity. - */ - public void onAttach() - { - if (mSettings != null) - { - mView.passSettingsToActivity(mSettings); - } - } + /** + * If the screen is rotated, the Activity will forget the settings map. This fragment + * won't, though; so rather than have the Activity reload from disk, have the fragment pass + * the settings map back to the Activity. + */ + public void onAttach() { + if (mSettings != null) { + mView.passSettingsToActivity(mSettings); + } + } - public void putSetting(Setting setting) - { - mSettings.get(setting.getFile()).get(setting.getSection()).putSetting(setting); - } + public void putSetting(Setting setting) { + mSettings.get(setting.getFile()).get(setting.getSection()).putSetting(setting); + } - public void loadDefaultSettings() - { - loadSettingsList(); - } + public void loadDefaultSettings() { + loadSettingsList(); + } - public void setSettings(ArrayList> settings) - { - if (mSettingsList == null && settings != null) - { - mSettings = settings; + public void setSettings(ArrayList> settings) { + if (mSettingsList == null && settings != null) { + mSettings = settings; - loadSettingsList(); - } - else - { - mView.showSettingsList(mSettingsList); - } - } + loadSettingsList(); + } else { + mView.showSettingsList(mSettingsList); + } + } - private void loadSettingsList() - { - if (!TextUtils.isEmpty(mGameID)) - { - mView.getActivity().setTitle("Game Settings: " + mGameID); - } - ArrayList sl = new ArrayList<>(); + private void loadSettingsList() { + if (!TextUtils.isEmpty(mGameID)) { + mView.getActivity().setTitle("Game Settings: " + mGameID); + } + ArrayList sl = new ArrayList<>(); - switch (mMenuTag) - { - case SettingsFile.FILE_NAME_CONFIG: - addCoreSettings(sl); - sl.add(new HeaderSetting(null, null, R.string.video_backend, 0)); - addGraphicsSettings(sl); - break; - default: - mView.showToastMessage("Unimplemented menu"); - return; - } + switch (mMenuTag) { + case SettingsFile.FILE_NAME_CONFIG: + addCoreSettings(sl); + sl.add(new HeaderSetting(null, null, R.string.video_backend, 0)); + addGraphicsSettings(sl); + break; + default: + mView.showToastMessage("Unimplemented menu"); + return; + } - mSettingsList = sl; - mView.showSettingsList(mSettingsList); - } + mSettingsList = sl; + mView.showSettingsList(mSettingsList); + } - private void addCoreSettings(ArrayList sl) - { - Setting useCpuJit = null; - Setting audioStretch = null; - Setting region = null; - Setting systemClock = null; - Setting dateTime = null; + private void addCoreSettings(ArrayList sl) { + Setting useCpuJit = null; + Setting audioStretch = null; + Setting region = null; + Setting systemClock = null; + Setting dateTime = null; - if (!mSettings.get(SettingsFile.SETTINGS_DOLPHIN).isEmpty()) - { - useCpuJit = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_CORE).getSetting(SettingsFile.KEY_CPU_JIT); - audioStretch = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_AUDIO).getSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING); - region = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_SYSTEM).getSetting(SettingsFile.KEY_REGION_VALUE); - systemClock = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_SYSTEM).getSetting(SettingsFile.KEY_INIT_CLOCK); - dateTime = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_SYSTEM).getSetting(SettingsFile.KEY_INIT_TIME); - } - else - { - mView.passSettingsToActivity(mSettings); - } + if (!mSettings.get(SettingsFile.SETTINGS_DOLPHIN).isEmpty()) { + useCpuJit = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_CORE).getSetting(SettingsFile.KEY_CPU_JIT); + audioStretch = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_AUDIO).getSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING); + region = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_SYSTEM).getSetting(SettingsFile.KEY_REGION_VALUE); + systemClock = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_SYSTEM).getSetting(SettingsFile.KEY_INIT_CLOCK); + dateTime = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_SYSTEM).getSetting(SettingsFile.KEY_INIT_TIME); + } else { + mView.passSettingsToActivity(mSettings); + } - String defaultCpuCore = System.getProperty("os.arch"); - switch (defaultCpuCore) - { - case "x86_64": - sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, SettingsFile.SECTION_CORE, - SettingsFile.SETTINGS_DOLPHIN, R.string.cpu_jit, 0, true, useCpuJit)); - break; - case "aarch64": - default: - break; - } - sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_VALUE, SettingsFile.SECTION_SYSTEM,SettingsFile.SETTINGS_DOLPHIN, R.string.region, 0, R.array.regionNames, R.array.regionValues, -1, region)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING, SettingsFile.SECTION_AUDIO, SettingsFile.SETTINGS_DOLPHIN, R.string.audio_stretch, R.string.audio_stretch_description, false, audioStretch)); - sl.add(new SingleChoiceSetting(SettingsFile.KEY_INIT_CLOCK, SettingsFile.SECTION_SYSTEM, SettingsFile.SETTINGS_DOLPHIN, R.string.init_clock, R.string.init_clock_descrip, R.array.systemClockNames, R.array.systemClockValues, 0, systemClock)); - sl.add(new DateTimeSetting(SettingsFile.KEY_INIT_TIME, SettingsFile.SECTION_SYSTEM, SettingsFile.SETTINGS_DOLPHIN, R.string.init_time, R.string.init_time_descrip, "2000-01-01 00:00:01", dateTime)); - } + String defaultCpuCore = System.getProperty("os.arch"); + switch (defaultCpuCore) { + case "x86_64": + sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, SettingsFile.SECTION_CORE, + SettingsFile.SETTINGS_DOLPHIN, R.string.cpu_jit, 0, true, useCpuJit)); + break; + case "aarch64": + default: + break; + } + sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_VALUE, SettingsFile.SECTION_SYSTEM, SettingsFile.SETTINGS_DOLPHIN, R.string.region, 0, R.array.regionNames, R.array.regionValues, -1, region)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING, SettingsFile.SECTION_AUDIO, SettingsFile.SETTINGS_DOLPHIN, R.string.audio_stretch, R.string.audio_stretch_description, false, audioStretch)); + sl.add(new SingleChoiceSetting(SettingsFile.KEY_INIT_CLOCK, SettingsFile.SECTION_SYSTEM, SettingsFile.SETTINGS_DOLPHIN, R.string.init_clock, R.string.init_clock_descrip, R.array.systemClockNames, R.array.systemClockValues, 0, systemClock)); + sl.add(new DateTimeSetting(SettingsFile.KEY_INIT_TIME, SettingsFile.SECTION_SYSTEM, SettingsFile.SETTINGS_DOLPHIN, R.string.init_time, R.string.init_time_descrip, "2000-01-01 00:00:01", dateTime)); + } - private void addGraphicsSettings(ArrayList sl) - { - Setting hardwareRenderer = null; - Setting hardwareShader = null; - Setting shadersAccurateMul = null; - Setting shadersAccurateGs = null; - Setting shaderJitEnable = null; - Setting resolutionFactor = null; - Setting vsyncEnable = null; - Setting frameLimitEnable = null; - Setting frameLimitValue = null; - Setting stereoscopyEnable = null; - Setting stereoscopyDepth = null; + private void addGraphicsSettings(ArrayList sl) { + Setting hardwareRenderer = null; + Setting hardwareShader = null; + Setting shadersAccurateMul = null; + Setting shadersAccurateGs = null; + Setting shaderJitEnable = null; + Setting resolutionFactor = null; + Setting vsyncEnable = null; + Setting frameLimitEnable = null; + Setting frameLimitValue = null; + Setting stereoscopyEnable = null; + Setting stereoscopyDepth = null; - if (!mSettings.get(SettingsFile.SETTINGS_DOLPHIN).isEmpty()) - { - hardwareRenderer = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_HW_RENDERER); - hardwareShader = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_HW_SHADER); - shadersAccurateMul = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); - shadersAccurateGs = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_SHADERS_ACCURATE_GS); - shaderJitEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_USE_SHADER_JIT); - resolutionFactor = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); - vsyncEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_USE_VSYNC); - frameLimitEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED); - frameLimitValue = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_FRAME_LIMIT); - stereoscopyEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_STEREOSCOPY); - stereoscopyDepth = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_FACTOR_3D); - } - else - { - mView.passSettingsToActivity(mSettings); - } + if (!mSettings.get(SettingsFile.SETTINGS_DOLPHIN).isEmpty()) { + hardwareRenderer = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_HW_RENDERER); + hardwareShader = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_HW_SHADER); + shadersAccurateMul = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); + shadersAccurateGs = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_SHADERS_ACCURATE_GS); + shaderJitEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_USE_SHADER_JIT); + resolutionFactor = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); + vsyncEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_USE_VSYNC); + frameLimitEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED); + frameLimitValue = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_FRAME_LIMIT); + stereoscopyEnable = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_STEREOSCOPY); + stereoscopyDepth = mSettings.get(SettingsFile.SETTINGS_DOLPHIN).get(SettingsFile.SECTION_RENDERER).getSetting(SettingsFile.KEY_FACTOR_3D); + } else { + mView.passSettingsToActivity(mSettings); + } - if (mSettings.get(SettingsFile.SETTINGS_DOLPHIN).isEmpty()) - { - mView.passSettingsToActivity(mSettings); - } + if (mSettings.get(SettingsFile.SETTINGS_DOLPHIN).isEmpty()) { + mView.passSettingsToActivity(mSettings); + } - sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.hw_renderer, 0, true, hardwareRenderer)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.hw_shaders, R.string.hw_shaders_descrip, true, hardwareShader)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.shaders_accurate_mul, 0, false, shadersAccurateMul)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_GS, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.shaders_accurate_gs , 0, false, shadersAccurateGs)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_SHADER_JIT, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.use_shader_jit, 0, true, shaderJitEnable)); - sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.internal_resolution, R.string.internal_resolution_descrip, 10, "x", 0, resolutionFactor)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.vsync , 0, false, vsyncEnable)); - sl.add(new CheckBoxSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.overclock_enable, 0, false, frameLimitEnable)); - sl.add(new SliderSetting(SettingsFile.KEY_FRAME_LIMIT, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.overclock_title, R.string.overclock_enable_description, 500, "%", 100, frameLimitValue)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.hw_renderer, 0, true, hardwareRenderer)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.hw_shaders, R.string.hw_shaders_descrip, true, hardwareShader)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.shaders_accurate_mul, 0, false, shadersAccurateMul)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_GS, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.shaders_accurate_gs, 0, false, shadersAccurateGs)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_SHADER_JIT, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.use_shader_jit, 0, true, shaderJitEnable)); + sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.internal_resolution, R.string.internal_resolution_descrip, 10, "x", 0, resolutionFactor)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.vsync, 0, false, vsyncEnable)); + sl.add(new CheckBoxSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.overclock_enable, 0, false, frameLimitEnable)); + sl.add(new SliderSetting(SettingsFile.KEY_FRAME_LIMIT, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.overclock_title, R.string.overclock_enable_description, 500, "%", 100, frameLimitValue)); - // Todo: Implement ColorPickerSetting - // sl.add(new ColorPickerSetting(SettingsFile.KEY_BACKGROUND_RED,SettingsFile.KEY_BACKGROUND_GREEN,SettingsFile.KEY_BACKGROUND_BLUE, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string., R.string., Color.BLACK, backgroundColor)); + // Todo: Implement ColorPickerSetting + // sl.add(new ColorPickerSetting(SettingsFile.KEY_BACKGROUND_RED,SettingsFile.KEY_BACKGROUND_GREEN,SettingsFile.KEY_BACKGROUND_BLUE, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string., R.string., Color.BLACK, backgroundColor)); /* Check if we support stereo If we support desktop GL then we must support at least OpenGL 3.2 If we only support OpenGLES then we need both OpenGLES 3.1 and AEP */ - EGLHelper helper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT); + EGLHelper helper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT); - if ((helper.supportsOpenGL() && helper.GetVersion() >= 320) || - (helper.supportsGLES3() && helper.GetVersion() >= 310 && helper.SupportsExtension("GL_ANDROID_extension_pack_es31a"))) - { - sl.add(new CheckBoxSetting(SettingsFile.KEY_STEREOSCOPY, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.stereoscopy, R.string.stereoscopy_descrip, false, stereoscopyEnable)); - sl.add(new SliderSetting(SettingsFile.KEY_FACTOR_3D, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.sterescopy_depth,R.string.sterescopy_depth_descrip,100,"%",0, stereoscopyDepth)); - } + if ((helper.supportsOpenGL() && helper.GetVersion() >= 320) || + (helper.supportsGLES3() && helper.GetVersion() >= 310 && helper.SupportsExtension("GL_ANDROID_extension_pack_es31a"))) { + sl.add(new CheckBoxSetting(SettingsFile.KEY_STEREOSCOPY, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.stereoscopy, R.string.stereoscopy_descrip, false, stereoscopyEnable)); + sl.add(new SliderSetting(SettingsFile.KEY_FACTOR_3D, SettingsFile.SECTION_RENDERER, SettingsFile.SETTINGS_DOLPHIN, R.string.sterescopy_depth, R.string.sterescopy_depth_descrip, 100, "%", 0, stereoscopyDepth)); + } - } + } - private void addGcPadSubSettings(ArrayList sl, int gcPadNumber, int gcPadType) - { + private void addGcPadSubSettings(ArrayList sl, int gcPadNumber, int gcPadType) { /* if (gcPadType == 1) // Emulated { @@ -280,18 +253,14 @@ public final class SettingsFragmentPresenter sl.add(new CheckBoxSetting(SettingsFile.KEY_GCADAPTER_BONGOS + gcPadNumber, SettingsFile.SECTION_CORE, SettingsFile.SETTINGS_DOLPHIN, R.string.gc_adapter_bongos, R.string.gc_adapter_bongos_description, false, bongos)); } */ - } + } - private boolean getInvertedBooleanValue(int file, String section, String key, boolean defaultValue) - { - try - { - return ((IntSetting) mSettings.get(file).get(section).getSetting(key)).getValue() != 1; - } - catch (NullPointerException ex) - { - return defaultValue; - } - } + private boolean getInvertedBooleanValue(int file, String section, String key, boolean defaultValue) { + try { + return ((IntSetting) mSettings.get(file).get(section).getSetting(key)).getValue() != 1; + } catch (NullPointerException ex) { + return defaultValue; + } + } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentView.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentView.java index a108eb98c..2f29f3aad 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentView.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFragmentView.java @@ -13,92 +13,91 @@ import java.util.HashMap; * Abstraction for a screen showing a list of settings. Instances of * this type of view will each display a layer of the setting hierarchy. */ -public interface SettingsFragmentView -{ - /** - * Called by the containing Activity to notify the Fragment that an - * asynchronous load operation completed. - * - * @param settings The (possibly null) result of the ini load operation. - */ - void onSettingsFileLoaded(ArrayList> settings); +public interface SettingsFragmentView { + /** + * Called by the containing Activity to notify the Fragment that an + * asynchronous load operation completed. + * + * @param settings The (possibly null) result of the ini load operation. + */ + void onSettingsFileLoaded(ArrayList> settings); - /** - * Pass a settings HashMap to the containing activity, so that it can - * share the HashMap with other SettingsFragments; useful so that rotations - * do not require an additional load operation. - * - * @param settings An ArrayList containing all the settings HashMaps. - */ - void passSettingsToActivity(ArrayList> settings); + /** + * Pass a settings HashMap to the containing activity, so that it can + * share the HashMap with other SettingsFragments; useful so that rotations + * do not require an additional load operation. + * + * @param settings An ArrayList containing all the settings HashMaps. + */ + void passSettingsToActivity(ArrayList> settings); - /** - * Pass an ArrayList to the View so that it can be displayed on screen. - * - * @param settingsList The result of converting the HashMap to an ArrayList - */ - void showSettingsList(ArrayList settingsList); + /** + * Pass an ArrayList to the View so that it can be displayed on screen. + * + * @param settingsList The result of converting the HashMap to an ArrayList + */ + void showSettingsList(ArrayList settingsList); - /** - * Called by the containing Activity when an asynchronous load operation fails. - * Instructs the Fragment to load the settings screen with defaults selected. - */ - void loadDefaultSettings(); + /** + * Called by the containing Activity when an asynchronous load operation fails. + * Instructs the Fragment to load the settings screen with defaults selected. + */ + void loadDefaultSettings(); - /** - * @return The Fragment's containing activity. - */ - FragmentActivity getActivity(); + /** + * @return The Fragment's containing activity. + */ + FragmentActivity getActivity(); - /** - * Tell the Fragment to tell the containing Activity to show a new - * Fragment containing a submenu of settings. - * - * @param menuKey Identifier for the settings group that should be shown. - */ - void loadSubMenu(String menuKey); + /** + * Tell the Fragment to tell the containing Activity to show a new + * Fragment containing a submenu of settings. + * + * @param menuKey Identifier for the settings group that should be shown. + */ + void loadSubMenu(String menuKey); - /** - * Tell the Fragment to tell the containing activity to display a toast message. - * - * @param message Text to be shown in the Toast - */ - void showToastMessage(String message); + /** + * Tell the Fragment to tell the containing activity to display a toast message. + * + * @param message Text to be shown in the Toast + */ + void showToastMessage(String message); - /** - * Have the fragment add a setting to the HashMap. - * - * @param setting The (possibly previously missing) new setting. - */ - void putSetting(Setting setting); + /** + * Have the fragment add a setting to the HashMap. + * + * @param setting The (possibly previously missing) new setting. + */ + void putSetting(Setting setting); - /** - * Have the fragment tell the containing Activity that a setting was modified. - */ - void onSettingChanged(); + /** + * Have the fragment tell the containing Activity that a setting was modified. + */ + void onSettingChanged(); - /** - * Have the fragment tell the containing Activity that a GCPad's setting was modified. - * - * @param key Identifier for the GCPad that was modified. - * @param value New setting for the GCPad. - */ - void onGcPadSettingChanged(String key, int value); + /** + * Have the fragment tell the containing Activity that a GCPad's setting was modified. + * + * @param key Identifier for the GCPad that was modified. + * @param value New setting for the GCPad. + */ + void onGcPadSettingChanged(String key, int value); - /** - * Have the fragment tell the containing Activity that a Wiimote's setting was modified. - * - * @param section Identifier for Wiimote that was modified; Wiimotes are identified by their section, - * not their key. - * @param value New setting for the Wiimote. - */ - void onWiimoteSettingChanged(String section, int value); + /** + * Have the fragment tell the containing Activity that a Wiimote's setting was modified. + * + * @param section Identifier for Wiimote that was modified; Wiimotes are identified by their section, + * not their key. + * @param value New setting for the Wiimote. + */ + void onWiimoteSettingChanged(String section, int value); - /** - * Have the fragment tell the containing Activity that an extension setting was modified. - * - * @param key Identifier for the extension that was modified. - * @param value New setting for the extension. - */ - void onExtensionSettingChanged(String key, int value); + /** + * Have the fragment tell the containing Activity that an extension setting was modified. + * + * @param key Identifier for the extension that was modified. + * @param value New setting for the extension. + */ + void onExtensionSettingChanged(String key, int value); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFrameLayout.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFrameLayout.java index 855397efd..297552674 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFrameLayout.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/SettingsFrameLayout.java @@ -7,50 +7,41 @@ import android.widget.FrameLayout; /** * FrameLayout subclass with few Properties added to simplify animations. */ -public final class SettingsFrameLayout extends FrameLayout -{ - private float mVisibleness = 1.0f; +public final class SettingsFrameLayout extends FrameLayout { + private float mVisibleness = 1.0f; - public SettingsFrameLayout(Context context) - { - super(context); - } + public SettingsFrameLayout(Context context) { + super(context); + } - public SettingsFrameLayout(Context context, AttributeSet attrs) - { - super(context, attrs); - } + public SettingsFrameLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } - public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) - { - super(context, attrs, defStyleAttr); - } + public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } - public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) - { - super(context, attrs, defStyleAttr, defStyleRes); - } + public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } - public float getYFraction() - { - return getY() / getHeight(); - } + public float getYFraction() { + return getY() / getHeight(); + } - public void setYFraction(float yFraction) - { - final int height = getHeight(); - setY((height > 0) ? (yFraction * height) : -9999); - } + public void setYFraction(float yFraction) { + final int height = getHeight(); + setY((height > 0) ? (yFraction * height) : -9999); + } - public float getVisibleness() - { - return mVisibleness; - } + public float getVisibleness() { + return mVisibleness; + } - public void setVisibleness(float visibleness) - { - setScaleX(visibleness); - setScaleY(visibleness); - setAlpha(visibleness); - } + public void setVisibleness(float visibleness) { + setScaleX(visibleness); + setScaleY(visibleness); + setAlpha(visibleness); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/CheckBoxSettingViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/CheckBoxSettingViewHolder.java index 9f0d4dc08..0059f9458 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/CheckBoxSettingViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/CheckBoxSettingViewHolder.java @@ -9,48 +9,42 @@ import org.citra.citra_android.model.settings.view.CheckBoxSetting; import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.ui.settings.SettingsAdapter; -public final class CheckBoxSettingViewHolder extends SettingViewHolder -{ - private CheckBoxSetting mItem; +public final class CheckBoxSettingViewHolder extends SettingViewHolder { + private CheckBoxSetting mItem; - private TextView mTextSettingName; - private TextView mTextSettingDescription; + private TextView mTextSettingName; + private TextView mTextSettingDescription; - private CheckBox mCheckbox; + private CheckBox mCheckbox; - public CheckBoxSettingViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView, adapter); - } + public CheckBoxSettingViewHolder(View itemView, SettingsAdapter adapter) { + super(itemView, adapter); + } - @Override - protected void findViews(View root) - { - mTextSettingName = root.findViewById(R.id.text_setting_name); - mTextSettingDescription = root.findViewById(R.id.text_setting_description); - mCheckbox = root.findViewById(R.id.checkbox); - } + @Override + protected void findViews(View root) { + mTextSettingName = root.findViewById(R.id.text_setting_name); + mTextSettingDescription = root.findViewById(R.id.text_setting_description); + mCheckbox = root.findViewById(R.id.checkbox); + } - @Override - public void bind(SettingsItem item) - { - mItem = (CheckBoxSetting) item; + @Override + public void bind(SettingsItem item) { + mItem = (CheckBoxSetting) item; - mTextSettingName.setText(item.getNameId()); + mTextSettingName.setText(item.getNameId()); - if (item.getDescriptionId() > 0) - { - mTextSettingDescription.setText(item.getDescriptionId()); - } + if (item.getDescriptionId() > 0) { + mTextSettingDescription.setText(item.getDescriptionId()); + } - mCheckbox.setChecked(mItem.isChecked()); - } + mCheckbox.setChecked(mItem.isChecked()); + } - @Override - public void onClick(View clicked) - { - mCheckbox.toggle(); + @Override + public void onClick(View clicked) { + mCheckbox.toggle(); - getAdapter().onBooleanClick(mItem, getAdapterPosition(), mCheckbox.isChecked()); - } + getAdapter().onBooleanClick(mItem, getAdapterPosition(), mCheckbox.isChecked()); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/DateTimeViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/DateTimeViewHolder.java index ab8ea8afd..e71d3e926 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/DateTimeViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/DateTimeViewHolder.java @@ -9,21 +9,18 @@ import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.ui.settings.SettingsAdapter; import org.citra.citra_android.utils.Log; -public final class DateTimeViewHolder extends SettingViewHolder -{ +public final class DateTimeViewHolder extends SettingViewHolder { private DateTimeSetting mItem; private TextView mTextSettingName; private TextView mTextSettingDescription; - public DateTimeViewHolder(View itemView, SettingsAdapter adapter) - { + public DateTimeViewHolder(View itemView, SettingsAdapter adapter) { super(itemView, adapter); } @Override - protected void findViews(View root) - { + protected void findViews(View root) { mTextSettingName = root.findViewById(R.id.text_setting_name); Log.error("test " + mTextSettingName); mTextSettingDescription = root.findViewById(R.id.text_setting_description); @@ -31,19 +28,16 @@ public final class DateTimeViewHolder extends SettingViewHolder } @Override - public void bind(SettingsItem item) - { + public void bind(SettingsItem item) { mItem = (DateTimeSetting) item; mTextSettingName.setText(item.getNameId()); - if (item.getDescriptionId() > 0) - { + if (item.getDescriptionId() > 0) { mTextSettingDescription.setText(item.getDescriptionId()); } } @Override - public void onClick(View clicked) - { + public void onClick(View clicked) { getAdapter().onDateTimeClick(mItem); } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/HeaderViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/HeaderViewHolder.java index a01e94051..131df134b 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/HeaderViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/HeaderViewHolder.java @@ -7,31 +7,26 @@ import org.citra.citra_android.R; import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.ui.settings.SettingsAdapter; -public final class HeaderViewHolder extends SettingViewHolder -{ - private TextView mHeaderName; +public final class HeaderViewHolder extends SettingViewHolder { + private TextView mHeaderName; - public HeaderViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView, adapter); - itemView.setOnClickListener(null); - } + public HeaderViewHolder(View itemView, SettingsAdapter adapter) { + super(itemView, adapter); + itemView.setOnClickListener(null); + } - @Override - protected void findViews(View root) - { - mHeaderName = root.findViewById(R.id.text_header_name); - } + @Override + protected void findViews(View root) { + mHeaderName = root.findViewById(R.id.text_header_name); + } - @Override - public void bind(SettingsItem item) - { - mHeaderName.setText(item.getNameId()); - } + @Override + public void bind(SettingsItem item) { + mHeaderName.setText(item.getNameId()); + } - @Override - public void onClick(View clicked) - { - // no-op - } + @Override + public void onClick(View clicked) { + // no-op + } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/InputBindingSettingViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/InputBindingSettingViewHolder.java index 19cb0355e..a9fb87824 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/InputBindingSettingViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/InputBindingSettingViewHolder.java @@ -11,43 +11,38 @@ import org.citra.citra_android.model.settings.view.InputBindingSetting; import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.ui.settings.SettingsAdapter; -public final class InputBindingSettingViewHolder extends SettingViewHolder -{ - private InputBindingSetting mItem; +public final class InputBindingSettingViewHolder extends SettingViewHolder { + private InputBindingSetting mItem; - private TextView mTextSettingName; - private TextView mTextSettingDescription; + private TextView mTextSettingName; + private TextView mTextSettingDescription; - private Context mContext; + private Context mContext; - public InputBindingSettingViewHolder(View itemView, SettingsAdapter adapter, Context context) - { - super(itemView, adapter); + public InputBindingSettingViewHolder(View itemView, SettingsAdapter adapter, Context context) { + super(itemView, adapter); - mContext = context; - } + mContext = context; + } - @Override - protected void findViews(View root) - { - mTextSettingName = root.findViewById(R.id.text_setting_name); - mTextSettingDescription = root.findViewById(R.id.text_setting_description); - } + @Override + protected void findViews(View root) { + mTextSettingName = root.findViewById(R.id.text_setting_name); + mTextSettingDescription = root.findViewById(R.id.text_setting_description); + } - @Override - public void bind(SettingsItem item) - { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); + @Override + public void bind(SettingsItem item) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - mItem = (InputBindingSetting) item; + mItem = (InputBindingSetting) item; - mTextSettingName.setText(item.getNameId()); - mTextSettingDescription.setText(sharedPreferences.getString(mItem.getKey(), "")); - } + mTextSettingName.setText(item.getNameId()); + mTextSettingDescription.setText(sharedPreferences.getString(mItem.getKey(), "")); + } - @Override - public void onClick(View clicked) - { - getAdapter().onInputBindingClick(mItem, getAdapterPosition()); - } + @Override + public void onClick(View clicked) { + getAdapter().onInputBindingClick(mItem, getAdapterPosition()); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SettingViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SettingViewHolder.java index 0824ae468..cb3f427a2 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SettingViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SettingViewHolder.java @@ -6,46 +6,43 @@ import android.view.View; import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.ui.settings.SettingsAdapter; -public abstract class SettingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener -{ - private SettingsAdapter mAdapter; +public abstract class SettingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + private SettingsAdapter mAdapter; - public SettingViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView); + public SettingViewHolder(View itemView, SettingsAdapter adapter) { + super(itemView); - mAdapter = adapter; + mAdapter = adapter; - itemView.setOnClickListener(this); + itemView.setOnClickListener(this); - findViews(itemView); - } + findViews(itemView); + } - protected SettingsAdapter getAdapter() - { - return mAdapter; - } + protected SettingsAdapter getAdapter() { + return mAdapter; + } - /** - * Gets handles to all this ViewHolder's child views using their XML-defined identifiers. - * - * @param root The newly inflated top-level view. - */ - protected abstract void findViews(View root); + /** + * Gets handles to all this ViewHolder's child views using their XML-defined identifiers. + * + * @param root The newly inflated top-level view. + */ + protected abstract void findViews(View root); - /** - * Called by the adapter to set this ViewHolder's child views to display the list item - * it must now represent. - * - * @param item The list item that should be represented by this ViewHolder. - */ - public abstract void bind(SettingsItem item); + /** + * Called by the adapter to set this ViewHolder's child views to display the list item + * it must now represent. + * + * @param item The list item that should be represented by this ViewHolder. + */ + public abstract void bind(SettingsItem item); - /** - * Called when this ViewHolder's view is clicked on. Implementations should usually pass - * this event up to the adapter. - * - * @param clicked The view that was clicked on. - */ - public abstract void onClick(View clicked); + /** + * Called when this ViewHolder's view is clicked on. Implementations should usually pass + * this event up to the adapter. + * + * @param clicked The view that was clicked on. + */ + public abstract void onClick(View clicked); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SingleChoiceViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SingleChoiceViewHolder.java index 8eb09d5bc..fd7cc339a 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SingleChoiceViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SingleChoiceViewHolder.java @@ -8,41 +8,35 @@ import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.model.settings.view.SingleChoiceSetting; import org.citra.citra_android.ui.settings.SettingsAdapter; -public final class SingleChoiceViewHolder extends SettingViewHolder -{ - private SingleChoiceSetting mItem; +public final class SingleChoiceViewHolder extends SettingViewHolder { + private SingleChoiceSetting mItem; - private TextView mTextSettingName; - private TextView mTextSettingDescription; + private TextView mTextSettingName; + private TextView mTextSettingDescription; - public SingleChoiceViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView, adapter); - } + public SingleChoiceViewHolder(View itemView, SettingsAdapter adapter) { + super(itemView, adapter); + } - @Override - protected void findViews(View root) - { - mTextSettingName = root.findViewById(R.id.text_setting_name); - mTextSettingDescription = root.findViewById(R.id.text_setting_description); - } + @Override + protected void findViews(View root) { + mTextSettingName = root.findViewById(R.id.text_setting_name); + mTextSettingDescription = root.findViewById(R.id.text_setting_description); + } - @Override - public void bind(SettingsItem item) - { - mItem = (SingleChoiceSetting) item; + @Override + public void bind(SettingsItem item) { + mItem = (SingleChoiceSetting) item; - mTextSettingName.setText(item.getNameId()); + mTextSettingName.setText(item.getNameId()); - if (item.getDescriptionId() > 0) - { - mTextSettingDescription.setText(item.getDescriptionId()); - } - } + if (item.getDescriptionId() > 0) { + mTextSettingDescription.setText(item.getDescriptionId()); + } + } - @Override - public void onClick(View clicked) - { - getAdapter().onSingleChoiceClick(mItem); - } + @Override + public void onClick(View clicked) { + getAdapter().onSingleChoiceClick(mItem); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SliderViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SliderViewHolder.java index dafed6007..8b704d442 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SliderViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SliderViewHolder.java @@ -8,42 +8,36 @@ import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.model.settings.view.SliderSetting; import org.citra.citra_android.ui.settings.SettingsAdapter; -public final class SliderViewHolder extends SettingViewHolder -{ - private SliderSetting mItem; +public final class SliderViewHolder extends SettingViewHolder { + private SliderSetting mItem; - private TextView mTextSettingName; - private TextView mTextSettingDescription; + private TextView mTextSettingName; + private TextView mTextSettingDescription; - public SliderViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView, adapter); - } + public SliderViewHolder(View itemView, SettingsAdapter adapter) { + super(itemView, adapter); + } - @Override - protected void findViews(View root) - { - mTextSettingName = root.findViewById(R.id.text_setting_name); - mTextSettingDescription = root.findViewById(R.id.text_setting_description); - } + @Override + protected void findViews(View root) { + mTextSettingName = root.findViewById(R.id.text_setting_name); + mTextSettingDescription = root.findViewById(R.id.text_setting_description); + } - @Override - public void bind(SettingsItem item) - { - mItem = (SliderSetting) item; + @Override + public void bind(SettingsItem item) { + mItem = (SliderSetting) item; - mTextSettingName.setText(item.getNameId()); + mTextSettingName.setText(item.getNameId()); - if (item.getDescriptionId() > 0) - { - mTextSettingDescription.setText(item.getDescriptionId()); - } - } + if (item.getDescriptionId() > 0) { + mTextSettingDescription.setText(item.getDescriptionId()); + } + } - @Override - public void onClick(View clicked) - { - getAdapter().onSliderClick(mItem); - } + @Override + public void onClick(View clicked) { + getAdapter().onSliderClick(mItem); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SubmenuViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SubmenuViewHolder.java index c28ba1d95..fde19b01f 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SubmenuViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/settings/viewholder/SubmenuViewHolder.java @@ -8,41 +8,35 @@ import org.citra.citra_android.model.settings.view.SettingsItem; import org.citra.citra_android.model.settings.view.SubmenuSetting; import org.citra.citra_android.ui.settings.SettingsAdapter; -public final class SubmenuViewHolder extends SettingViewHolder -{ - private SubmenuSetting mItem; +public final class SubmenuViewHolder extends SettingViewHolder { + private SubmenuSetting mItem; - private TextView mTextSettingName; - private TextView mTextSettingDescription; + private TextView mTextSettingName; + private TextView mTextSettingDescription; - public SubmenuViewHolder(View itemView, SettingsAdapter adapter) - { - super(itemView, adapter); - } + public SubmenuViewHolder(View itemView, SettingsAdapter adapter) { + super(itemView, adapter); + } - @Override - protected void findViews(View root) - { - mTextSettingName = root.findViewById(R.id.text_setting_name); - mTextSettingDescription = root.findViewById(R.id.text_setting_description); - } + @Override + protected void findViews(View root) { + mTextSettingName = root.findViewById(R.id.text_setting_name); + mTextSettingDescription = root.findViewById(R.id.text_setting_description); + } - @Override - public void bind(SettingsItem item) - { - mItem = (SubmenuSetting) item; + @Override + public void bind(SettingsItem item) { + mItem = (SubmenuSetting) item; - mTextSettingName.setText(item.getNameId()); + mTextSettingName.setText(item.getNameId()); - if (item.getDescriptionId() > 0) - { - mTextSettingDescription.setText(item.getDescriptionId()); - } - } + if (item.getDescriptionId() > 0) { + mTextSettingDescription.setText(item.getDescriptionId()); + } + } - @Override - public void onClick(View clicked) - { - getAdapter().onSubmenuClick(mItem); - } + @Override + public void onClick(View clicked) { + getAdapter().onSubmenuClick(mItem); + } } \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/AddDirectoryHelper.java b/src/android/app/src/main/java/org/citra/citra_android/utils/AddDirectoryHelper.java index 2096a1111..42a377cf2 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/AddDirectoryHelper.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/AddDirectoryHelper.java @@ -8,37 +8,31 @@ import android.net.Uri; import org.citra.citra_android.model.GameDatabase; import org.citra.citra_android.model.GameProvider; -public class AddDirectoryHelper -{ - private Context mContext; +public class AddDirectoryHelper { + private Context mContext; - public interface AddDirectoryListener - { - void onDirectoryAdded(); - } + public AddDirectoryHelper(Context context) { + this.mContext = context; + } - public AddDirectoryHelper(Context context) - { - this.mContext = context; - } + public void addDirectory(String dir, AddDirectoryListener addDirectoryListener) { + AsyncQueryHandler handler = new AsyncQueryHandler(mContext.getContentResolver()) { + @Override + protected void onInsertComplete(int token, Object cookie, Uri uri) { + addDirectoryListener.onDirectoryAdded(); + } + }; - public void addDirectory(String dir, AddDirectoryListener addDirectoryListener) - { - AsyncQueryHandler handler = new AsyncQueryHandler(mContext.getContentResolver()) - { - @Override - protected void onInsertComplete(int token, Object cookie, Uri uri) - { - addDirectoryListener.onDirectoryAdded(); - } - }; + ContentValues file = new ContentValues(); + file.put(GameDatabase.KEY_FOLDER_PATH, dir); - ContentValues file = new ContentValues(); - file.put(GameDatabase.KEY_FOLDER_PATH, dir); + handler.startInsert(0, // We don't need to identify this call to the handler + null, // We don't need to pass additional data to the handler + GameProvider.URI_FOLDER, // Tell the GameProvider we are adding a folder + file); + } - handler.startInsert(0, // We don't need to identify this call to the handler - null, // We don't need to pass additional data to the handler - GameProvider.URI_FOLDER, // Tell the GameProvider we are adding a folder - file); - } + public interface AddDirectoryListener { + void onDirectoryAdded(); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/Animations.java b/src/android/app/src/main/java/org/citra/citra_android/utils/Animations.java index 8633a08ba..b0aca6235 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/Animations.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/Animations.java @@ -3,27 +3,23 @@ package org.citra.citra_android.utils; import android.view.View; import android.view.ViewPropertyAnimator; -public final class Animations -{ - private Animations() - { - } +public final class Animations { + private Animations() { + } - public static ViewPropertyAnimator fadeViewIn(View view) - { - view.setVisibility(View.VISIBLE); + public static ViewPropertyAnimator fadeViewIn(View view) { + view.setVisibility(View.VISIBLE); - return view.animate() - .withLayer() - .setDuration(100) - .alpha(1.0f); - } + return view.animate() + .withLayer() + .setDuration(100) + .alpha(1.0f); + } - public static ViewPropertyAnimator fadeViewOut(View view) - { - return view.animate() - .withLayer() - .setDuration(300) - .alpha(0.0f); - } + public static ViewPropertyAnimator fadeViewOut(View view) { + return view.animate() + .withLayer() + .setDuration(300) + .alpha(0.0f); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/ControllerMappingHelper.java b/src/android/app/src/main/java/org/citra/citra_android/utils/ControllerMappingHelper.java index 2b703ec8a..45f9eb278 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/ControllerMappingHelper.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/ControllerMappingHelper.java @@ -7,77 +7,61 @@ import android.view.MotionEvent; /** * Some controllers have incorrect mappings. This class has special-case fixes for them. */ -public class ControllerMappingHelper -{ - /** - * Some controllers report extra button presses that can be ignored. - */ - public boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode) - { - if (isDualShock4(inputDevice)) - { - // The two analog triggers generate analog motion events as well as a keycode. - // We always prefer to use the analog values, so throw away the button press - // Even though the triggers are L/R2, without mappings they generate L/R1 events. - return keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_R1; +public class ControllerMappingHelper { + /** + * Some controllers report extra button presses that can be ignored. + */ + public boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode) { + if (isDualShock4(inputDevice)) { + // The two analog triggers generate analog motion events as well as a keycode. + // We always prefer to use the analog values, so throw away the button press + // Even though the triggers are L/R2, without mappings they generate L/R1 events. + return keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_R1; + } + return false; } - return false; - } - /** - * Scale an axis to be zero-centered with a proper range. - */ - public float scaleAxis(InputDevice inputDevice, int axis, float value) - { - if (isDualShock4(inputDevice)) - { - // Android doesn't have correct mappings for this controller's triggers. It reports them - // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0] - // Scale them to properly zero-centered with a range of [0.0, 1.0]. - if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) - { - return (value + 1) / 2.0f; - } + /** + * Scale an axis to be zero-centered with a proper range. + */ + public float scaleAxis(InputDevice inputDevice, int axis, float value) { + if (isDualShock4(inputDevice)) { + // Android doesn't have correct mappings for this controller's triggers. It reports them + // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0] + // Scale them to properly zero-centered with a range of [0.0, 1.0]. + if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) { + return (value + 1) / 2.0f; + } + } else if (isXboxOneWireless(inputDevice)) { + // Same as the DualShock 4, the mappings are missing. + if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) { + return (value + 1) / 2.0f; + } + if (axis == MotionEvent.AXIS_GENERIC_1) { + // This axis is stuck at ~.5. Ignore it. + return 0.0f; + } + } else if (isMogaPro2Hid(inputDevice)) { + // This controller has a broken axis that reports a constant value. Ignore it. + if (axis == MotionEvent.AXIS_GENERIC_1) { + return 0.0f; + } + } + return value; } - else if (isXboxOneWireless(inputDevice)) - { - // Same as the DualShock 4, the mappings are missing. - if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) - { - return (value + 1) / 2.0f; - } - if (axis == MotionEvent.AXIS_GENERIC_1) - { - // This axis is stuck at ~.5. Ignore it. - return 0.0f; - } + + private boolean isDualShock4(InputDevice inputDevice) { + // Sony DualShock 4 controller + return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc; } - else if (isMogaPro2Hid(inputDevice)) - { - // This controller has a broken axis that reports a constant value. Ignore it. - if (axis == MotionEvent.AXIS_GENERIC_1) - { - return 0.0f; - } + + private boolean isXboxOneWireless(InputDevice inputDevice) { + // Microsoft Xbox One controller + return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0; } - return value; - } - private boolean isDualShock4(InputDevice inputDevice) - { - // Sony DualShock 4 controller - return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc; - } - - private boolean isXboxOneWireless(InputDevice inputDevice) - { - // Microsoft Xbox One controller - return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0; - } - - private boolean isMogaPro2Hid(InputDevice inputDevice) - { - // Moga Pro 2 HID - return inputDevice.getVendorId() == 0x20d6 && inputDevice.getProductId() == 0x6271; - } + private boolean isMogaPro2Hid(InputDevice inputDevice) { + // Moga Pro 2 HID + return inputDevice.getVendorId() == 0x20d6 && inputDevice.getProductId() == 0x6271; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/DirectoryStateReceiver.java b/src/android/app/src/main/java/org/citra/citra_android/utils/DirectoryStateReceiver.java index f3182d532..a30696b3d 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/DirectoryStateReceiver.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/DirectoryStateReceiver.java @@ -9,20 +9,17 @@ import org.citra.citra_android.services.DirectoryInitializationService.Directory import rx.functions.Action1; -public class DirectoryStateReceiver extends BroadcastReceiver -{ - Action1 callback; +public class DirectoryStateReceiver extends BroadcastReceiver { + Action1 callback; - public DirectoryStateReceiver(Action1 callback) - { - this.callback = callback; - } + public DirectoryStateReceiver(Action1 callback) { + this.callback = callback; + } - @Override - public void onReceive(Context context, Intent intent) - { - DirectoryInitializationState state = (DirectoryInitializationState) intent - .getSerializableExtra(DirectoryInitializationService.EXTRA_STATE); - callback.call(state); - } + @Override + public void onReceive(Context context, Intent intent) { + DirectoryInitializationState state = (DirectoryInitializationState) intent + .getSerializableExtra(DirectoryInitializationService.EXTRA_STATE); + callback.call(state); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/EGLHelper.java b/src/android/app/src/main/java/org/citra/citra_android/utils/EGLHelper.java index b82bd9542..003ffd136 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/EGLHelper.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/EGLHelper.java @@ -22,368 +22,332 @@ import javax.microedition.khronos.opengles.GL10; * EGL initialization out of the way if all that is * wanted is to query the underlying GL API for information. */ -public final class EGLHelper -{ - private final EGL10 mEGL; - private final EGLDisplay mDisplay; - private EGLConfig[] mEGLConfigs; - private EGLContext mEGLContext; - private EGLSurface mEGLSurface; - private GL10 mGL; +public final class EGLHelper { + // Renderable type bitmasks + public static final int EGL_OPENGL_ES_BIT = 0x0001; + public static final int EGL_OPENGL_ES2_BIT = 0x0004; + public static final int EGL_OPENGL_BIT = 0x0008; + public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040; + // API types + public static final int EGL_OPENGL_ES_API = 0x30A0; + public static final int EGL_OPENGL_API = 0x30A2; + private final EGL10 mEGL; + private final EGLDisplay mDisplay; + private EGLConfig[] mEGLConfigs; + private EGLContext mEGLContext; + private EGLSurface mEGLSurface; + private GL10 mGL; + // GL support flags + private boolean supportGL; + private boolean supportGLES2; + private boolean supportGLES3; - // GL support flags - private boolean supportGL; - private boolean supportGLES2; - private boolean supportGLES3; - - // Renderable type bitmasks - public static final int EGL_OPENGL_ES_BIT = 0x0001; - public static final int EGL_OPENGL_ES2_BIT = 0x0004; - public static final int EGL_OPENGL_BIT = 0x0008; - public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040; - - // API types - public static final int EGL_OPENGL_ES_API = 0x30A0; - public static final int EGL_OPENGL_API = 0x30A2; - - /** - * Constructor - *

- * Initializes the underlying {@link EGLSurface} with a width and height of 1. - * This is useful if all you need to use this class for is to query information - * from specific API contexts. - * - * @param renderableType Bitmask indicating which types of client API contexts - * the framebuffer config must support. - */ - public EGLHelper(int renderableType) - { - this(1, 1, renderableType); - } - - /** - * Constructor - * - * @param width Width of the underlying {@link EGLSurface}. - * @param height Height of the underlying {@link EGLSurface}. - * @param renderableType Bitmask indicating which types of client API contexts - * the framebuffer config must support. - */ - public EGLHelper(int width, int height, int renderableType) - { - // Initialize handle to an EGL display. - mEGL = (EGL10) EGLContext.getEGL(); - mDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); - - // If a display is present, initialize EGL. - if (mDisplay != EGL10.EGL_NO_DISPLAY) - { - int[] version = new int[2]; - if (mEGL.eglInitialize(mDisplay, version)) - { - // Detect supported GL APIs, initialize configs, etc. - detect(); - - // Create context and surface - create(width, height, renderableType); - } - else - { - Log.error("[EGLHelper] Error initializing EGL."); - } - } - else - { - Log.error("[EGLHelper] Error initializing EGL display."); - } - } - - /** - * Releases all resources associated with this helper. - *

- * This should be called whenever this helper is no longer needed. - */ - public void closeHelper() - { - mEGL.eglTerminate(mDisplay); - } - - /** - * Gets information through EGL.
- *

- * Index 0: Vendor
- * Index 1: Version
- * Index 2: Renderer
- * Index 3: Extensions
- * - * @return information retrieved through EGL. - */ - public String[] getEGLInfo() - { - return new String[]{ - mGL.glGetString(GL10.GL_VENDOR), - mGL.glGetString(GL10.GL_VERSION), - mGL.glGetString(GL10.GL_RENDERER), - mGL.glGetString(GL10.GL_EXTENSIONS), - }; - } - - /** - * Whether or not this device supports OpenGL. - * - * @return true if this device supports OpenGL; false otherwise. - */ - public boolean supportsOpenGL() - { - return supportGL; - } - - /** - * Whether or not this device supports OpenGL ES 2. - *
- * Note that if this returns true, then OpenGL ES 1 is also supported. - * - * @return true if this device supports OpenGL ES 2; false otherwise. - */ - public boolean supportsGLES2() - { - return supportGLES2; - } - - /** - * Whether or not this device supports OpenGL ES 3. - *
- * Note that if this returns true, then OpenGL ES 1 and 2 are also supported. - * - * @return true if this device supports OpenGL ES 3; false otherwise. - */ - public boolean supportsGLES3() - { - return supportGLES3; - } - - /** - * Gets the underlying {@link EGL10} instance. - * - * @return the underlying {@link EGL10} instance. - */ - public EGL10 getEGL() - { - return mEGL; - } - - /** - * Gets the underlying {@link GL10} instance. - * - * @return the underlying {@link GL10} instance. - */ - public GL10 getGL() - { - return mGL; - } - - /** - * Gets the underlying {@link EGLDisplay}. - * - * @return the underlying {@link EGLDisplay} - */ - public EGLDisplay getDisplay() - { - return mDisplay; - } - - /** - * Gets all supported framebuffer configurations for this device. - * - * @return all supported framebuffer configurations for this device. - */ - public EGLConfig[] getConfigs() - { - return mEGLConfigs; - } - - /** - * Gets the underlying {@link EGLContext}. - * - * @return the underlying {@link EGLContext}. - */ - public EGLContext getContext() - { - return mEGLContext; - } - - /** - * Gets the underlying {@link EGLSurface}. - * - * @return the underlying {@link EGLSurface}. - */ - public EGLSurface getSurface() - { - return mEGLSurface; - } - - // Detects the specific kind of GL modes that are supported - private boolean detect() - { - // Get total number of configs available. - int[] numConfigs = new int[1]; - if (!mEGL.eglGetConfigs(mDisplay, null, 0, numConfigs)) - { - Log.error("[EGLHelper] Error retrieving number of EGL configs available."); - return false; + /** + * Constructor + *

+ * Initializes the underlying {@link EGLSurface} with a width and height of 1. + * This is useful if all you need to use this class for is to query information + * from specific API contexts. + * + * @param renderableType Bitmask indicating which types of client API contexts + * the framebuffer config must support. + */ + public EGLHelper(int renderableType) { + this(1, 1, renderableType); } - // Now get all the configurations - mEGLConfigs = new EGLConfig[numConfigs[0]]; - if (!mEGL.eglGetConfigs(mDisplay, mEGLConfigs, mEGLConfigs.length, numConfigs)) - { - Log.error("[EGLHelper] Error retrieving all EGL configs."); - return false; + /** + * Constructor + * + * @param width Width of the underlying {@link EGLSurface}. + * @param height Height of the underlying {@link EGLSurface}. + * @param renderableType Bitmask indicating which types of client API contexts + * the framebuffer config must support. + */ + public EGLHelper(int width, int height, int renderableType) { + // Initialize handle to an EGL display. + mEGL = (EGL10) EGLContext.getEGL(); + mDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); + + // If a display is present, initialize EGL. + if (mDisplay != EGL10.EGL_NO_DISPLAY) { + int[] version = new int[2]; + if (mEGL.eglInitialize(mDisplay, version)) { + // Detect supported GL APIs, initialize configs, etc. + detect(); + + // Create context and surface + create(width, height, renderableType); + } else { + Log.error("[EGLHelper] Error initializing EGL."); + } + } else { + Log.error("[EGLHelper] Error initializing EGL display."); + } } - for (EGLConfig mEGLConfig : mEGLConfigs) - { - int[] attribVal = new int[1]; - boolean ret = - mEGL.eglGetConfigAttrib(mDisplay, mEGLConfig, EGL10.EGL_RENDERABLE_TYPE, attribVal); - if (ret) - { - if ((attribVal[0] & EGL_OPENGL_BIT) != 0) - supportGL = true; - - if ((attribVal[0] & EGL_OPENGL_ES2_BIT) != 0) - supportGLES2 = true; - - if ((attribVal[0] & EGL_OPENGL_ES3_BIT_KHR) != 0) - supportGLES3 = true; - } + /** + * Releases all resources associated with this helper. + *

+ * This should be called whenever this helper is no longer needed. + */ + public void closeHelper() { + mEGL.eglTerminate(mDisplay); } - return true; - } - - // Creates the context and surface. - private void create(int width, int height, int renderableType) - { - int[] attribs = { - EGL10.EGL_WIDTH, width, - EGL10.EGL_HEIGHT, height, - EGL10.EGL_NONE - }; - - // Initially we just assume GLES2 will be the default context. - int EGL_CONTEXT_CLIENT_VERSION = 0x3098; - int[] ctx_attribs = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL10.EGL_NONE - }; - - // Determine the type of context that will be created - // and change the attribute arrays accordingly. - switch (renderableType) - { - case EGL_OPENGL_ES_BIT: - ctx_attribs[1] = 1; - break; - - case EGL_OPENGL_BIT: - ctx_attribs[0] = EGL10.EGL_NONE; - break; - - case EGL_OPENGL_ES3_BIT_KHR: - ctx_attribs[1] = 3; - break; - - case EGL_OPENGL_ES2_BIT: - default: // Fall-back to GLES 2. - ctx_attribs[1] = 2; - break; + /** + * Gets information through EGL.
+ *

+ * Index 0: Vendor
+ * Index 1: Version
+ * Index 2: Renderer
+ * Index 3: Extensions
+ * + * @return information retrieved through EGL. + */ + public String[] getEGLInfo() { + return new String[]{ + mGL.glGetString(GL10.GL_VENDOR), + mGL.glGetString(GL10.GL_VERSION), + mGL.glGetString(GL10.GL_RENDERER), + mGL.glGetString(GL10.GL_EXTENSIONS), + }; } - if (renderableType == EGL_OPENGL_BIT) - NativeLibrary.eglBindAPI(EGL_OPENGL_API); - else - NativeLibrary.eglBindAPI(EGL_OPENGL_ES_API); - mEGLContext = - mEGL.eglCreateContext(mDisplay, mEGLConfigs[0], EGL10.EGL_NO_CONTEXT, ctx_attribs); - mEGLSurface = mEGL.eglCreatePbufferSurface(mDisplay, mEGLConfigs[0], attribs); - mEGL.eglMakeCurrent(mDisplay, mEGLSurface, mEGLSurface, mEGLContext); - mGL = (GL10) mEGLContext.getGL(); - } + /** + * Whether or not this device supports OpenGL. + * + * @return true if this device supports OpenGL; false otherwise. + */ + public boolean supportsOpenGL() { + return supportGL; + } - /** - * Simplified call to {@link GL10#glGetString(int)} - *

- * Accepts the following constants: - *

    - *
  • GL_VENDOR - Company responsible for the GL implementation.
  • - *
  • GL_VERSION - Version or release number.
  • - *
  • GL_RENDERER - Name of the renderer
  • - *
  • GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language
  • - *
- * - * @param glEnum A symbolic constant within {@link GL10}. - * @return the string information represented by {@code glEnum}. - */ - public String glGetString(int glEnum) - { - return mGL.glGetString(glEnum); - } + /** + * Whether or not this device supports OpenGL ES 2. + *
+ * Note that if this returns true, then OpenGL ES 1 is also supported. + * + * @return true if this device supports OpenGL ES 2; false otherwise. + */ + public boolean supportsGLES2() { + return supportGLES2; + } - /** - * Simplified call to {@link GLES30#glGetStringi(int, int)} - *

- * Accepts the following constants: - *

    - *
  • GL_VENDOR - Company responsible for the GL implementation.
  • - *
  • GL_VERSION - Version or release number.
  • - *
  • GL_RENDERER - Name of the renderer
  • - *
  • GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language
  • - *
  • GL_EXTENSIONS - Extension string supported by the implementation at {@code index}.
  • - *
- * - * @param glEnum A symbolic GL constant - * @param index The index of the string to return. - * @return the string information represented by {@code glEnum} and {@code index}. - */ - public String glGetStringi(int glEnum, int index) - { - return GLES30.glGetStringi(glEnum, index); - } + /** + * Whether or not this device supports OpenGL ES 3. + *
+ * Note that if this returns true, then OpenGL ES 1 and 2 are also supported. + * + * @return true if this device supports OpenGL ES 3; false otherwise. + */ + public boolean supportsGLES3() { + return supportGLES3; + } - public boolean SupportsExtension(String extension) - { - int[] num_ext = new int[1]; - GLES30.glGetIntegerv(GLES30.GL_NUM_EXTENSIONS, num_ext, 0); + /** + * Gets the underlying {@link EGL10} instance. + * + * @return the underlying {@link EGL10} instance. + */ + public EGL10 getEGL() { + return mEGL; + } + + /** + * Gets the underlying {@link GL10} instance. + * + * @return the underlying {@link GL10} instance. + */ + public GL10 getGL() { + return mGL; + } + + /** + * Gets the underlying {@link EGLDisplay}. + * + * @return the underlying {@link EGLDisplay} + */ + public EGLDisplay getDisplay() { + return mDisplay; + } + + /** + * Gets all supported framebuffer configurations for this device. + * + * @return all supported framebuffer configurations for this device. + */ + public EGLConfig[] getConfigs() { + return mEGLConfigs; + } + + /** + * Gets the underlying {@link EGLContext}. + * + * @return the underlying {@link EGLContext}. + */ + public EGLContext getContext() { + return mEGLContext; + } + + /** + * Gets the underlying {@link EGLSurface}. + * + * @return the underlying {@link EGLSurface}. + */ + public EGLSurface getSurface() { + return mEGLSurface; + } + + // Detects the specific kind of GL modes that are supported + private boolean detect() { + // Get total number of configs available. + int[] numConfigs = new int[1]; + if (!mEGL.eglGetConfigs(mDisplay, null, 0, numConfigs)) { + Log.error("[EGLHelper] Error retrieving number of EGL configs available."); + return false; + } + + // Now get all the configurations + mEGLConfigs = new EGLConfig[numConfigs[0]]; + if (!mEGL.eglGetConfigs(mDisplay, mEGLConfigs, mEGLConfigs.length, numConfigs)) { + Log.error("[EGLHelper] Error retrieving all EGL configs."); + return false; + } + + for (EGLConfig mEGLConfig : mEGLConfigs) { + int[] attribVal = new int[1]; + boolean ret = + mEGL.eglGetConfigAttrib(mDisplay, mEGLConfig, EGL10.EGL_RENDERABLE_TYPE, attribVal); + if (ret) { + if ((attribVal[0] & EGL_OPENGL_BIT) != 0) + supportGL = true; + + if ((attribVal[0] & EGL_OPENGL_ES2_BIT) != 0) + supportGLES2 = true; + + if ((attribVal[0] & EGL_OPENGL_ES3_BIT_KHR) != 0) + supportGLES3 = true; + } + } - for (int i = 0; i < num_ext[0]; ++i) - { - String ext = GLES30.glGetStringi(GLES30.GL_EXTENSIONS, i); - if (ext.equals(extension)) return true; } - return false; - } - public int GetVersion() - { - int[] major = new int[1]; - int[] minor = new int[1]; - GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, major, 0); - GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, minor, 0); - return major[0] * 100 + minor[0] * 10; - } + // Creates the context and surface. + private void create(int width, int height, int renderableType) { + int[] attribs = { + EGL10.EGL_WIDTH, width, + EGL10.EGL_HEIGHT, height, + EGL10.EGL_NONE + }; - /** - * Simplified call to {@link GL10#glGetIntegerv(int, int[], int) - * - * @param glEnum A symbolic GL constant. - * @return the integer information represented by {@code glEnum}. - */ - public int glGetInteger(int glEnum) - { - int[] val = new int[1]; - mGL.glGetIntegerv(glEnum, val, 0); - return val[0]; - } + // Initially we just assume GLES2 will be the default context. + int EGL_CONTEXT_CLIENT_VERSION = 0x3098; + int[] ctx_attribs = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL10.EGL_NONE + }; + + // Determine the type of context that will be created + // and change the attribute arrays accordingly. + switch (renderableType) { + case EGL_OPENGL_ES_BIT: + ctx_attribs[1] = 1; + break; + + case EGL_OPENGL_BIT: + ctx_attribs[0] = EGL10.EGL_NONE; + break; + + case EGL_OPENGL_ES3_BIT_KHR: + ctx_attribs[1] = 3; + break; + + case EGL_OPENGL_ES2_BIT: + default: // Fall-back to GLES 2. + ctx_attribs[1] = 2; + break; + } + if (renderableType == EGL_OPENGL_BIT) + NativeLibrary.eglBindAPI(EGL_OPENGL_API); + else + NativeLibrary.eglBindAPI(EGL_OPENGL_ES_API); + + mEGLContext = + mEGL.eglCreateContext(mDisplay, mEGLConfigs[0], EGL10.EGL_NO_CONTEXT, ctx_attribs); + mEGLSurface = mEGL.eglCreatePbufferSurface(mDisplay, mEGLConfigs[0], attribs); + mEGL.eglMakeCurrent(mDisplay, mEGLSurface, mEGLSurface, mEGLContext); + mGL = (GL10) mEGLContext.getGL(); + } + + /** + * Simplified call to {@link GL10#glGetString(int)} + *

+ * Accepts the following constants: + *

    + *
  • GL_VENDOR - Company responsible for the GL implementation.
  • + *
  • GL_VERSION - Version or release number.
  • + *
  • GL_RENDERER - Name of the renderer
  • + *
  • GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language
  • + *
+ * + * @param glEnum A symbolic constant within {@link GL10}. + * @return the string information represented by {@code glEnum}. + */ + public String glGetString(int glEnum) { + return mGL.glGetString(glEnum); + } + + /** + * Simplified call to {@link GLES30#glGetStringi(int, int)} + *

+ * Accepts the following constants: + *

    + *
  • GL_VENDOR - Company responsible for the GL implementation.
  • + *
  • GL_VERSION - Version or release number.
  • + *
  • GL_RENDERER - Name of the renderer
  • + *
  • GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language
  • + *
  • GL_EXTENSIONS - Extension string supported by the implementation at {@code index}.
  • + *
+ * + * @param glEnum A symbolic GL constant + * @param index The index of the string to return. + * @return the string information represented by {@code glEnum} and {@code index}. + */ + public String glGetStringi(int glEnum, int index) { + return GLES30.glGetStringi(glEnum, index); + } + + public boolean SupportsExtension(String extension) { + int[] num_ext = new int[1]; + GLES30.glGetIntegerv(GLES30.GL_NUM_EXTENSIONS, num_ext, 0); + + for (int i = 0; i < num_ext[0]; ++i) { + String ext = GLES30.glGetStringi(GLES30.GL_EXTENSIONS, i); + if (ext.equals(extension)) + return true; + } + return false; + } + + public int GetVersion() { + int[] major = new int[1]; + int[] minor = new int[1]; + GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, major, 0); + GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, minor, 0); + return major[0] * 100 + minor[0] * 10; + } + + /** + * Simplified call to {@link GL10#glGetIntegerv(int, int[], int) + * + * @param glEnum A symbolic GL constant. + * @return the integer information represented by {@code glEnum}. + */ + public int glGetInteger(int glEnum) { + int[] val = new int[1]; + mGL.glGetIntegerv(glEnum, val, 0); + return val[0]; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/FileBrowserHelper.java b/src/android/app/src/main/java/org/citra/citra_android/utils/FileBrowserHelper.java index 9752d61dc..746b0932f 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/FileBrowserHelper.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/FileBrowserHelper.java @@ -15,45 +15,40 @@ import org.citra.citra_android.ui.main.MainPresenter; import java.io.File; import java.util.List; -public final class FileBrowserHelper -{ - public static void openDirectoryPicker(FragmentActivity activity) - { - Intent i = new Intent(activity, CustomFilePickerActivity.class); +public final class FileBrowserHelper { + public static void openDirectoryPicker(FragmentActivity activity) { + Intent i = new Intent(activity, CustomFilePickerActivity.class); - i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false); - i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false); - i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR); - i.putExtra(FilePickerActivity.EXTRA_START_PATH, - Environment.getExternalStorageDirectory().getPath()); + i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false); + i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false); + i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR); + i.putExtra(FilePickerActivity.EXTRA_START_PATH, + Environment.getExternalStorageDirectory().getPath()); - activity.startActivityForResult(i, MainPresenter.REQUEST_ADD_DIRECTORY); - } - - public static void openFilePicker(FragmentActivity activity, int requestCode) - { - Intent i = new Intent(activity, CustomFilePickerActivity.class); - - i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false); - i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false); - i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_FILE); - i.putExtra(FilePickerActivity.EXTRA_START_PATH, - Environment.getExternalStorageDirectory().getPath()); - - activity.startActivityForResult(i, requestCode); - } - - @Nullable - public static String getSelectedDirectory(Intent result) - { - // Use the provided utility method to parse the result - List files = Utils.getSelectedFilesFromResult(result); - if (!files.isEmpty()) - { - File file = Utils.getFileForUri(files.get(0)); - return file.getAbsolutePath(); + activity.startActivityForResult(i, MainPresenter.REQUEST_ADD_DIRECTORY); } - return null; - } + public static void openFilePicker(FragmentActivity activity, int requestCode) { + Intent i = new Intent(activity, CustomFilePickerActivity.class); + + i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false); + i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, false); + i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_FILE); + i.putExtra(FilePickerActivity.EXTRA_START_PATH, + Environment.getExternalStorageDirectory().getPath()); + + activity.startActivityForResult(i, requestCode); + } + + @Nullable + public static String getSelectedDirectory(Intent result) { + // Use the provided utility method to parse the result + List files = Utils.getSelectedFilesFromResult(result); + if (!files.isEmpty()) { + File file = Utils.getFileForUri(files.get(0)); + return file.getAbsolutePath(); + } + + return null; + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/GameBannerRequestHandler.java b/src/android/app/src/main/java/org/citra/citra_android/utils/GameBannerRequestHandler.java index 063808d9f..ab010e933 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/GameBannerRequestHandler.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/GameBannerRequestHandler.java @@ -10,21 +10,18 @@ import org.citra.citra_android.NativeLibrary; import java.nio.IntBuffer; -public class GameBannerRequestHandler extends RequestHandler -{ - @Override - public boolean canHandleRequest(Request data) - { - return "iso".equals(data.uri.getScheme()); - } +public class GameBannerRequestHandler extends RequestHandler { + @Override + public boolean canHandleRequest(Request data) { + return "iso".equals(data.uri.getScheme()); + } - @Override - public Result load(Request request, int networkPolicy) - { - String url = request.uri.getHost() + request.uri.getPath(); - int[] vector = NativeLibrary.GetBanner(url); - Bitmap bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.RGB_565); - bitmap.copyPixelsFromBuffer(IntBuffer.wrap(vector)); - return new Result(bitmap, Picasso.LoadedFrom.DISK); - } + @Override + public Result load(Request request, int networkPolicy) { + String url = request.uri.getHost() + request.uri.getPath(); + int[] vector = NativeLibrary.GetBanner(url); + Bitmap bitmap = Bitmap.createBitmap(48, 48, Bitmap.Config.RGB_565); + bitmap.copyPixelsFromBuffer(IntBuffer.wrap(vector)); + return new Result(bitmap, Picasso.LoadedFrom.DISK); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/Java_GCAdapter.java b/src/android/app/src/main/java/org/citra/citra_android/utils/Java_GCAdapter.java index 1538d6c96..6847fa63a 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/Java_GCAdapter.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/Java_GCAdapter.java @@ -19,143 +19,116 @@ import org.citra.citra_android.services.USBPermService; import java.util.HashMap; import java.util.Map; -public class Java_GCAdapter -{ - public static UsbManager manager; - static byte[] controller_payload = new byte[37]; +public class Java_GCAdapter { + public static UsbManager manager; + static byte[] controller_payload = new byte[37]; - static UsbDeviceConnection usb_con; - static UsbInterface usb_intf; - static UsbEndpoint usb_in; - static UsbEndpoint usb_out; + static UsbDeviceConnection usb_con; + static UsbInterface usb_intf; + static UsbEndpoint usb_in; + static UsbEndpoint usb_out; - private static void RequestPermission() - { - Context context = NativeLibrary.sEmulationActivity.get(); - if (context != null) - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) - { - if (!manager.hasPermission(dev)) - { - Intent intent = new Intent(); - PendingIntent pend_intent; - intent.setClass(context, USBPermService.class); - pend_intent = PendingIntent.getService(context, 0, intent, 0); - manager.requestPermission(dev, pend_intent); - } + private static void RequestPermission() { + Context context = NativeLibrary.sEmulationActivity.get(); + if (context != null) { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) { + if (!manager.hasPermission(dev)) { + Intent intent = new Intent(); + PendingIntent pend_intent; + intent.setClass(context, USBPermService.class); + pend_intent = PendingIntent.getService(context, 0, intent, 0); + manager.requestPermission(dev, pend_intent); + } + } + } + } else { + Log.warning("Cannot request GameCube Adapter permission as EmulationActivity is null."); } - } - } - else - { - Log.warning("Cannot request GameCube Adapter permission as EmulationActivity is null."); + } - } - - public static void Shutdown() - { - usb_con.close(); - } - - public static int GetFD() - { - return usb_con.getFileDescriptor(); - } - - public static boolean QueryAdapter() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) - { - if (manager.hasPermission(dev)) - return true; - else - RequestPermission(); - } + public static void Shutdown() { + usb_con.close(); } - return false; - } - public static void InitAdapter() - { - byte[] init = {0x13}; - usb_con.bulkTransfer(usb_in, init, init.length, 0); - } + public static int GetFD() { + return usb_con.getFileDescriptor(); + } - public static int Input() - { - return usb_con.bulkTransfer(usb_in, controller_payload, controller_payload.length, 16); - } - - public static int Output(byte[] rumble) - { - return usb_con.bulkTransfer(usb_out, rumble, 5, 16); - } - - public static boolean OpenAdapter() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) - { - if (manager.hasPermission(dev)) - { - usb_con = manager.openDevice(dev); - - Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); - Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); - - if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) - { - UsbConfiguration conf = dev.getConfiguration(0); - usb_intf = conf.getInterface(0); - usb_con.claimInterface(usb_intf, true); - - Log.info("GCAdapter: Number of endpoints: " + usb_intf.getEndpointCount()); - - if (usb_intf.getEndpointCount() == 2) - { - for (int i = 0; i < usb_intf.getEndpointCount(); ++i) - if (usb_intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) - usb_in = usb_intf.getEndpoint(i); + public static boolean QueryAdapter() { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) { + if (manager.hasPermission(dev)) + return true; else - usb_out = usb_intf.getEndpoint(i); - - InitAdapter(); - return true; + RequestPermission(); } - else - { - usb_con.releaseInterface(usb_intf); - } - } - - final Activity emulationActivity = NativeLibrary.sEmulationActivity.get(); - if (emulationActivity != null) - { - emulationActivity.runOnUiThread(() -> Toast.makeText(emulationActivity, - "GameCube Adapter couldn't be opened. Please re-plug the device.", - Toast.LENGTH_LONG).show()); - } - else - { - Log.warning("Cannot show toast for GameCube Adapter failure."); - } - usb_con.close(); } - } + return false; + } + + public static void InitAdapter() { + byte[] init = {0x13}; + usb_con.bulkTransfer(usb_in, init, init.length, 0); + } + + public static int Input() { + return usb_con.bulkTransfer(usb_in, controller_payload, controller_payload.length, 16); + } + + public static int Output(byte[] rumble) { + return usb_con.bulkTransfer(usb_out, rumble, 5, 16); + } + + public static boolean OpenAdapter() { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) { + if (manager.hasPermission(dev)) { + usb_con = manager.openDevice(dev); + + Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); + Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); + + if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) { + UsbConfiguration conf = dev.getConfiguration(0); + usb_intf = conf.getInterface(0); + usb_con.claimInterface(usb_intf, true); + + Log.info("GCAdapter: Number of endpoints: " + usb_intf.getEndpointCount()); + + if (usb_intf.getEndpointCount() == 2) { + for (int i = 0; i < usb_intf.getEndpointCount(); ++i) + if (usb_intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) + usb_in = usb_intf.getEndpoint(i); + else + usb_out = usb_intf.getEndpoint(i); + + InitAdapter(); + return true; + } else { + usb_con.releaseInterface(usb_intf); + } + } + + final Activity emulationActivity = NativeLibrary.sEmulationActivity.get(); + if (emulationActivity != null) { + emulationActivity.runOnUiThread(() -> Toast.makeText(emulationActivity, + "GameCube Adapter couldn't be opened. Please re-plug the device.", + Toast.LENGTH_LONG).show()); + } else { + Log.warning("Cannot show toast for GameCube Adapter failure."); + } + usb_con.close(); + } + } + } + return false; } - return false; - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/Log.java b/src/android/app/src/main/java/org/citra/citra_android/utils/Log.java index c92df416a..2439980b9 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/Log.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/Log.java @@ -7,47 +7,37 @@ import org.citra.citra_android.BuildConfig; * with the same TAG automatically provided. Also no-ops VERBOSE and DEBUG log * levels in release builds. */ -public final class Log -{ - private static final String TAG = "Citra Frontend"; +public final class Log { + private static final String TAG = "Citra Frontend"; - private Log() - { - } - - public static void verbose(String message) - { - if (BuildConfig.DEBUG) - { - android.util.Log.v(TAG, message); + private Log() { } - } - public static void debug(String message) - { - if (BuildConfig.DEBUG) - { - android.util.Log.d(TAG, message); + public static void verbose(String message) { + if (BuildConfig.DEBUG) { + android.util.Log.v(TAG, message); + } } - } - public static void info(String message) - { - android.util.Log.i(TAG, message); - } + public static void debug(String message) { + if (BuildConfig.DEBUG) { + android.util.Log.d(TAG, message); + } + } - public static void warning(String message) - { - android.util.Log.w(TAG, message); - } + public static void info(String message) { + android.util.Log.i(TAG, message); + } - public static void error(String message) - { - android.util.Log.e(TAG, message); - } + public static void warning(String message) { + android.util.Log.w(TAG, message); + } - public static void wtf(String message) - { - android.util.Log.wtf(TAG, message); - } + public static void error(String message) { + android.util.Log.e(TAG, message); + } + + public static void wtf(String message) { + android.util.Log.wtf(TAG, message); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/PermissionsHandler.java b/src/android/app/src/main/java/org/citra/citra_android/utils/PermissionsHandler.java index 77c84c669..def47ef49 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/PermissionsHandler.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/PermissionsHandler.java @@ -14,59 +14,51 @@ import org.citra.citra_android.R; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -public class PermissionsHandler -{ - public static final int REQUEST_CODE_WRITE_PERMISSION = 500; +public class PermissionsHandler { + public static final int REQUEST_CODE_WRITE_PERMISSION = 500; - @TargetApi(Build.VERSION_CODES.M) - public static boolean checkWritePermission(final FragmentActivity activity) - { - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) - { - return true; + @TargetApi(Build.VERSION_CODES.M) + public static boolean checkWritePermission(final FragmentActivity activity) { + if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return true; + } + + int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE); + + if (hasWritePermission != PackageManager.PERMISSION_GRANTED) { + if (activity.shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) { + showMessageOKCancel(activity, activity.getString(R.string.write_permission_needed), + (dialog, which) -> activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, + REQUEST_CODE_WRITE_PERMISSION)); + return false; + } + + activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, + REQUEST_CODE_WRITE_PERMISSION); + return false; + } + + return true; } - int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE); + public static boolean hasWriteAccess(Context context) { + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + int hasWritePermission = ContextCompat.checkSelfPermission(context, WRITE_EXTERNAL_STORAGE); + return hasWritePermission == PackageManager.PERMISSION_GRANTED; + } - if (hasWritePermission != PackageManager.PERMISSION_GRANTED) - { - if (activity.shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) - { - showMessageOKCancel(activity, activity.getString(R.string.write_permission_needed), - (dialog, which) -> activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, - REQUEST_CODE_WRITE_PERMISSION)); - return false; - } - - activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, - REQUEST_CODE_WRITE_PERMISSION); - return false; + return true; } - return true; - } - - public static boolean hasWriteAccess(Context context) - { - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - { - int hasWritePermission = ContextCompat.checkSelfPermission(context, WRITE_EXTERNAL_STORAGE); - return hasWritePermission == PackageManager.PERMISSION_GRANTED; + private static void showMessageOKCancel(final FragmentActivity activity, String message, + DialogInterface.OnClickListener okListener) { + new AlertDialog.Builder(activity) + .setMessage(message) + .setPositiveButton(android.R.string.ok, okListener) + .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> + Toast.makeText(activity, R.string.write_permission_needed, Toast.LENGTH_SHORT) + .show()) + .create() + .show(); } - - return true; - } - - private static void showMessageOKCancel(final FragmentActivity activity, String message, - DialogInterface.OnClickListener okListener) - { - new AlertDialog.Builder(activity) - .setMessage(message) - .setPositiveButton(android.R.string.ok, okListener) - .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> - Toast.makeText(activity, R.string.write_permission_needed, Toast.LENGTH_SHORT) - .show()) - .create() - .show(); - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/PicassoUtils.java b/src/android/app/src/main/java/org/citra/citra_android/utils/PicassoUtils.java index b37d5c375..5cfaa9e7c 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/PicassoUtils.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/PicassoUtils.java @@ -11,40 +11,35 @@ import org.citra.citra_android.R; import java.io.File; import java.net.URI; -public class PicassoUtils -{ - public static void loadGameBanner(ImageView imageView, String screenshotPath, String gamePath) - { - File file = new File(URI.create(screenshotPath.replaceAll(" ", "%20"))); - if (file.exists()) - { - // Fill in the view contents. - Picasso.with(imageView.getContext()) - .load(screenshotPath) - .fit() - .centerCrop() - .noFade() - .noPlaceholder() - .config(Bitmap.Config.RGB_565) - .error(R.drawable.no_banner) - .into(imageView); +public class PicassoUtils { + public static void loadGameBanner(ImageView imageView, String screenshotPath, String gamePath) { + File file = new File(URI.create(screenshotPath.replaceAll(" ", "%20"))); + if (file.exists()) { + // Fill in the view contents. + Picasso.with(imageView.getContext()) + .load(screenshotPath) + .fit() + .centerCrop() + .noFade() + .noPlaceholder() + .config(Bitmap.Config.RGB_565) + .error(R.drawable.no_banner) + .into(imageView); + } else { + Picasso picassoInstance = new Picasso.Builder(imageView.getContext()) + .addRequestHandler(new GameBannerRequestHandler()) + .build(); + + picassoInstance + .load(Uri.parse("iso:/" + gamePath)) + .fit() + .noFade() + .noPlaceholder() + .config(Bitmap.Config.RGB_565) + .error(R.drawable.no_banner) + .into(imageView); + } + + } - else - { - Picasso picassoInstance = new Picasso.Builder(imageView.getContext()) - .addRequestHandler(new GameBannerRequestHandler()) - .build(); - - picassoInstance - .load(Uri.parse("iso:/" + gamePath)) - .fit() - .noFade() - .noPlaceholder() - .config(Bitmap.Config.RGB_565) - .error(R.drawable.no_banner) - .into(imageView); - } - - - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/SettingsFile.java b/src/android/app/src/main/java/org/citra/citra_android/utils/SettingsFile.java index 9e5003a09..9ad28715c 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/SettingsFile.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/SettingsFile.java @@ -23,279 +23,239 @@ import java.util.Set; * A HashMap that constructs a new SettingSection instead of returning null * when getting a key not already in the map */ -final class SettingsSectionMap extends HashMap -{ - @Override - public SettingSection get(Object key) - { - if (!(key instanceof String)) - { - return null; - } +final class SettingsSectionMap extends HashMap { + @Override + public SettingSection get(Object key) { + if (!(key instanceof String)) { + return null; + } - String stringKey = (String) key; + String stringKey = (String) key; - if (!super.containsKey(stringKey)) - { - SettingSection section = new SettingSection(stringKey); - super.put(stringKey, section); - return section; + if (!super.containsKey(stringKey)) { + SettingSection section = new SettingSection(stringKey); + super.put(stringKey, section); + return section; + } + return super.get(key); } - return super.get(key); - } } /** * Contains static methods for interacting with .ini files in which settings are stored. */ -public final class SettingsFile -{ - public static final int SETTINGS_DOLPHIN = 0; +public final class SettingsFile { + public static final int SETTINGS_DOLPHIN = 0; - public static final String FILE_NAME_CONFIG = "config"; + public static final String FILE_NAME_CONFIG = "config"; - public static final String SECTION_CONTROLS = "Controls"; - public static final String SECTION_CORE = "Core"; - public static final String SECTION_RENDERER = "Renderer"; - public static final String SECTION_LAYOUT = "Layout"; - public static final String SECTION_AUDIO = "Audio"; - public static final String SECTION_SYSTEM = "System"; - public static final String SECTION_CAMERA = "Camera"; - public static final String SECTION_MISC = "Miscellaneous"; - public static final String SECTION_DEBUGGING = "Debugging"; - public static final String SECTION_WEBSERVICE = "WebService"; + public static final String SECTION_CONTROLS = "Controls"; + public static final String SECTION_CORE = "Core"; + public static final String SECTION_RENDERER = "Renderer"; + public static final String SECTION_LAYOUT = "Layout"; + public static final String SECTION_AUDIO = "Audio"; + public static final String SECTION_SYSTEM = "System"; + public static final String SECTION_CAMERA = "Camera"; + public static final String SECTION_MISC = "Miscellaneous"; + public static final String SECTION_DEBUGGING = "Debugging"; + public static final String SECTION_WEBSERVICE = "WebService"; - public static final String KEY_CPU_JIT = "use_cpu_jit"; + public static final String KEY_CPU_JIT = "use_cpu_jit"; - public static final String KEY_HW_RENDERER = "use_hw_renderer"; - public static final String KEY_HW_SHADER = "use_hw_shader"; - public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; - public static final String KEY_SHADERS_ACCURATE_GS = "shaders_accurate_gs"; - public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; - public static final String KEY_USE_VSYNC = "use_vsync"; - public static final String KEY_RESOLUTION_FACTOR = "resolution_factor"; - public static final String KEY_FRAME_LIMIT_ENABLED = "use_frame_limit"; - public static final String KEY_FRAME_LIMIT = "frame_limit"; - public static final String KEY_BACKGROUND_RED = "bg_red"; - public static final String KEY_BACKGROUND_BLUE = "bg_blue"; - public static final String KEY_BACKGROUND_GREEN = "bg_green"; - public static final String KEY_STEREOSCOPY = "toggle_3d"; - public static final String KEY_FACTOR_3D = "factor_3d"; + public static final String KEY_HW_RENDERER = "use_hw_renderer"; + public static final String KEY_HW_SHADER = "use_hw_shader"; + public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; + public static final String KEY_SHADERS_ACCURATE_GS = "shaders_accurate_gs"; + public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; + public static final String KEY_USE_VSYNC = "use_vsync"; + public static final String KEY_RESOLUTION_FACTOR = "resolution_factor"; + public static final String KEY_FRAME_LIMIT_ENABLED = "use_frame_limit"; + public static final String KEY_FRAME_LIMIT = "frame_limit"; + public static final String KEY_BACKGROUND_RED = "bg_red"; + public static final String KEY_BACKGROUND_BLUE = "bg_blue"; + public static final String KEY_BACKGROUND_GREEN = "bg_green"; + public static final String KEY_STEREOSCOPY = "toggle_3d"; + public static final String KEY_FACTOR_3D = "factor_3d"; - public static final String KEY_LAYOUT_OPTION = "layout_option"; - public static final String KEY_SWAP_SCREEN = "swap_screen"; + public static final String KEY_LAYOUT_OPTION = "layout_option"; + public static final String KEY_SWAP_SCREEN = "swap_screen"; - public static final String KEY_AUDIO_OUTPUT_ENGINE = "output_engine"; - public static final String KEY_ENABLE_AUDIO_STRETCHING = "enable_audio_stretching"; - public static final String KEY_VOLUME = "volume"; + public static final String KEY_AUDIO_OUTPUT_ENGINE = "output_engine"; + public static final String KEY_ENABLE_AUDIO_STRETCHING = "enable_audio_stretching"; + public static final String KEY_VOLUME = "volume"; - public static final String KEY_USE_VIRTUAL_SD = "use_virtual_sd"; + public static final String KEY_USE_VIRTUAL_SD = "use_virtual_sd"; - public static final String KEY_IS_NEW_3DS = "is_new_3ds"; - public static final String KEY_REGION_VALUE = "region_value"; - public static final String KEY_INIT_CLOCK = "init_clock"; - public static final String KEY_INIT_TIME = "init_time"; + public static final String KEY_IS_NEW_3DS = "is_new_3ds"; + public static final String KEY_REGION_VALUE = "region_value"; + public static final String KEY_INIT_CLOCK = "init_clock"; + public static final String KEY_INIT_TIME = "init_time"; - public static final String KEY_CAMERA_OUTER_RIGHT_NAME = "camera_outer_right_name"; - public static final String KEY_CAMERA_OUTER_RIGHT_CONFIG = "camera_outer_right_config"; - public static final String KEY_CAMERA_OUTER_RIGHT_FLIP = "camera_outer_right_flip"; - public static final String KEY_CAMERA_OUTER_LEFT_FLIP = "camera_outer_left_flip"; - public static final String KEY_CAMERA_INNER_NAME = "camera_inner_name"; - public static final String KEY_CAMERA_INNER_CONFIG = "camera_inner_config"; - public static final String KEY_CAMERA_INNER_FLIP = "camera_inner_flip"; + public static final String KEY_CAMERA_OUTER_RIGHT_NAME = "camera_outer_right_name"; + public static final String KEY_CAMERA_OUTER_RIGHT_CONFIG = "camera_outer_right_config"; + public static final String KEY_CAMERA_OUTER_RIGHT_FLIP = "camera_outer_right_flip"; + public static final String KEY_CAMERA_OUTER_LEFT_FLIP = "camera_outer_left_flip"; + public static final String KEY_CAMERA_INNER_NAME = "camera_inner_name"; + public static final String KEY_CAMERA_INNER_CONFIG = "camera_inner_config"; + public static final String KEY_CAMERA_INNER_FLIP = "camera_inner_flip"; - public static final String KEY_LOG_FILTER = "log_filter"; + public static final String KEY_LOG_FILTER = "log_filter"; - private SettingsFile() - { - } + private SettingsFile() { + } - /** - * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves - * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it - * failed. - * - * @param fileName The name of the settings file without a path or extension. - * @param view The current view. - * @return An Observable that emits a HashMap of the file's contents, then completes. - */ - public static HashMap readFile(final String fileName, - SettingsActivityView view) - { - HashMap sections = new SettingsSectionMap(); + /** + * Reads a given .ini file from disk and returns it as a HashMap of SettingSections, themselves + * effectively a HashMap of key/value settings. If unsuccessful, outputs an error telling why it + * failed. + * + * @param fileName The name of the settings file without a path or extension. + * @param view The current view. + * @return An Observable that emits a HashMap of the file's contents, then completes. + */ + public static HashMap readFile(final String fileName, + SettingsActivityView view) { + HashMap sections = new SettingsSectionMap(); - File ini = getSettingsFile(fileName); + File ini = getSettingsFile(fileName); - BufferedReader reader = null; + BufferedReader reader = null; - try - { - reader = new BufferedReader(new FileReader(ini)); + try { + reader = new BufferedReader(new FileReader(ini)); - SettingSection current = null; - for (String line; (line = reader.readLine()) != null; ) - { - if (line.startsWith("[") && line.endsWith("]")) - { - current = sectionFromLine(line); - sections.put(current.getName(), current); + SettingSection current = null; + for (String line; (line = reader.readLine()) != null; ) { + if (line.startsWith("[") && line.endsWith("]")) { + current = sectionFromLine(line); + sections.put(current.getName(), current); + } else if ((current != null)) { + Setting setting = settingFromLine(current, line, fileName); + if (setting != null) { + current.putSetting(setting); + } + } + } + } catch (FileNotFoundException e) { + Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage()); + view.onSettingsFileNotFound(); + } catch (IOException e) { + Log.error("[SettingsFile] Error reading from: " + fileName + ".ini: " + e.getMessage()); + view.onSettingsFileNotFound(); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + Log.error("[SettingsFile] Error closing: " + fileName + ".ini: " + e.getMessage()); + } + } } - else if ((current != null)) - { - Setting setting = settingFromLine(current, line, fileName); - if (setting != null) - { - current.putSetting(setting); - } + + return sections; + } + + /** + * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error + * telling why it failed. + * + * @param fileName The target filename without a path or extension. + * @param sections The HashMap containing the Settings we want to serialize. + * @param view The current view. + * @return An Observable representing the operation. + */ + public static void saveFile(final String fileName, final HashMap sections, + SettingsActivityView view) { + File ini = getSettingsFile(fileName); + + Wini writer = null; + try { + writer = new Wini(ini); + + Set keySet = sections.keySet(); + for (String key : keySet) { + SettingSection section = sections.get(key); + writeSection(writer, section); + } + writer.store(); + } catch (IOException e) { + Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage()); + view.showToastMessage("Error saving " + fileName + ".ini: " + e.getMessage()); } - } } - catch (FileNotFoundException e) - { - Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage()); - view.onSettingsFileNotFound(); + + @NonNull + private static File getSettingsFile(String fileName) { + return new File( + DirectoryInitializationService.getUserDirectory() + "/config/" + fileName + ".ini"); } - catch (IOException e) - { - Log.error("[SettingsFile] Error reading from: " + fileName + ".ini: " + e.getMessage()); - view.onSettingsFileNotFound(); + + private static SettingSection sectionFromLine(String line) { + String sectionName = line.substring(1, line.length() - 1); + return new SettingSection(sectionName); } - finally - { - if (reader != null) - { - try - { - reader.close(); + + /** + * For a line of text, determines what type of data is being represented, and returns + * a Setting object containing this data. + * + * @param current The section currently being parsed by the consuming method. + * @param line The line of text being parsed. + * @param fileName The name of the ini file the setting is in. + * @return A typed Setting containing the key/value contained in the line. + */ + private static Setting settingFromLine(SettingSection current, String line, String fileName) { + String[] splitLine = line.split("="); + + if (splitLine.length != 2) { + Log.warning("Skipping invalid config line \"" + line + "\""); + return null; } - catch (IOException e) - { - Log.error("[SettingsFile] Error closing: " + fileName + ".ini: " + e.getMessage()); + + String key = splitLine[0].trim(); + String value = splitLine[1].trim(); + + if (value.isEmpty()) { + Log.warning("Skipping null value in config line \"" + line + "\""); + return null; } - } + + int file = SETTINGS_DOLPHIN; + + try { + int valueAsInt = Integer.valueOf(value); + + return new IntSetting(key, current.getName(), file, valueAsInt); + } catch (NumberFormatException ex) { + } + + try { + float valueAsFloat = Float.valueOf(value); + + return new FloatSetting(key, current.getName(), file, valueAsFloat); + } catch (NumberFormatException ex) { + } + + return new StringSetting(key, current.getName(), file, value); } - return sections; - } + /** + * Writes the contents of a Section HashMap to disk. + * + * @param parser A Wini pointed at a file on disk. + * @param section A section containing settings to be written to the file. + */ + private static void writeSection(Wini parser, SettingSection section) { + // Write the section header. + String header = section.getName(); - /** - * Saves a Settings HashMap to a given .ini file on disk. If unsuccessful, outputs an error - * telling why it failed. - * - * @param fileName The target filename without a path or extension. - * @param sections The HashMap containing the Settings we want to serialize. - * @param view The current view. - * @return An Observable representing the operation. - */ - public static void saveFile(final String fileName, final HashMap sections, - SettingsActivityView view) - { - File ini = getSettingsFile(fileName); + // Write this section's values. + HashMap settings = section.getSettings(); + Set keySet = settings.keySet(); - Wini writer = null; - try - { - writer = new Wini(ini); - - Set keySet = sections.keySet(); - for (String key : keySet) - { - SettingSection section = sections.get(key); - writeSection(writer, section); - } - writer.store(); + for (String key : keySet) { + Setting setting = settings.get(key); + parser.put(header, setting.getKey(), setting.getValueAsString()); + } } - catch (IOException e) - { - Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage()); - view.showToastMessage("Error saving " + fileName + ".ini: " + e.getMessage()); - } - } - - @NonNull - private static File getSettingsFile(String fileName) - { - return new File( - DirectoryInitializationService.getUserDirectory() + "/config/" + fileName + ".ini"); - } - - private static SettingSection sectionFromLine(String line) - { - String sectionName = line.substring(1, line.length() - 1); - return new SettingSection(sectionName); - } - - /** - * For a line of text, determines what type of data is being represented, and returns - * a Setting object containing this data. - * - * @param current The section currently being parsed by the consuming method. - * @param line The line of text being parsed. - * @param fileName The name of the ini file the setting is in. - * @return A typed Setting containing the key/value contained in the line. - */ - private static Setting settingFromLine(SettingSection current, String line, String fileName) - { - String[] splitLine = line.split("="); - - if (splitLine.length != 2) - { - Log.warning("Skipping invalid config line \"" + line + "\""); - return null; - } - - String key = splitLine[0].trim(); - String value = splitLine[1].trim(); - - if(value.isEmpty()){ - Log.warning("Skipping null value in config line \"" + line + "\""); - return null; - } - - int file = SETTINGS_DOLPHIN; - - try - { - int valueAsInt = Integer.valueOf(value); - - return new IntSetting(key, current.getName(), file, valueAsInt); - } - catch (NumberFormatException ex) - { - } - - try - { - float valueAsFloat = Float.valueOf(value); - - return new FloatSetting(key, current.getName(), file, valueAsFloat); - } - catch (NumberFormatException ex) - { - } - - return new StringSetting(key, current.getName(), file, value); - } - - /** - * Writes the contents of a Section HashMap to disk. - * - * @param parser A Wini pointed at a file on disk. - * @param section A section containing settings to be written to the file. - */ - private static void writeSection(Wini parser, SettingSection section) - { - // Write the section header. - String header = section.getName(); - - // Write this section's values. - HashMap settings = section.getSettings(); - Set keySet = settings.keySet(); - - for (String key : keySet) - { - Setting setting = settings.get(key); - parser.put(header, setting.getKey(), setting.getValueAsString()); - } - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/utils/StartupHandler.java b/src/android/app/src/main/java/org/citra/citra_android/utils/StartupHandler.java index d54944c02..12a406f7c 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/utils/StartupHandler.java +++ b/src/android/app/src/main/java/org/citra/citra_android/utils/StartupHandler.java @@ -7,25 +7,22 @@ import android.text.TextUtils; import org.citra.citra_android.activities.EmulationActivity; -public final class StartupHandler -{ - public static void HandleInit(FragmentActivity parent) - { - // Ask the user to grant write permission if it's not already granted - PermissionsHandler.checkWritePermission(parent); +public final class StartupHandler { + public static void HandleInit(FragmentActivity parent) { + // Ask the user to grant write permission if it's not already granted + PermissionsHandler.checkWritePermission(parent); - String start_file = ""; - Bundle extras = parent.getIntent().getExtras(); - if (extras != null) - start_file = extras.getString("AutoStartFile"); + String start_file = ""; + Bundle extras = parent.getIntent().getExtras(); + if (extras != null) + start_file = extras.getString("AutoStartFile"); - if (!TextUtils.isEmpty(start_file)) - { - // Start the emulation activity, send the ISO passed in and finish the main activity - Intent emulation_intent = new Intent(parent, EmulationActivity.class); - emulation_intent.putExtra("SelectedGame", start_file); - parent.startActivity(emulation_intent); - parent.finish(); + if (!TextUtils.isEmpty(start_file)) { + // Start the emulation activity, send the ISO passed in and finish the main activity + Intent emulation_intent = new Intent(parent, EmulationActivity.class); + emulation_intent.putExtra("SelectedGame", start_file); + parent.startActivity(emulation_intent); + parent.finish(); + } } - } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/viewholders/GameViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/viewholders/GameViewHolder.java index 7599110a7..0bbf9d954 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/viewholders/GameViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/viewholders/GameViewHolder.java @@ -11,30 +11,28 @@ import org.citra.citra_android.R; * A simple class that stores references to views so that the GameAdapter doesn't need to * keep calling findViewById(), which is expensive. */ -public class GameViewHolder extends RecyclerView.ViewHolder -{ - public ImageView imageScreenshot; - public TextView textGameTitle; - public TextView textCompany; +public class GameViewHolder extends RecyclerView.ViewHolder { + public ImageView imageScreenshot; + public TextView textGameTitle; + public TextView textCompany; - public String gameId; + public String gameId; - // TODO Not need any of this stuff. Currently only the properties dialog needs it. - public String path; - public String title; - public String description; - public int country; - public String company; - public String screenshotPath; + // TODO Not need any of this stuff. Currently only the properties dialog needs it. + public String path; + public String title; + public String description; + public int country; + public String company; + public String screenshotPath; - public GameViewHolder(View itemView) - { - super(itemView); + public GameViewHolder(View itemView) { + super(itemView); - itemView.setTag(this); + itemView.setTag(this); - imageScreenshot = itemView.findViewById(R.id.image_game_screen); - textGameTitle = itemView.findViewById(R.id.text_game_title); - textCompany = itemView.findViewById(R.id.text_company); - } + imageScreenshot = itemView.findViewById(R.id.image_game_screen); + textGameTitle = itemView.findViewById(R.id.text_game_title); + textCompany = itemView.findViewById(R.id.text_company); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvGameViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvGameViewHolder.java index 07e37b688..cdbae2aab 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvGameViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvGameViewHolder.java @@ -9,29 +9,27 @@ import android.widget.ImageView; * A simple class that stores references to views so that the GameAdapter doesn't need to * keep calling findViewById(), which is expensive. */ -public final class TvGameViewHolder extends Presenter.ViewHolder -{ - public ImageCardView cardParent; +public final class TvGameViewHolder extends Presenter.ViewHolder { + public ImageCardView cardParent; - public ImageView imageScreenshot; + public ImageView imageScreenshot; - public String gameId; + public String gameId; - // TODO Not need any of this stuff. Currently only the properties dialog needs it. - public String path; - public String title; - public String description; - public int country; - public String company; - public String screenshotPath; + // TODO Not need any of this stuff. Currently only the properties dialog needs it. + public String path; + public String title; + public String description; + public int country; + public String company; + public String screenshotPath; - public TvGameViewHolder(View itemView) - { - super(itemView); + public TvGameViewHolder(View itemView) { + super(itemView); - itemView.setTag(this); + itemView.setTag(this); - cardParent = (ImageCardView) itemView; - imageScreenshot = cardParent.getMainImageView(); - } + cardParent = (ImageCardView) itemView; + imageScreenshot = cardParent.getMainImageView(); + } } diff --git a/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvSettingsViewHolder.java b/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvSettingsViewHolder.java index 40831378a..2487b1cd1 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvSettingsViewHolder.java +++ b/src/android/app/src/main/java/org/citra/citra_android/viewholders/TvSettingsViewHolder.java @@ -4,19 +4,17 @@ import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.Presenter; import android.view.View; -public final class TvSettingsViewHolder extends Presenter.ViewHolder -{ - public ImageCardView cardParent; +public final class TvSettingsViewHolder extends Presenter.ViewHolder { + public ImageCardView cardParent; - // Determines what action to take when this item is clicked. - public int itemId; + // Determines what action to take when this item is clicked. + public int itemId; - public TvSettingsViewHolder(View itemView) - { - super(itemView); + public TvSettingsViewHolder(View itemView) { + super(itemView); - itemView.setTag(this); + itemView.setTag(this); - cardParent = (ImageCardView) itemView; - } + cardParent = (ImageCardView) itemView; + } }