Reformat Android code

This commit is contained in:
riking 2018-08-26 21:37:54 -07:00
parent ab76631a7f
commit 248ee12aed
102 changed files with 11134 additions and 10354 deletions

View File

@ -67,7 +67,8 @@ android {
defaultConfig { defaultConfig {
externalNativeBuild { externalNativeBuild {
cmake { cmake {
arguments "-DANDROID_STL=c++_static", "-DCMAKE_BUILD_TYPE=RelWithDebInfo" // , "-DENABLE_GENERIC=ON" arguments "-DANDROID_STL=c++_static", "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
// , "-DENABLE_GENERIC=ON"
abiFilters "arm64-v8a", "x86_64" //, "armeabi-v7a", "x86" abiFilters "arm64-v8a", "x86_64" //, "armeabi-v7a", "x86"
} }
} }

View File

@ -11,13 +11,13 @@
android:name="android.software.leanback" android:name="android.software.leanback"
android:required="false"/> android:required="false"/>
<uses-feature android:glEsVersion="0x00030000" /> <uses-feature android:glEsVersion="0x00030000"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" /> <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA"/>
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /> <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA"/>
<application <application
android:name=".DolphinApplication" android:name=".DolphinApplication"
@ -27,7 +27,9 @@
android:supportsRtl="true" android:supportsRtl="true"
android:isGame="true" android:isGame="true"
android:banner="@drawable/banner_tv"> android:banner="@drawable/banner_tv">
<meta-data android:name="android.max_aspect" android:value="2.1" /> <meta-data
android:name="android.max_aspect"
android:value="2.1"/>
<activity <activity
android:name=".ui.main.MainActivity" android:name=".ui.main.MainActivity"
@ -67,18 +69,18 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/FilePickerTheme"> android:theme="@style/FilePickerTheme">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.GET_CONTENT" /> <action android:name="android.intent.action.GET_CONTENT"/>
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activities.AppLinkActivity" > <activity android:name=".activities.AppLinkActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT"/>
<data <data
android:host="@string/host" android:host="@string/host"
android:scheme="@string/scheme" /> android:scheme="@string/scheme"/>
</intent-filter> </intent-filter>
</activity> </activity>
@ -87,11 +89,11 @@
<service <service
android:name=".services.SyncChannelJobService" android:name=".services.SyncChannelJobService"
android:exported="false" android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" /> android:permission="android.permission.BIND_JOB_SERVICE"/>
<service <service
android:name=".services.SyncProgramsJobService" android:name=".services.SyncProgramsJobService"
android:exported="false" android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" /> android:permission="android.permission.BIND_JOB_SERVICE"/>
<provider <provider
android:name="android.support.v4.content.FileProvider" android:name="android.support.v4.content.FileProvider"
@ -100,7 +102,7 @@
android:grantUriPermissions="true"> android:grantUriPermissions="true">
<meta-data <meta-data
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/nnf_provider_paths" /> android:resource="@xml/nnf_provider_paths"/>
</provider> </provider>
</application> </application>

View File

@ -8,15 +8,15 @@ import org.dolphinemu.dolphinemu.utils.VolleyUtil;
public class DolphinApplication extends Application public class DolphinApplication extends Application
{ {
@Override @Override
public void onCreate() public void onCreate()
{ {
super.onCreate(); super.onCreate();
VolleyUtil.init(getApplicationContext()); VolleyUtil.init(getApplicationContext());
System.loadLibrary("main"); System.loadLibrary("main");
if (PermissionsHandler.hasWriteAccess(getApplicationContext())) if (PermissionsHandler.hasWriteAccess(getApplicationContext()))
DirectoryInitializationService.startService(getApplicationContext()); DirectoryInitializationService.startService(getApplicationContext());
} }
} }

View File

@ -7,13 +7,7 @@
package org.dolphinemu.dolphinemu; package org.dolphinemu.dolphinemu;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.content.res.AssetManager;
import android.view.Surface; import android.view.Surface;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.utils.Log; import org.dolphinemu.dolphinemu.utils.Log;
@ -26,433 +20,448 @@ import java.lang.ref.WeakReference;
*/ */
public final class NativeLibrary public final class NativeLibrary
{ {
public static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null); public static WeakReference<EmulationActivity> sEmulationActivity = new WeakReference<>(null);
/** /**
* Button type for use in onTouchEvent * Button type for use in onTouchEvent
*/ */
public static final class ButtonType public static final class ButtonType
{ {
public static final int BUTTON_A = 0; public static final int BUTTON_A = 0;
public static final int BUTTON_B = 1; public static final int BUTTON_B = 1;
public static final int BUTTON_START = 2; public static final int BUTTON_START = 2;
public static final int BUTTON_X = 3; public static final int BUTTON_X = 3;
public static final int BUTTON_Y = 4; public static final int BUTTON_Y = 4;
public static final int BUTTON_Z = 5; public static final int BUTTON_Z = 5;
public static final int BUTTON_UP = 6; public static final int BUTTON_UP = 6;
public static final int BUTTON_DOWN = 7; public static final int BUTTON_DOWN = 7;
public static final int BUTTON_LEFT = 8; public static final int BUTTON_LEFT = 8;
public static final int BUTTON_RIGHT = 9; public static final int BUTTON_RIGHT = 9;
public static final int STICK_MAIN = 10; public static final int STICK_MAIN = 10;
public static final int STICK_MAIN_UP = 11; public static final int STICK_MAIN_UP = 11;
public static final int STICK_MAIN_DOWN = 12; public static final int STICK_MAIN_DOWN = 12;
public static final int STICK_MAIN_LEFT = 13; public static final int STICK_MAIN_LEFT = 13;
public static final int STICK_MAIN_RIGHT = 14; public static final int STICK_MAIN_RIGHT = 14;
public static final int STICK_C = 15; public static final int STICK_C = 15;
public static final int STICK_C_UP = 16; public static final int STICK_C_UP = 16;
public static final int STICK_C_DOWN = 17; public static final int STICK_C_DOWN = 17;
public static final int STICK_C_LEFT = 18; public static final int STICK_C_LEFT = 18;
public static final int STICK_C_RIGHT = 19; public static final int STICK_C_RIGHT = 19;
public static final int TRIGGER_L = 20; public static final int TRIGGER_L = 20;
public static final int TRIGGER_R = 21; public static final int TRIGGER_R = 21;
public static final int WIIMOTE_BUTTON_A = 100; public static final int WIIMOTE_BUTTON_A = 100;
public static final int WIIMOTE_BUTTON_B = 101; public static final int WIIMOTE_BUTTON_B = 101;
public static final int WIIMOTE_BUTTON_MINUS = 102; public static final int WIIMOTE_BUTTON_MINUS = 102;
public static final int WIIMOTE_BUTTON_PLUS = 103; public static final int WIIMOTE_BUTTON_PLUS = 103;
public static final int WIIMOTE_BUTTON_HOME = 104; public static final int WIIMOTE_BUTTON_HOME = 104;
public static final int WIIMOTE_BUTTON_1 = 105; public static final int WIIMOTE_BUTTON_1 = 105;
public static final int WIIMOTE_BUTTON_2 = 106; public static final int WIIMOTE_BUTTON_2 = 106;
public static final int WIIMOTE_UP = 107; public static final int WIIMOTE_UP = 107;
public static final int WIIMOTE_DOWN = 108; public static final int WIIMOTE_DOWN = 108;
public static final int WIIMOTE_LEFT = 109; public static final int WIIMOTE_LEFT = 109;
public static final int WIIMOTE_RIGHT = 110; public static final int WIIMOTE_RIGHT = 110;
public static final int WIIMOTE_IR = 111; public static final int WIIMOTE_IR = 111;
public static final int WIIMOTE_IR_UP = 112; public static final int WIIMOTE_IR_UP = 112;
public static final int WIIMOTE_IR_DOWN = 113; public static final int WIIMOTE_IR_DOWN = 113;
public static final int WIIMOTE_IR_LEFT = 114; public static final int WIIMOTE_IR_LEFT = 114;
public static final int WIIMOTE_IR_RIGHT = 115; public static final int WIIMOTE_IR_RIGHT = 115;
public static final int WIIMOTE_IR_FORWARD = 116; public static final int WIIMOTE_IR_FORWARD = 116;
public static final int WIIMOTE_IR_BACKWARD = 117; public static final int WIIMOTE_IR_BACKWARD = 117;
public static final int WIIMOTE_IR_HIDE = 118; public static final int WIIMOTE_IR_HIDE = 118;
public static final int WIIMOTE_SWING = 119; public static final int WIIMOTE_SWING = 119;
public static final int WIIMOTE_SWING_UP = 120; public static final int WIIMOTE_SWING_UP = 120;
public static final int WIIMOTE_SWING_DOWN = 121; public static final int WIIMOTE_SWING_DOWN = 121;
public static final int WIIMOTE_SWING_LEFT = 122; public static final int WIIMOTE_SWING_LEFT = 122;
public static final int WIIMOTE_SWING_RIGHT = 123; public static final int WIIMOTE_SWING_RIGHT = 123;
public static final int WIIMOTE_SWING_FORWARD = 124; public static final int WIIMOTE_SWING_FORWARD = 124;
public static final int WIIMOTE_SWING_BACKWARD = 125; public static final int WIIMOTE_SWING_BACKWARD = 125;
public static final int WIIMOTE_TILT = 126; public static final int WIIMOTE_TILT = 126;
public static final int WIIMOTE_TILT_FORWARD = 127; public static final int WIIMOTE_TILT_FORWARD = 127;
public static final int WIIMOTE_TILT_BACKWARD = 128; public static final int WIIMOTE_TILT_BACKWARD = 128;
public static final int WIIMOTE_TILT_LEFT = 129; public static final int WIIMOTE_TILT_LEFT = 129;
public static final int WIIMOTE_TILT_RIGHT = 130; public static final int WIIMOTE_TILT_RIGHT = 130;
public static final int WIIMOTE_TILT_MODIFIER = 131; public static final int WIIMOTE_TILT_MODIFIER = 131;
public static final int WIIMOTE_SHAKE_X = 132; public static final int WIIMOTE_SHAKE_X = 132;
public static final int WIIMOTE_SHAKE_Y = 133; public static final int WIIMOTE_SHAKE_Y = 133;
public static final int WIIMOTE_SHAKE_Z = 134; public static final int WIIMOTE_SHAKE_Z = 134;
public static final int NUNCHUK_BUTTON_C = 200; public static final int NUNCHUK_BUTTON_C = 200;
public static final int NUNCHUK_BUTTON_Z = 201; public static final int NUNCHUK_BUTTON_Z = 201;
public static final int NUNCHUK_STICK = 202; public static final int NUNCHUK_STICK = 202;
public static final int NUNCHUK_STICK_UP = 203; public static final int NUNCHUK_STICK_UP = 203;
public static final int NUNCHUK_STICK_DOWN = 204; public static final int NUNCHUK_STICK_DOWN = 204;
public static final int NUNCHUK_STICK_LEFT = 205; public static final int NUNCHUK_STICK_LEFT = 205;
public static final int NUNCHUK_STICK_RIGHT = 206; public static final int NUNCHUK_STICK_RIGHT = 206;
public static final int NUNCHUK_SWING = 207; public static final int NUNCHUK_SWING = 207;
public static final int NUNCHUK_SWING_UP = 208; public static final int NUNCHUK_SWING_UP = 208;
public static final int NUNCHUK_SWING_DOWN = 209; public static final int NUNCHUK_SWING_DOWN = 209;
public static final int NUNCHUK_SWING_LEFT = 210; public static final int NUNCHUK_SWING_LEFT = 210;
public static final int NUNCHUK_SWING_RIGHT = 221; public static final int NUNCHUK_SWING_RIGHT = 221;
public static final int NUNCHUK_SWING_FORWARD = 212; public static final int NUNCHUK_SWING_FORWARD = 212;
public static final int NUNCHUK_SWING_BACKWARD = 213; public static final int NUNCHUK_SWING_BACKWARD = 213;
public static final int NUNCHUK_TILT = 214; public static final int NUNCHUK_TILT = 214;
public static final int NUNCHUK_TILT_FORWARD = 215; public static final int NUNCHUK_TILT_FORWARD = 215;
public static final int NUNCHUK_TILT_BACKWARD = 216; public static final int NUNCHUK_TILT_BACKWARD = 216;
public static final int NUNCHUK_TILT_LEFT = 217; public static final int NUNCHUK_TILT_LEFT = 217;
public static final int NUNCHUK_TILT_RIGHT = 218; public static final int NUNCHUK_TILT_RIGHT = 218;
public static final int NUNCHUK_TILT_MODIFIER = 219; public static final int NUNCHUK_TILT_MODIFIER = 219;
public static final int NUNCHUK_SHAKE_X = 220; public static final int NUNCHUK_SHAKE_X = 220;
public static final int NUNCHUK_SHAKE_Y = 221; public static final int NUNCHUK_SHAKE_Y = 221;
public static final int NUNCHUK_SHAKE_Z = 222; public static final int NUNCHUK_SHAKE_Z = 222;
public static final int CLASSIC_BUTTON_A = 300; public static final int CLASSIC_BUTTON_A = 300;
public static final int CLASSIC_BUTTON_B = 301; public static final int CLASSIC_BUTTON_B = 301;
public static final int CLASSIC_BUTTON_X = 302; public static final int CLASSIC_BUTTON_X = 302;
public static final int CLASSIC_BUTTON_Y = 303; public static final int CLASSIC_BUTTON_Y = 303;
public static final int CLASSIC_BUTTON_MINUS = 304; public static final int CLASSIC_BUTTON_MINUS = 304;
public static final int CLASSIC_BUTTON_PLUS = 305; public static final int CLASSIC_BUTTON_PLUS = 305;
public static final int CLASSIC_BUTTON_HOME = 306; public static final int CLASSIC_BUTTON_HOME = 306;
public static final int CLASSIC_BUTTON_ZL = 307; public static final int CLASSIC_BUTTON_ZL = 307;
public static final int CLASSIC_BUTTON_ZR = 308; public static final int CLASSIC_BUTTON_ZR = 308;
public static final int CLASSIC_DPAD_UP = 309; public static final int CLASSIC_DPAD_UP = 309;
public static final int CLASSIC_DPAD_DOWN = 310; public static final int CLASSIC_DPAD_DOWN = 310;
public static final int CLASSIC_DPAD_LEFT = 311; public static final int CLASSIC_DPAD_LEFT = 311;
public static final int CLASSIC_DPAD_RIGHT = 312; public static final int CLASSIC_DPAD_RIGHT = 312;
public static final int CLASSIC_STICK_LEFT = 313; public static final int CLASSIC_STICK_LEFT = 313;
public static final int CLASSIC_STICK_LEFT_UP = 314; public static final int CLASSIC_STICK_LEFT_UP = 314;
public static final int CLASSIC_STICK_LEFT_DOWN = 315; public static final int CLASSIC_STICK_LEFT_DOWN = 315;
public static final int CLASSIC_STICK_LEFT_LEFT = 316; public static final int CLASSIC_STICK_LEFT_LEFT = 316;
public static final int CLASSIC_STICK_LEFT_RIGHT = 317; public static final int CLASSIC_STICK_LEFT_RIGHT = 317;
public static final int CLASSIC_STICK_RIGHT = 318; public static final int CLASSIC_STICK_RIGHT = 318;
public static final int CLASSIC_STICK_RIGHT_UP = 319; public static final int CLASSIC_STICK_RIGHT_UP = 319;
public static final int CLASSIC_STICK_RIGHT_DOWN = 100; public static final int CLASSIC_STICK_RIGHT_DOWN = 100;
public static final int CLASSIC_STICK_RIGHT_LEFT = 321; public static final int CLASSIC_STICK_RIGHT_LEFT = 321;
public static final int CLASSIC_STICK_RIGHT_RIGHT = 322; public static final int CLASSIC_STICK_RIGHT_RIGHT = 322;
public static final int CLASSIC_TRIGGER_L = 323; public static final int CLASSIC_TRIGGER_L = 323;
public static final int CLASSIC_TRIGGER_R = 324; public static final int CLASSIC_TRIGGER_R = 324;
public static final int GUITAR_BUTTON_MINUS = 400; public static final int GUITAR_BUTTON_MINUS = 400;
public static final int GUITAR_BUTTON_PLUS = 401; public static final int GUITAR_BUTTON_PLUS = 401;
public static final int GUITAR_FRET_GREEN = 402; public static final int GUITAR_FRET_GREEN = 402;
public static final int GUITAR_FRET_RED = 403; public static final int GUITAR_FRET_RED = 403;
public static final int GUITAR_FRET_YELLOW = 404; public static final int GUITAR_FRET_YELLOW = 404;
public static final int GUITAR_FRET_BLUE = 405; public static final int GUITAR_FRET_BLUE = 405;
public static final int GUITAR_FRET_ORANGE = 406; public static final int GUITAR_FRET_ORANGE = 406;
public static final int GUITAR_STRUM_UP = 407; public static final int GUITAR_STRUM_UP = 407;
public static final int GUITAR_STRUM_DOWN = 408; public static final int GUITAR_STRUM_DOWN = 408;
public static final int GUITAR_STICK = 409; public static final int GUITAR_STICK = 409;
public static final int GUITAR_STICK_UP = 410; public static final int GUITAR_STICK_UP = 410;
public static final int GUITAR_STICK_DOWN = 411; public static final int GUITAR_STICK_DOWN = 411;
public static final int GUITAR_STICK_LEFT = 412; public static final int GUITAR_STICK_LEFT = 412;
public static final int GUITAR_STICK_RIGHT = 413; public static final int GUITAR_STICK_RIGHT = 413;
public static final int GUITAR_WHAMMY_BAR = 414; public static final int GUITAR_WHAMMY_BAR = 414;
public static final int DRUMS_BUTTON_MINUS = 500; public static final int DRUMS_BUTTON_MINUS = 500;
public static final int DRUMS_BUTTON_PLUS = 501; public static final int DRUMS_BUTTON_PLUS = 501;
public static final int DRUMS_PAD_RED = 502; public static final int DRUMS_PAD_RED = 502;
public static final int DRUMS_PAD_YELLOW = 503; public static final int DRUMS_PAD_YELLOW = 503;
public static final int DRUMS_PAD_BLUE = 504; public static final int DRUMS_PAD_BLUE = 504;
public static final int DRUMS_PAD_GREEN = 505; public static final int DRUMS_PAD_GREEN = 505;
public static final int DRUMS_PAD_ORANGE = 506; public static final int DRUMS_PAD_ORANGE = 506;
public static final int DRUMS_PAD_BASS = 507; public static final int DRUMS_PAD_BASS = 507;
public static final int DRUMS_STICK = 508; public static final int DRUMS_STICK = 508;
public static final int DRUMS_STICK_UP = 509; public static final int DRUMS_STICK_UP = 509;
public static final int DRUMS_STICK_DOWN = 510; public static final int DRUMS_STICK_DOWN = 510;
public static final int DRUMS_STICK_LEFT = 511; public static final int DRUMS_STICK_LEFT = 511;
public static final int DRUMS_STICK_RIGHT = 512; public static final int DRUMS_STICK_RIGHT = 512;
public static final int TURNTABLE_BUTTON_GREEN_LEFT = 600; public static final int TURNTABLE_BUTTON_GREEN_LEFT = 600;
public static final int TURNTABLE_BUTTON_RED_LEFT = 601; public static final int TURNTABLE_BUTTON_RED_LEFT = 601;
public static final int TURNTABLE_BUTTON_BLUE_LEFT = 602; public static final int TURNTABLE_BUTTON_BLUE_LEFT = 602;
public static final int TURNTABLE_BUTTON_GREEN_RIGHT = 603; public static final int TURNTABLE_BUTTON_GREEN_RIGHT = 603;
public static final int TURNTABLE_BUTTON_RED_RIGHT = 604; public static final int TURNTABLE_BUTTON_RED_RIGHT = 604;
public static final int TURNTABLE_BUTTON_BLUE_RIGHT = 605; public static final int TURNTABLE_BUTTON_BLUE_RIGHT = 605;
public static final int TURNTABLE_BUTTON_MINUS = 606; public static final int TURNTABLE_BUTTON_MINUS = 606;
public static final int TURNTABLE_BUTTON_PLUS = 607; public static final int TURNTABLE_BUTTON_PLUS = 607;
public static final int TURNTABLE_BUTTON_HOME = 608; public static final int TURNTABLE_BUTTON_HOME = 608;
public static final int TURNTABLE_BUTTON_EUPHORIA = 609; public static final int TURNTABLE_BUTTON_EUPHORIA = 609;
public static final int TURNTABLE_TABLE_LEFT = 610; public static final int TURNTABLE_TABLE_LEFT = 610;
public static final int TURNTABLE_TABLE_LEFT_LEFT = 611; public static final int TURNTABLE_TABLE_LEFT_LEFT = 611;
public static final int TURNTABLE_TABLE_LEFT_RIGHT = 612; public static final int TURNTABLE_TABLE_LEFT_RIGHT = 612;
public static final int TURNTABLE_TABLE_RIGHT = 613; public static final int TURNTABLE_TABLE_RIGHT = 613;
public static final int TURNTABLE_TABLE_RIGHT_LEFT = 614; public static final int TURNTABLE_TABLE_RIGHT_LEFT = 614;
public static final int TURNTABLE_TABLE_RIGHT_RIGHT = 615; public static final int TURNTABLE_TABLE_RIGHT_RIGHT = 615;
public static final int TURNTABLE_STICK = 616; public static final int TURNTABLE_STICK = 616;
public static final int TURNTABLE_STICK_UP = 617; public static final int TURNTABLE_STICK_UP = 617;
public static final int TURNTABLE_STICK_DOWN = 618; public static final int TURNTABLE_STICK_DOWN = 618;
public static final int TURNTABLE_STICK_LEFT = 619; public static final int TURNTABLE_STICK_LEFT = 619;
public static final int TURNTABLE_STICK_RIGHT = 620; public static final int TURNTABLE_STICK_RIGHT = 620;
public static final int TURNTABLE_EFFECT_DIAL = 621; public static final int TURNTABLE_EFFECT_DIAL = 621;
public static final int TURNTABLE_CROSSFADE = 622; public static final int TURNTABLE_CROSSFADE = 622;
public static final int TURNTABLE_CROSSFADE_LEFT = 623; public static final int TURNTABLE_CROSSFADE_LEFT = 623;
public static final int TURNTABLE_CROSSFADE_RIGHT = 624; public static final int TURNTABLE_CROSSFADE_RIGHT = 624;
} }
/** /**
* Button states * Button states
*/ */
public static final class ButtonState public static final class ButtonState
{ {
public static final int RELEASED = 0; public static final int RELEASED = 0;
public static final int PRESSED = 1; public static final int PRESSED = 1;
} }
private NativeLibrary() private NativeLibrary()
{ {
// Disallows instantiation. // Disallows instantiation.
} }
/** /**
* Default touchscreen device * Default touchscreen device
*/ */
public static final String TouchScreenDevice = "Touchscreen"; public static final String TouchScreenDevice = "Touchscreen";
/** /**
* Handles button press events for a gamepad. * Handles button press events for a gamepad.
* *
* @param Device The input descriptor of the gamepad. * @param Device The input descriptor of the gamepad.
* @param Button Key code identifying which button was pressed. * @param Button Key code identifying which button was pressed.
* @param Action Mask identifying which action is happening (button pressed down, or button released). * @param Action Mask identifying which action is happening (button pressed down, or button released).
* * @return If we handled the button press.
* @return If we handled the button press. */
*/ public static native boolean onGamePadEvent(String Device, int Button, int Action);
public static native boolean onGamePadEvent(String Device, int Button, int Action);
/** /**
* Handles gamepad movement events. * Handles gamepad movement events.
* *
* @param Device The device ID of the gamepad. * @param Device The device ID of the gamepad.
* @param Axis The axis ID * @param Axis The axis ID
* @param Value The value of the axis represented by the given ID. * @param Value The value of the axis represented by the given ID.
*/ */
public static native void onGamePadMoveEvent(String Device, int Axis, float Value); public static native void onGamePadMoveEvent(String Device, int Axis, float Value);
public static native String GetUserSetting(String gameID, String Section, String Key); 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 SetUserSetting(String gameID, String Section, String Key, String Value);
public static native void InitGameIni(String gameID); public static native void InitGameIni(String gameID);
/** /**
* Gets a value from a key in the given ini-based config file. * 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 configFile The ini-based config file to get the value from.
* @param Section The section key that the actual key is in. * @param Section The section key that the actual key is in.
* @param Key The key to get the value from. * @param Key The key to get the value from.
* @param Default The value to return in the event the given key doesn't exist. * @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.
* @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,
public static native String GetConfig(String configFile, String Section, String Key, String Default); String Default);
/** /**
* Sets a value to a key in the given ini config file. * 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 configFile The ini-based config file to add the value to.
* @param Section The section key for the ini key * @param Section The section key for the ini key
* @param Key The actual ini key to set. * @param Key The actual ini key to set.
* @param Value The string to set the ini key to. * @param Value The string to set the ini key to.
*/ */
public static native void SetConfig(String configFile, String Section, String Key, String Value); public static native void SetConfig(String configFile, String Section, String Key, String Value);
/** /**
* Gets the Dolphin version string. * Gets the Dolphin version string.
* *
* @return the Dolphin version string. * @return the Dolphin version string.
*/ */
public static native String GetVersionString(); public static native String GetVersionString();
public static native String GetGitRevision(); public static native String GetGitRevision();
/** /**
* Saves a screen capture of the game * Saves a screen capture of the game
*/ */
public static native void SaveScreenShot(); public static native void SaveScreenShot();
/** /**
* Saves a game state to the slot number. * Saves a game state to the slot number.
* *
* @param slot The slot location to save state to. * @param slot The slot location to save state to.
* @param wait If false, returns as early as possible. * @param wait If false, returns as early as possible.
* If true, returns once the savestate has been written to disk. * If true, returns once the savestate has been written to disk.
*/ */
public static native void SaveState(int slot, boolean wait); public static native void SaveState(int slot, boolean wait);
/** /**
* Saves a game state to the specified path. * Saves a game state to the specified path.
* *
* @param path The path to save state to. * @param path The path to save state to.
* @param wait If false, returns as early as possible. * @param wait If false, returns as early as possible.
* If true, returns once the savestate has been written to disk. * If true, returns once the savestate has been written to disk.
*/ */
public static native void SaveStateAs(String path, boolean wait); public static native void SaveStateAs(String path, boolean wait);
/** /**
* Loads a game state from the slot number. * Loads a game state from the slot number.
* *
* @param slot The slot location to load state from. * @param slot The slot location to load state from.
*/ */
public static native void LoadState(int slot); public static native void LoadState(int slot);
/** /**
* Loads a game state from the specified path. * Loads a game state from the specified path.
* *
* @param path The path to load state from. * @param path The path to load state from.
*/ */
public static native void LoadStateAs(String path); public static native void LoadStateAs(String path);
/** /**
* Sets the current working user directory * Sets the current working user directory
* If not set, it auto-detects a location * If not set, it auto-detects a location
*/ */
public static native void SetUserDirectory(String directory); public static native void SetUserDirectory(String directory);
/** /**
* Returns the current working user directory * Returns the current working user directory
*/ */
public static native String GetUserDirectory(); public static native String GetUserDirectory();
public static native int DefaultCPUCore(); public static native int DefaultCPUCore();
/** /**
* Begins emulation. * Begins emulation.
*/ */
public static native void Run(String path, boolean firstOpen); public static native void Run(String path, boolean firstOpen);
/** /**
* Begins emulation from the specified savestate. * Begins emulation from the specified savestate.
*/ */
public static native void Run(String path, String savestatePath, boolean deleteSavestate); public static native void Run(String path, String savestatePath, boolean deleteSavestate);
public static native void ChangeDisc(String path); public static native void ChangeDisc(String path);
// Surface Handling // Surface Handling
public static native void SurfaceChanged(Surface surf); public static native void SurfaceChanged(Surface surf);
public static native void SurfaceDestroyed();
/** Unpauses emulation from a paused state. */ public static native void SurfaceDestroyed();
public static native void UnPauseEmulation();
/** Pauses emulation. */ /**
public static native void PauseEmulation(); * Unpauses emulation from a paused state.
*/
public static native void UnPauseEmulation();
/** Stops emulation. */ /**
public static native void StopEmulation(); * Pauses emulation.
*/
public static native void PauseEmulation();
/** Returns true if emulation is running (or is paused). */ /**
public static native boolean IsRunning(); * Stops emulation.
*/
public static native void StopEmulation();
/** /**
* Enables or disables CPU block profiling * Returns true if emulation is running (or is paused).
* @param enable */
*/ public static native boolean IsRunning();
public static native void SetProfiling(boolean enable);
/** /**
* Writes out the block profile results * Enables or disables CPU block profiling
*/ *
public static native void WriteProfileResults(); * @param enable
*/
public static native void SetProfiling(boolean enable);
/** Native EGL functions not exposed by Java bindings **/ /**
public static native void eglBindAPI(int api); * Writes out the block profile results
*/
public static native void WriteProfileResults();
/** /**
* Provides a way to refresh the connections on Wiimotes * Native EGL functions not exposed by Java bindings
*/ **/
public static native void RefreshWiimotes(); public static native void eglBindAPI(int api);
private static boolean alertResult = false; /**
public static boolean displayAlertMsg(final String caption, final String text, final boolean yesNo) * Provides a way to refresh the connections on Wiimotes
{ */
Log.error("[NativeLibrary] Alert: " + text); public static native void RefreshWiimotes();
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, private static boolean alertResult = false;
// 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 public static boolean displayAlertMsg(final String caption, final String text,
.setPositiveButton("Yes", (dialog, whichButton) -> final boolean yesNo)
{ {
alertResult = true; Log.error("[NativeLibrary] Alert: " + text);
dialog.dismiss(); final EmulationActivity emulationActivity = sEmulationActivity.get();
synchronized (lock) boolean result = false;
{ if (emulationActivity == null)
lock.notify(); {
} Log.warning("[NativeLibrary] EmulationActivity is null, can't do panic alert.");
}) }
.setNegativeButton("No", (dialog, whichButton) -> else
{ {
alertResult = false; // Create object used for waiting.
dialog.dismiss(); final Object lock = new Object();
synchronized (lock) AlertDialog.Builder builder = new AlertDialog.Builder(emulationActivity)
{ .setTitle(caption)
lock.notify(); .setMessage(text);
}
});
}
// Show the AlertDialog on the main thread. // If not yes/no dialog just have one button that dismisses modal,
emulationActivity.runOnUiThread(() -> builder.show()); // 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;
// Wait for the lock to notify that it is complete. builder
synchronized (lock) .setPositiveButton("Yes", (dialog, whichButton) ->
{ {
try alertResult = true;
{ dialog.dismiss();
lock.wait(); synchronized (lock)
} {
catch (Exception e) { } lock.notify();
} }
})
.setNegativeButton("No", (dialog, whichButton) ->
{
alertResult = false;
dialog.dismiss();
synchronized (lock)
{
lock.notify();
}
});
}
if (yesNo) // Show the AlertDialog on the main thread.
result = alertResult; emulationActivity.runOnUiThread(() -> builder.show());
}
return result;
}
public static void setEmulationActivity(EmulationActivity emulationActivity) // Wait for the lock to notify that it is complete.
{ synchronized (lock)
Log.verbose("[NativeLibrary] Registering EmulationActivity."); {
sEmulationActivity = new WeakReference<>(emulationActivity); try
} {
lock.wait();
}
catch (Exception e)
{
}
}
public static void clearEmulationActivity() if (yesNo)
{ result = alertResult;
Log.verbose("[NativeLibrary] Unregistering EmulationActivity."); }
return result;
}
sEmulationActivity.clear(); 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();
}
} }

View File

@ -22,113 +22,116 @@ import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver;
*/ */
public class AppLinkActivity extends FragmentActivity public class AppLinkActivity extends FragmentActivity
{ {
private static final String TAG = "AppLinkActivity"; private static final String TAG = "AppLinkActivity";
private AppLinkHelper.PlayAction playAction; private AppLinkHelper.PlayAction playAction;
private DirectoryStateReceiver directoryStateReceiver; private DirectoryStateReceiver directoryStateReceiver;
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = getIntent(); Intent intent = getIntent();
Uri uri = intent.getData(); Uri uri = intent.getData();
Log.v(TAG, uri.toString()); Log.v(TAG, uri.toString());
if (uri.getPathSegments().isEmpty()) if (uri.getPathSegments().isEmpty())
{ {
Log.e(TAG, "Invalid uri " + uri); Log.e(TAG, "Invalid uri " + uri);
finish(); finish();
return; return;
} }
AppLinkHelper.AppLinkAction action = AppLinkHelper.extractAction(uri); AppLinkHelper.AppLinkAction action = AppLinkHelper.extractAction(uri);
switch (action.getAction()) switch (action.getAction())
{ {
case AppLinkHelper.PLAY: case AppLinkHelper.PLAY:
playAction = (AppLinkHelper.PlayAction) action; playAction = (AppLinkHelper.PlayAction) action;
initResources(); initResources();
break; break;
case AppLinkHelper.BROWSE: case AppLinkHelper.BROWSE:
browse(); browse();
break; break;
default: default:
throw new IllegalArgumentException("Invalid Action " + action); throw new IllegalArgumentException("Invalid Action " + action);
} }
} }
/** /**
* Need to init these since they usually occur in the main activity. * Need to init these since they usually occur in the main activity.
*/ */
private void initResources() private void initResources()
{ {
IntentFilter statusIntentFilter = new IntentFilter( IntentFilter statusIntentFilter = new IntentFilter(
DirectoryInitializationService.BROADCAST_ACTION); DirectoryInitializationService.BROADCAST_ACTION);
directoryStateReceiver = directoryStateReceiver =
new DirectoryStateReceiver(directoryInitializationState -> new DirectoryStateReceiver(directoryInitializationState ->
{ {
if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) if (directoryInitializationState ==
{ DirectoryInitializationService.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
play(playAction); {
} play(playAction);
else if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) }
{ else if (directoryInitializationState ==
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) DirectoryInitializationService.DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED)
.show(); {
} Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
else if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) .show();
{ }
Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT) else if (directoryInitializationState ==
.show(); DirectoryInitializationService.DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE)
} {
}); Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT)
.show();
}
});
// Registers the DirectoryStateReceiver and its intent filters // Registers the DirectoryStateReceiver and its intent filters
LocalBroadcastManager.getInstance(this).registerReceiver( LocalBroadcastManager.getInstance(this).registerReceiver(
directoryStateReceiver, directoryStateReceiver,
statusIntentFilter); statusIntentFilter);
DirectoryInitializationService.startService(this); DirectoryInitializationService.startService(this);
GameFileCacheService.startLoad(this); GameFileCacheService.startLoad(this);
} }
/** /**
* Action if channel icon is selected * Action if channel icon is selected
*/ */
private void browse() private void browse()
{ {
Intent openApp = new Intent(this, TvMainActivity.class); Intent openApp = new Intent(this, TvMainActivity.class);
startActivity(openApp); startActivity(openApp);
finish(); finish();
} }
/** /**
* Action if program(game) is selected * Action if program(game) is selected
*/ */
private void play(AppLinkHelper.PlayAction action) private void play(AppLinkHelper.PlayAction action)
{ {
Log.d(TAG, "Playing game " Log.d(TAG, "Playing game "
+ action.getGameId() + action.getGameId()
+ " from channel " + " from channel "
+ action.getChannelId()); + action.getChannelId());
GameFile game = GameFileCacheService.getGameFileByGameId(action.getGameId()); GameFile game = GameFileCacheService.getGameFileByGameId(action.getGameId());
if (game == null) if (game == null)
Log.e(TAG, "Invalid Game: " + action.getGameId()); Log.e(TAG, "Invalid Game: " + action.getGameId());
else else
startGame(game); startGame(game);
finish(); finish();
} }
private void startGame(GameFile game) private void startGame(GameFile game)
{ {
if (directoryStateReceiver != null) if (directoryStateReceiver != null)
{ {
LocalBroadcastManager.getInstance(this).unregisterReceiver(directoryStateReceiver); LocalBroadcastManager.getInstance(this).unregisterReceiver(directoryStateReceiver);
directoryStateReceiver = null; directoryStateReceiver = null;
} }
EmulationActivity.launch(this, game, -1, null); EmulationActivity.launch(this, game, -1, null);
} }
} }

View File

@ -13,16 +13,17 @@ import java.io.File;
public class CustomFilePickerActivity extends FilePickerActivity public class CustomFilePickerActivity extends FilePickerActivity
{ {
@Override @Override
protected AbstractFilePickerFragment<File> getFragment( protected AbstractFilePickerFragment<File> getFragment(
@Nullable final String startPath, final int mode, final boolean allowMultiple, @Nullable final String startPath, final int mode, final boolean allowMultiple,
final boolean allowCreateDir, final boolean allowExistingFile, final boolean allowCreateDir, final boolean allowExistingFile,
final boolean singleClick) final boolean singleClick)
{ {
AbstractFilePickerFragment<File> fragment = new CustomFilePickerFragment(); AbstractFilePickerFragment<File> fragment = new CustomFilePickerFragment();
// startPath is allowed to be null. In that case, default folder should be SD-card and not "/" // 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(), fragment.setArgs(
mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick); startPath != null ? startPath : Environment.getExternalStorageDirectory().getPath(),
return fragment; mode, allowMultiple, allowCreateDir, allowExistingFile, singleClick);
} return fragment;
}
} }

View File

@ -12,10 +12,10 @@ import android.widget.Toast;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag; import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.utils.PicassoUtils; import org.dolphinemu.dolphinemu.utils.PicassoUtils;
import org.dolphinemu.dolphinemu.viewholders.GameViewHolder; import org.dolphinemu.dolphinemu.viewholders.GameViewHolder;
@ -24,186 +24,195 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements public final class GameAdapter extends RecyclerView.Adapter<GameViewHolder> implements
View.OnClickListener, View.OnClickListener,
View.OnLongClickListener View.OnLongClickListener
{ {
private List<GameFile> mGameFiles; private List<GameFile> mGameFiles;
/** /**
* Initializes the adapter's observer, which watches for changes to the dataset. The adapter will * Initializes the adapter's observer, which watches for changes to the dataset. The adapter will
* display no data until swapDataSet is called. * display no data until swapDataSet is called.
*/ */
public GameAdapter() public GameAdapter()
{ {
mGameFiles = new ArrayList<>(); mGameFiles = new ArrayList<>();
} }
/** /**
* Called by the LayoutManager when it is necessary to create a new view. * 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 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. * @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. * @return The created ViewHolder with references to all the child view's members.
*/ */
@Override @Override
public GameViewHolder onCreateViewHolder(ViewGroup parent, int viewType) public GameViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{ {
// Create a new view. // Create a new view.
View gameCard = LayoutInflater.from(parent.getContext()) View gameCard = LayoutInflater.from(parent.getContext())
.inflate(R.layout.card_game, parent, false); .inflate(R.layout.card_game, parent, false);
gameCard.setOnClickListener(this); gameCard.setOnClickListener(this);
gameCard.setOnLongClickListener(this); gameCard.setOnLongClickListener(this);
// Use that view to create a ViewHolder. // Use that view to create a ViewHolder.
return new GameViewHolder(gameCard); return new GameViewHolder(gameCard);
} }
/** /**
* Called by the LayoutManager when a new view is not necessary because we can recycle * 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 * 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.) * 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 holder A ViewHolder representing the view we're recycling.
* @param position The position of the 'new' view in the dataset. * @param position The position of the 'new' view in the dataset.
*/ */
@Override @Override
public void onBindViewHolder(GameViewHolder holder, int position) public void onBindViewHolder(GameViewHolder holder, int position)
{ {
GameFile gameFile = mGameFiles.get(position); GameFile gameFile = mGameFiles.get(position);
PicassoUtils.loadGameBanner(holder.imageScreenshot, gameFile); PicassoUtils.loadGameBanner(holder.imageScreenshot, gameFile);
holder.textGameTitle.setText(gameFile.getTitle()); holder.textGameTitle.setText(gameFile.getTitle());
holder.textCompany.setText(gameFile.getCompany()); holder.textCompany.setText(gameFile.getCompany());
holder.gameFile = gameFile; holder.gameFile = gameFile;
} }
/** /**
* Called by the LayoutManager to find out how much data we have. * Called by the LayoutManager to find out how much data we have.
* *
* @return Size of the dataset. * @return Size of the dataset.
*/ */
@Override @Override
public int getItemCount() public int getItemCount()
{ {
return mGameFiles.size(); return mGameFiles.size();
} }
/** /**
* Tell Android whether or not each item in the dataset has a stable identifier. * Tell Android whether or not each item in the dataset has a stable identifier.
* *
* @param hasStableIds ignored. * @param hasStableIds ignored.
*/ */
@Override @Override
public void setHasStableIds(boolean hasStableIds) public void setHasStableIds(boolean hasStableIds)
{ {
super.setHasStableIds(false); super.setHasStableIds(false);
} }
/** /**
* When a load is finished, call this to replace the existing data * When a load is finished, call this to replace the existing data
* with the newly-loaded data. * with the newly-loaded data.
*/ */
public void swapDataSet(List<GameFile> gameFiles) public void swapDataSet(List<GameFile> gameFiles)
{ {
mGameFiles = gameFiles; mGameFiles = gameFiles;
notifyDataSetChanged(); notifyDataSetChanged();
} }
/** /**
* Launches the game that was clicked on. * Launches the game that was clicked on.
* *
* @param view The card representing the game the user wants to play. * @param view The card representing the game the user wants to play.
*/ */
@Override @Override
public void onClick(View view) public void onClick(View view)
{ {
GameViewHolder holder = (GameViewHolder) view.getTag(); GameViewHolder holder = (GameViewHolder) view.getTag();
EmulationActivity.launch((FragmentActivity) view.getContext(), EmulationActivity.launch((FragmentActivity) view.getContext(),
holder.gameFile, holder.gameFile,
holder.getAdapterPosition(), holder.getAdapterPosition(),
holder.imageScreenshot); holder.imageScreenshot);
} }
/** /**
* Launches the details activity for this Game, using an ID stored in the * Launches the details activity for this Game, using an ID stored in the
* details button's Tag. * details button's Tag.
* *
* @param view The Card button that was long-clicked. * @param view The Card button that was long-clicked.
*/ */
@Override @Override
public boolean onLongClick(View view) public boolean onLongClick(View view)
{ {
FragmentActivity activity = (FragmentActivity) view.getContext(); FragmentActivity activity = (FragmentActivity) view.getContext();
GameViewHolder holder = (GameViewHolder) view.getTag(); GameViewHolder holder = (GameViewHolder) view.getTag();
String gameId = holder.gameFile.getGameId(); String gameId = holder.gameFile.getGameId();
if (gameId.isEmpty()) if (gameId.isEmpty())
{ {
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Game Settings"); builder.setTitle("Game Settings");
builder.setMessage("Files without game IDs don't support game-specific settings."); builder.setMessage("Files without game IDs don't support game-specific settings.");
builder.show(); builder.show();
return true; return true;
} }
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Game Settings") builder.setTitle("Game Settings")
.setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener() { .setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which) { {
switch (which) { public void onClick(DialogInterface dialog, int which)
case 0: {
SettingsActivity.launch(activity, MenuTag.CONFIG, gameId); switch (which)
break; {
case 1: case 0:
SettingsActivity.launch(activity, MenuTag.GRAPHICS, gameId); SettingsActivity.launch(activity, MenuTag.CONFIG, gameId);
break; break;
case 2: case 1:
String path = DirectoryInitializationService.getUserDirectory() + "/GameSettings/" + gameId + ".ini"; SettingsActivity.launch(activity, MenuTag.GRAPHICS, gameId);
File gameSettingsFile = new File(path); break;
if (gameSettingsFile.exists()) case 2:
{ String path =
if (gameSettingsFile.delete()) DirectoryInitializationService.getUserDirectory() + "/GameSettings/" +
{ gameId + ".ini";
Toast.makeText(view.getContext(), "Cleared settings for " + gameId, Toast.LENGTH_SHORT).show(); File gameSettingsFile = new File(path);
} if (gameSettingsFile.exists())
else {
{ if (gameSettingsFile.delete())
Toast.makeText(view.getContext(), "Unable to clear settings for " + gameId, Toast.LENGTH_SHORT).show(); {
} Toast.makeText(view.getContext(), "Cleared settings for " + gameId,
} Toast.LENGTH_SHORT).show();
else }
{ else
Toast.makeText(view.getContext(), "No game settings to delete", Toast.LENGTH_SHORT).show(); {
} Toast.makeText(view.getContext(), "Unable to clear settings for " + gameId,
break; Toast.LENGTH_SHORT).show();
} }
} }
}); else
{
Toast.makeText(view.getContext(), "No game settings to delete",
Toast.LENGTH_SHORT).show();
}
break;
}
}
});
builder.show(); builder.show();
return true; return true;
} }
public static class SpacesItemDecoration extends RecyclerView.ItemDecoration public static class SpacesItemDecoration extends RecyclerView.ItemDecoration
{ {
private int space; private int space;
public SpacesItemDecoration(int space) public SpacesItemDecoration(int space)
{ {
this.space = space; this.space = space;
} }
@Override @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
{ RecyclerView.State state)
outRect.left = space; {
outRect.right = space; outRect.left = space;
outRect.bottom = space; outRect.right = space;
outRect.top = space; outRect.bottom = space;
} outRect.top = space;
} }
}
} }

View File

@ -14,11 +14,11 @@ import android.widget.ImageView;
import android.widget.Toast; import android.widget.Toast;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.PicassoUtils; import org.dolphinemu.dolphinemu.utils.PicassoUtils;
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder; import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
@ -30,117 +30,126 @@ import java.io.File;
*/ */
public final class GameRowPresenter extends Presenter public final class GameRowPresenter extends Presenter
{ {
@Override @Override
public ViewHolder onCreateViewHolder(ViewGroup parent) public ViewHolder onCreateViewHolder(ViewGroup parent)
{ {
// Create a new view. // Create a new view.
ImageCardView gameCard = new ImageCardView(parent.getContext()); ImageCardView gameCard = new ImageCardView(parent.getContext());
gameCard.setMainImageAdjustViewBounds(true); gameCard.setMainImageAdjustViewBounds(true);
gameCard.setMainImageDimensions(240, 336); gameCard.setMainImageDimensions(240, 336);
gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP); gameCard.setMainImageScaleType(ImageView.ScaleType.CENTER_CROP);
gameCard.setFocusable(true); gameCard.setFocusable(true);
gameCard.setFocusableInTouchMode(true); gameCard.setFocusableInTouchMode(true);
// Use that view to create a ViewHolder. // Use that view to create a ViewHolder.
return new TvGameViewHolder(gameCard); return new TvGameViewHolder(gameCard);
} }
@Override @Override
public void onBindViewHolder(ViewHolder viewHolder, Object item) public void onBindViewHolder(ViewHolder viewHolder, Object item)
{ {
TvGameViewHolder holder = (TvGameViewHolder) viewHolder; TvGameViewHolder holder = (TvGameViewHolder) viewHolder;
GameFile gameFile = (GameFile) item; GameFile gameFile = (GameFile) item;
holder.imageScreenshot.setImageDrawable(null); holder.imageScreenshot.setImageDrawable(null);
PicassoUtils.loadGameBanner(holder.imageScreenshot, gameFile); PicassoUtils.loadGameBanner(holder.imageScreenshot, gameFile);
holder.cardParent.setTitleText(gameFile.getTitle()); holder.cardParent.setTitleText(gameFile.getTitle());
holder.cardParent.setContentText(gameFile.getCompany()); holder.cardParent.setContentText(gameFile.getCompany());
holder.gameFile = gameFile; holder.gameFile = gameFile;
// Set the platform-dependent background color of the card // Set the platform-dependent background color of the card
int backgroundId; int backgroundId;
switch (Platform.fromNativeInt(gameFile.getPlatform())) switch (Platform.fromNativeInt(gameFile.getPlatform()))
{ {
case GAMECUBE: case GAMECUBE:
backgroundId = R.drawable.tv_card_background_gamecube; backgroundId = R.drawable.tv_card_background_gamecube;
break; break;
case WII: case WII:
backgroundId = R.drawable.tv_card_background_wii; backgroundId = R.drawable.tv_card_background_wii;
break; break;
case WIIWARE: case WIIWARE:
backgroundId = R.drawable.tv_card_background_wiiware; backgroundId = R.drawable.tv_card_background_wiiware;
break; break;
default: default:
throw new AssertionError("Not reachable."); throw new AssertionError("Not reachable.");
} }
Context context = holder.cardParent.getContext(); Context context = holder.cardParent.getContext();
Drawable background = ContextCompat.getDrawable(context, backgroundId); Drawable background = ContextCompat.getDrawable(context, backgroundId);
holder.cardParent.setInfoAreaBackground(background); holder.cardParent.setInfoAreaBackground(background);
holder.cardParent.setOnLongClickListener(new View.OnLongClickListener() { holder.cardParent.setOnLongClickListener(new View.OnLongClickListener()
@Override {
public boolean onLongClick(View view) @Override
{ public boolean onLongClick(View view)
FragmentActivity activity = (FragmentActivity) view.getContext(); {
String gameId = gameFile.getGameId(); FragmentActivity activity = (FragmentActivity) view.getContext();
String gameId = gameFile.getGameId();
if (gameId.isEmpty()) if (gameId.isEmpty())
{ {
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Game Settings"); builder.setTitle("Game Settings");
builder.setMessage("Files without game IDs don't support game-specific settings."); builder.setMessage("Files without game IDs don't support game-specific settings.");
builder.show(); builder.show();
return true; return true;
} }
AlertDialog.Builder builder = new AlertDialog.Builder(activity); AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setTitle("Game Settings") builder.setTitle("Game Settings")
.setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener() { .setItems(R.array.gameSettingsMenus, new DialogInterface.OnClickListener()
public void onClick(DialogInterface dialog, int which) { {
switch (which) { public void onClick(DialogInterface dialog, int which)
case 0: {
SettingsActivity.launch(activity, MenuTag.CONFIG, gameId); switch (which)
break; {
case 1: case 0:
SettingsActivity.launch(activity, MenuTag.GRAPHICS, gameId); SettingsActivity.launch(activity, MenuTag.CONFIG, gameId);
break; break;
case 2: case 1:
String path = DirectoryInitializationService.getUserDirectory() + "/GameSettings/" + gameId + ".ini"; SettingsActivity.launch(activity, MenuTag.GRAPHICS, gameId);
File gameSettingsFile = new File(path); break;
if (gameSettingsFile.exists()) case 2:
{ String path = DirectoryInitializationService.getUserDirectory() +
if (gameSettingsFile.delete()) "/GameSettings/" + gameId + ".ini";
{ File gameSettingsFile = new File(path);
Toast.makeText(view.getContext(), "Cleared settings for " + gameId, Toast.LENGTH_SHORT).show(); if (gameSettingsFile.exists())
} {
else if (gameSettingsFile.delete())
{ {
Toast.makeText(view.getContext(), "Unable to clear settings for " + gameId, Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(), "Cleared settings for " + gameId,
} Toast.LENGTH_SHORT).show();
} }
else else
{ {
Toast.makeText(view.getContext(), "No game settings to delete", Toast.LENGTH_SHORT).show(); Toast.makeText(view.getContext(),
} "Unable to clear settings for " + gameId, Toast.LENGTH_SHORT)
break; .show();
} }
} }
}); else
{
Toast.makeText(view.getContext(), "No game settings to delete",
Toast.LENGTH_SHORT).show();
}
break;
}
}
});
builder.show(); builder.show();
return true; return true;
} }
}); });
} }
@Override @Override
public void onUnbindViewHolder(ViewHolder viewHolder) public void onUnbindViewHolder(ViewHolder viewHolder)
{ {
// no op // no op
} }
} }

View File

@ -2,9 +2,9 @@ package org.dolphinemu.dolphinemu.adapters;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.style.ImageSpan; import android.text.style.ImageSpan;
@ -15,48 +15,48 @@ import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesFragment;
public class PlatformPagerAdapter extends FragmentPagerAdapter public class PlatformPagerAdapter extends FragmentPagerAdapter
{ {
private Context mContext; private Context mContext;
private final static int[] TAB_ICONS = private final static int[] TAB_ICONS =
{ {
R.drawable.ic_gamecube, R.drawable.ic_gamecube,
R.drawable.ic_wii, R.drawable.ic_wii,
R.drawable.ic_folder // WiiWare TODO Have an icon here. R.drawable.ic_folder // WiiWare TODO Have an icon here.
}; };
public PlatformPagerAdapter(FragmentManager fm, Context context) public PlatformPagerAdapter(FragmentManager fm, Context context)
{ {
super(fm); super(fm);
mContext = context; mContext = context;
} }
@Override @Override
public Fragment getItem(int position) public Fragment getItem(int position)
{ {
return PlatformGamesFragment.newInstance(Platform.fromPosition(position)); return PlatformGamesFragment.newInstance(Platform.fromPosition(position));
} }
@Override @Override
public int getCount() public int getCount()
{ {
return TAB_ICONS.length; return TAB_ICONS.length;
} }
@Override @Override
public CharSequence getPageTitle(int position) public CharSequence getPageTitle(int position)
{ {
// Hax from https://guides.codepath.com/android/Google-Play-Style-Tabs-using-TabLayout#design-support-library // Hax from https://guides.codepath.com/android/Google-Play-Style-Tabs-using-TabLayout#design-support-library
// Apparently a workaround for TabLayout not supporting icons. // Apparently a workaround for TabLayout not supporting icons.
// TODO This workaround will eventually not be necessary; switch to more legit methods when that is the case // TODO This workaround will eventually not be necessary; switch to more legit methods when that is the case
// TODO Also remove additional hax from styles.xml // TODO Also remove additional hax from styles.xml
Drawable drawable = mContext.getResources().getDrawable(TAB_ICONS[position]); Drawable drawable = mContext.getResources().getDrawable(TAB_ICONS[position]);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM); ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BOTTOM);
SpannableString sb = new SpannableString(" "); SpannableString sb = new SpannableString(" ");
sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return sb; return sb;
} }
} }

View File

@ -10,37 +10,37 @@ import org.dolphinemu.dolphinemu.viewholders.TvSettingsViewHolder;
public final class SettingsRowPresenter extends Presenter public final class SettingsRowPresenter extends Presenter
{ {
public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent)
{ {
// Create a new view. // Create a new view.
ImageCardView settingsCard = new ImageCardView(parent.getContext()); ImageCardView settingsCard = new ImageCardView(parent.getContext());
settingsCard.setMainImageAdjustViewBounds(true); settingsCard.setMainImageAdjustViewBounds(true);
settingsCard.setMainImageDimensions(192, 160); settingsCard.setMainImageDimensions(192, 160);
settingsCard.setFocusable(true); settingsCard.setFocusable(true);
settingsCard.setFocusableInTouchMode(true); settingsCard.setFocusableInTouchMode(true);
// Use that view to create a ViewHolder. // Use that view to create a ViewHolder.
return new TvSettingsViewHolder(settingsCard); return new TvSettingsViewHolder(settingsCard);
} }
public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item) public void onBindViewHolder(Presenter.ViewHolder viewHolder, Object item)
{ {
TvSettingsViewHolder holder = (TvSettingsViewHolder) viewHolder; TvSettingsViewHolder holder = (TvSettingsViewHolder) viewHolder;
TvSettingsItem settingsItem = (TvSettingsItem) item; 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.setTitleText(resources.getString(settingsItem.getLabelId()));
holder.cardParent.setMainImage(resources.getDrawable(settingsItem.getIconId(), null)); holder.cardParent.setMainImage(resources.getDrawable(settingsItem.getIconId(), null));
} }
public void onUnbindViewHolder(Presenter.ViewHolder viewHolder) public void onUnbindViewHolder(Presenter.ViewHolder viewHolder)
{ {
// no op // no op
} }
} }

View File

@ -5,14 +5,12 @@ import android.app.Dialog;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
@ -22,63 +20,64 @@ import de.hdodenhof.circleimageview.CircleImageView;
public final class GameDetailsDialog extends DialogFragment public final class GameDetailsDialog extends DialogFragment
{ {
private static final String ARG_GAME_PATH = "game_path"; private static final String ARG_GAME_PATH = "game_path";
public static GameDetailsDialog newInstance(String gamePath) public static GameDetailsDialog newInstance(String gamePath)
{ {
GameDetailsDialog fragment = new GameDetailsDialog(); GameDetailsDialog fragment = new GameDetailsDialog();
Bundle arguments = new Bundle(); Bundle arguments = new Bundle();
arguments.putString(ARG_GAME_PATH, gamePath); arguments.putString(ARG_GAME_PATH, gamePath);
fragment.setArguments(arguments); fragment.setArguments(arguments);
return fragment; return fragment;
} }
@Override @Override
public Dialog onCreateDialog(Bundle savedInstanceState) public Dialog onCreateDialog(Bundle savedInstanceState)
{ {
GameFile gameFile = GameFileCacheService.addOrGet(getArguments().getString(ARG_GAME_PATH)); GameFile gameFile = GameFileCacheService.addOrGet(getArguments().getString(ARG_GAME_PATH));
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater().inflate(R.layout.dialog_game_details, null); ViewGroup contents = (ViewGroup) getActivity().getLayoutInflater()
.inflate(R.layout.dialog_game_details, null);
final ImageView imageGameScreen = contents.findViewById(R.id.image_game_screen); final ImageView imageGameScreen = contents.findViewById(R.id.image_game_screen);
CircleImageView circleBanner = contents.findViewById(R.id.circle_banner); CircleImageView circleBanner = contents.findViewById(R.id.circle_banner);
TextView textTitle = contents.findViewById(R.id.text_game_title); TextView textTitle = contents.findViewById(R.id.text_game_title);
TextView textDescription = contents.findViewById(R.id.text_description); TextView textDescription = contents.findViewById(R.id.text_description);
TextView textCountry = contents.findViewById(R.id.text_country); TextView textCountry = contents.findViewById(R.id.text_country);
TextView textCompany = contents.findViewById(R.id.text_company); TextView textCompany = contents.findViewById(R.id.text_company);
FloatingActionButton buttonLaunch = contents.findViewById(R.id.button_launch); FloatingActionButton buttonLaunch = contents.findViewById(R.id.button_launch);
String country = getResources().getStringArray(R.array.countryNames)[gameFile.getCountry()]; String country = getResources().getStringArray(R.array.countryNames)[gameFile.getCountry()];
textTitle.setText(gameFile.getTitle()); textTitle.setText(gameFile.getTitle());
textDescription.setText(gameFile.getDescription()); textDescription.setText(gameFile.getDescription());
textCountry.setText(country); textCountry.setText(country);
textCompany.setText(gameFile.getCompany()); textCompany.setText(gameFile.getCompany());
buttonLaunch.setOnClickListener(view -> buttonLaunch.setOnClickListener(view ->
{ {
// Start the emulation activity and send the path of the clicked ROM to it. // Start the emulation activity and send the path of the clicked ROM to it.
EmulationActivity.launch(getActivity(), gameFile, -1, imageGameScreen); EmulationActivity.launch(getActivity(), gameFile, -1, imageGameScreen);
}); });
// Fill in the view contents. // Fill in the view contents.
Picasso.with(imageGameScreen.getContext()) Picasso.with(imageGameScreen.getContext())
.load(getArguments().getString(gameFile.getScreenshotPath())) .load(getArguments().getString(gameFile.getScreenshotPath()))
.fit() .fit()
.centerCrop() .centerCrop()
.noFade() .noFade()
.noPlaceholder() .noPlaceholder()
.into(imageGameScreen); .into(imageGameScreen);
circleBanner.setImageResource(R.drawable.no_banner); circleBanner.setImageResource(R.drawable.no_banner);
builder.setView(contents); builder.setView(contents);
return builder.create(); return builder.create();
} }
} }

View File

@ -22,205 +22,209 @@ import java.util.List;
*/ */
public final class MotionAlertDialog extends AlertDialog public final class MotionAlertDialog extends AlertDialog
{ {
// The selected input preference // The selected input preference
private final InputBindingSetting setting; private final InputBindingSetting setting;
private final ArrayList<Float> mPreviousValues = new ArrayList<>(); private final ArrayList<Float> mPreviousValues = new ArrayList<>();
private int mPrevDeviceId = 0; private int mPrevDeviceId = 0;
private boolean mWaitingForEvent = true; private boolean mWaitingForEvent = true;
/** /**
* Constructor * Constructor
* *
* @param context The current {@link Context}. * @param context The current {@link Context}.
* @param setting The Preference to show this dialog for. * @param setting The Preference to show this dialog for.
*/ */
public MotionAlertDialog(Context context, InputBindingSetting setting) public MotionAlertDialog(Context context, InputBindingSetting setting)
{ {
super(context); super(context);
this.setting = setting; this.setting = setting;
} }
public boolean onKeyEvent(int keyCode, KeyEvent event) public boolean onKeyEvent(int keyCode, KeyEvent event)
{ {
Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); Log.debug("[MotionAlertDialog] Received key event: " + event.getAction());
switch (event.getAction()) switch (event.getAction())
{ {
case KeyEvent.ACTION_UP: case KeyEvent.ACTION_UP:
if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode)) if (!ControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode))
{ {
saveKeyInput(event); saveKeyInput(event);
} }
// Even if we ignore the key, we still consume it. Thus return true regardless. // Even if we ignore the key, we still consume it. Thus return true regardless.
return true; return true;
default: default:
return false; return false;
} }
} }
@Override @Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) public boolean onKeyLongPress(int keyCode, KeyEvent event)
{ {
// Option to clear by long back is only needed on the TV interface // Option to clear by long back is only needed on the TV interface
if (TvUtil.isLeanback(getContext())) if (TvUtil.isLeanback(getContext()))
{ {
if (keyCode == KeyEvent.KEYCODE_BACK) if (keyCode == KeyEvent.KEYCODE_BACK)
{ {
clearBinding(); clearBinding();
return true; return true;
} }
} }
return super.onKeyLongPress(keyCode, event); return super.onKeyLongPress(keyCode, event);
} }
@Override @Override
public boolean dispatchKeyEvent(KeyEvent event) public boolean dispatchKeyEvent(KeyEvent event)
{ {
// Handle this key if we care about it, otherwise pass it down the framework // Handle this key if we care about it, otherwise pass it down the framework
return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event);
} }
@Override @Override
public boolean dispatchGenericMotionEvent(MotionEvent event) public boolean dispatchGenericMotionEvent(MotionEvent event)
{ {
// Handle this event if we care about it, otherwise pass it down the framework // Handle this event if we care about it, otherwise pass it down the framework
return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); return onMotionEvent(event) || super.dispatchGenericMotionEvent(event);
} }
private boolean onMotionEvent(MotionEvent event) private boolean onMotionEvent(MotionEvent event)
{ {
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)
return false; return false;
if (event.getAction() != MotionEvent.ACTION_MOVE) if (event.getAction() != MotionEvent.ACTION_MOVE)
return false; return false;
InputDevice input = event.getDevice(); InputDevice input = event.getDevice();
List<InputDevice.MotionRange> motionRanges = input.getMotionRanges(); List<InputDevice.MotionRange> motionRanges = input.getMotionRanges();
if (input.getId() != mPrevDeviceId) if (input.getId() != mPrevDeviceId)
{ {
mPreviousValues.clear(); mPreviousValues.clear();
} }
mPrevDeviceId = input.getId(); mPrevDeviceId = input.getId();
boolean firstEvent = mPreviousValues.isEmpty(); boolean firstEvent = mPreviousValues.isEmpty();
int numMovedAxis = 0; int numMovedAxis = 0;
float axisMoveValue = 0.0f; float axisMoveValue = 0.0f;
InputDevice.MotionRange lastMovedRange = null; InputDevice.MotionRange lastMovedRange = null;
char lastMovedDir = '?'; char lastMovedDir = '?';
if (mWaitingForEvent) if (mWaitingForEvent)
{ {
for (int i = 0; i < motionRanges.size(); i++) for (int i = 0; i < motionRanges.size(); i++)
{ {
InputDevice.MotionRange range = motionRanges.get(i); InputDevice.MotionRange range = motionRanges.get(i);
int axis = range.getAxis(); int axis = range.getAxis();
float origValue = event.getAxisValue(axis); float origValue = event.getAxisValue(axis);
float value = ControllerMappingHelper.scaleAxis(input, axis, origValue); float value = ControllerMappingHelper.scaleAxis(input, axis, origValue);
if (firstEvent) if (firstEvent)
{ {
mPreviousValues.add(value); mPreviousValues.add(value);
} }
else else
{ {
float previousValue = mPreviousValues.get(i); float previousValue = mPreviousValues.get(i);
// Only handle the axes that are not neutral (more than 0.5) // Only handle the axes that are not neutral (more than 0.5)
// but ignore any axis that has a constant value (e.g. always 1) // but ignore any axis that has a constant value (e.g. always 1)
if (Math.abs(value) > 0.5f && value != previousValue) if (Math.abs(value) > 0.5f && value != previousValue)
{ {
// It is common to have multiple axes with the same physical input. For example, // It is common to have multiple axes with the same physical input. For example,
// shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. // 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 // To handle this, we ignore an axis motion that's the exact same as a motion
// we already saw. This way, we ignore axes with two names, but catch the case // we already saw. This way, we ignore axes with two names, but catch the case
// where a joystick is moved in two directions. // where a joystick is moved in two directions.
// ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html
if (value != axisMoveValue) if (value != axisMoveValue)
{ {
axisMoveValue = value; axisMoveValue = value;
numMovedAxis++; numMovedAxis++;
lastMovedRange = range; lastMovedRange = range;
lastMovedDir = value < 0.0f ? '-' : '+'; lastMovedDir = value < 0.0f ? '-' : '+';
} }
} }
// Special case for d-pads (axis value jumps between 0 and 1 without any values // Special case for d-pads (axis value jumps between 0 and 1 without any values
// in between). Without this, the user would need to press the d-pad twice // in between). Without this, the user would need to press the d-pad twice
// due to the first press being caught by the "if (firstEvent)" case further up. // due to the first press being caught by the "if (firstEvent)" case further up.
else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f) else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f)
{ {
numMovedAxis++; numMovedAxis++;
lastMovedRange = range; lastMovedRange = range;
lastMovedDir = previousValue < 0.0f ? '-' : '+'; lastMovedDir = previousValue < 0.0f ? '-' : '+';
} }
} }
mPreviousValues.set(i, value); mPreviousValues.set(i, value);
} }
// If only one axis moved, that's the winner. // If only one axis moved, that's the winner.
if (numMovedAxis == 1) if (numMovedAxis == 1)
{ {
mWaitingForEvent = false; mWaitingForEvent = false;
saveMotionInput(input, lastMovedRange, lastMovedDir); saveMotionInput(input, lastMovedRange, lastMovedDir);
} }
} }
return true; return true;
} }
/** /**
* Saves the provided key input setting both to the INI file (so native code can use it) and as * 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.) * an Android preference (so it persists correctly and is human-readable.)
* *
* @param keyEvent KeyEvent of this key press. * @param keyEvent KeyEvent of this key press.
*/ */
private void saveKeyInput(KeyEvent keyEvent) private void saveKeyInput(KeyEvent keyEvent)
{ {
InputDevice device = keyEvent.getDevice(); InputDevice device = keyEvent.getDevice();
String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode();
String uiString = device.getName() + ": 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 * 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.) * an Android preference (so it persists correctly and is human-readable.)
* *
* @param device InputDevice from which the input event originated. * @param device InputDevice from which the input event originated.
* @param motionRange MotionRange of the movement * @param motionRange MotionRange of the movement
* @param axisDir Either '-' or '+' * @param axisDir Either '-' or '+'
*/ */
private void saveMotionInput(InputDevice device, InputDevice.MotionRange motionRange, char axisDir) 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; 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) * Save the input string to settings and SharedPreferences, then dismiss this Dialog.
{ */
setting.setValue(bind); private void saveInput(String bind, String ui)
{
setting.setValue(bind);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putString(setting.getKey(), ui); editor.putString(setting.getKey(), ui);
editor.apply(); editor.apply();
dismiss(); dismiss();
} }
private void clearBinding() private void clearBinding()
{ {
setting.setValue(""); setting.setValue("");
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.remove(setting.getKey()); editor.remove(setting.getKey());
editor.apply(); editor.apply();
dismiss(); dismiss();
} }
} }

View File

@ -2,27 +2,27 @@ package org.dolphinemu.dolphinemu.features.settings.model;
public final class BooleanSetting extends Setting public final class BooleanSetting extends Setting
{ {
private boolean mValue; private boolean mValue;
public BooleanSetting(String key, String section, boolean value) public BooleanSetting(String key, String section, boolean value)
{ {
super(key, section); super(key, section);
mValue = value; mValue = value;
} }
public boolean getValue() public boolean getValue()
{ {
return mValue; return mValue;
} }
public void setValue(boolean value) public void setValue(boolean value)
{ {
mValue = value; mValue = value;
} }
@Override @Override
public String getValueAsString() public String getValueAsString()
{ {
return mValue ? "True" : "False"; return mValue ? "True" : "False";
} }
} }

View File

@ -2,27 +2,27 @@ package org.dolphinemu.dolphinemu.features.settings.model;
public final class FloatSetting extends Setting public final class FloatSetting extends Setting
{ {
private float mValue; private float mValue;
public FloatSetting(String key, String section, float value) public FloatSetting(String key, String section, float value)
{ {
super(key, section); super(key, section);
mValue = value; mValue = value;
} }
public float getValue() public float getValue()
{ {
return mValue; return mValue;
} }
public void setValue(float value) public void setValue(float value)
{ {
mValue = value; mValue = value;
} }
@Override @Override
public String getValueAsString() public String getValueAsString()
{ {
return Float.toString(mValue); return Float.toString(mValue);
} }
} }

View File

@ -4,41 +4,41 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class IntSetting extends Setting public final class IntSetting extends Setting
{ {
private int mValue; private int mValue;
private MenuTag menuTag; private MenuTag menuTag;
public IntSetting(String key, String section, int value) public IntSetting(String key, String section, int value)
{ {
super(key, section); super(key, section);
mValue = value; mValue = value;
} }
public IntSetting(String key, String section, int value, MenuTag menuTag) public IntSetting(String key, String section, int value, MenuTag menuTag)
{ {
super(key, section); super(key, section);
mValue = value; mValue = value;
this.menuTag = menuTag; this.menuTag = menuTag;
} }
public int getValue() public int getValue()
{ {
return mValue; return mValue;
} }
public void setValue(int value) public void setValue(int value)
{ {
mValue = value; mValue = value;
} }
@Override @Override
public String getValueAsString() public String getValueAsString()
{ {
return Integer.toString(mValue); return Integer.toString(mValue);
} }
public MenuTag getMenuTag() public MenuTag getMenuTag()
{ {
return menuTag; return menuTag;
} }
} }

View File

@ -8,42 +8,40 @@ package org.dolphinemu.dolphinemu.features.settings.model;
*/ */
public abstract class Setting public abstract class Setting
{ {
private String mKey; private String mKey;
private String mSection; private String mSection;
/** /**
* Base constructor. * Base constructor.
* *
* @param key Everything to the left of the = in a line from the ini file. * @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 section The corresponding recent section header; e.g. [Core] or [Enhancements] without the brackets.
*/ */
public Setting(String key, String section) public Setting(String key, String section)
{ {
mKey = key; mKey = key;
mSection = section; mSection = section;
} }
/** /**
* * @return The identifier used to write this setting to the ini file.
* @return The identifier used to write this setting to the ini file. */
*/ public String getKey()
public String getKey() {
{ return mKey;
return mKey; }
}
/** /**
* * @return The name of the header under which this Setting should be written in the ini file.
* @return The name of the header under which this Setting should be written in the ini file. */
*/ public String getSection()
public String getSection() {
{ return mSection;
return mSection; }
}
/** /**
* @return A representation of this Setting's backing value converted to a String (e.g. for serialization). * @return A representation of this Setting's backing value converted to a String (e.g. for serialization).
*/ */
public abstract String getValueAsString(); public abstract String getValueAsString();
} }

View File

@ -8,56 +8,56 @@ import java.util.HashMap;
*/ */
public final class SettingSection public final class SettingSection
{ {
private String mName; private String mName;
private HashMap<String, Setting> mSettings = new HashMap<>(); private HashMap<String, Setting> mSettings = new HashMap<>();
/** /**
* Create a new SettingSection with no Settings in it. * Create a new SettingSection with no Settings in it.
* *
* @param name The header of this section; e.g. [Core] or [Enhancements] without the brackets. * @param name The header of this section; e.g. [Core] or [Enhancements] without the brackets.
*/ */
public SettingSection(String name) public SettingSection(String name)
{ {
mName = name; mName = name;
} }
public String getName() public String getName()
{ {
return mName; return mName;
} }
/** /**
* Convenience method; inserts a value directly into the backing HashMap. * Convenience method; inserts a value directly into the backing HashMap.
* *
* @param setting The Setting to be inserted. * @param setting The Setting to be inserted.
*/ */
public void putSetting(Setting setting) public void putSetting(Setting setting)
{ {
mSettings.put(setting.getKey(), setting); mSettings.put(setting.getKey(), setting);
} }
/** /**
* Convenience method; gets a value directly from the backing HashMap. * Convenience method; gets a value directly from the backing HashMap.
* *
* @param key Used to retrieve the Setting. * @param key Used to retrieve the Setting.
* @return A Setting object (you should probably cast this before using) * @return A Setting object (you should probably cast this before using)
*/ */
public Setting getSetting(String key) public Setting getSetting(String key)
{ {
return mSettings.get(key); return mSettings.get(key);
} }
public HashMap<String, Setting> getSettings() public HashMap<String, Setting> getSettings()
{ {
return mSettings; return mSettings;
} }
public void mergeSection(SettingSection settingSection) public void mergeSection(SettingSection settingSection)
{ {
for (Setting setting : settingSection.mSettings.values()) for (Setting setting : settingSection.mSettings.values())
{ {
putSetting(setting); putSetting(setting);
} }
} }
} }

View File

@ -14,168 +14,174 @@ import java.util.TreeMap;
public class Settings public class Settings
{ {
public static final String SECTION_INI_CORE = "Core"; public static final String SECTION_INI_CORE = "Core";
public static final String SECTION_INI_INTERFACE = "Interface"; public static final String SECTION_INI_INTERFACE = "Interface";
public static final String SECTION_GFX_SETTINGS = "Settings"; public static final String SECTION_GFX_SETTINGS = "Settings";
public static final String SECTION_GFX_ENHANCEMENTS = "Enhancements"; public static final String SECTION_GFX_ENHANCEMENTS = "Enhancements";
public static final String SECTION_GFX_HACKS = "Hacks"; public static final String SECTION_GFX_HACKS = "Hacks";
public static final String SECTION_STEREOSCOPY = "Stereoscopy"; public static final String SECTION_STEREOSCOPY = "Stereoscopy";
public static final String SECTION_WIIMOTE = "Wiimote"; public static final String SECTION_WIIMOTE = "Wiimote";
public static final String SECTION_BINDINGS = "Android"; public static final String SECTION_BINDINGS = "Android";
public static final String SECTION_ANALYTICS = "Analytics"; public static final String SECTION_ANALYTICS = "Analytics";
private String gameId; private String gameId;
private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>(); private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>();
static static
{
configFileSectionsMap.put(SettingsFile.FILE_NAME_DOLPHIN,
Arrays.asList(SECTION_INI_CORE, SECTION_INI_INTERFACE, SECTION_BINDINGS,
SECTION_ANALYTICS));
configFileSectionsMap.put(SettingsFile.FILE_NAME_GFX,
Arrays.asList(SECTION_GFX_SETTINGS, SECTION_GFX_ENHANCEMENTS, SECTION_GFX_HACKS,
SECTION_STEREOSCOPY));
configFileSectionsMap.put(SettingsFile.FILE_NAME_WIIMOTE,
Arrays.asList(SECTION_WIIMOTE + 1, SECTION_WIIMOTE + 2, SECTION_WIIMOTE + 3,
SECTION_WIIMOTE + 4));
}
/**
* A HashMap<String, SettingSection> that constructs a new SettingSection instead of returning null
* when getting a key not already in the map
*/
public static final class SettingsSectionMap extends HashMap<String, SettingSection>
{
@Override
public SettingSection get(Object key)
{ {
configFileSectionsMap.put(SettingsFile.FILE_NAME_DOLPHIN, Arrays.asList(SECTION_INI_CORE, SECTION_INI_INTERFACE, SECTION_BINDINGS, SECTION_ANALYTICS)); if (!(key instanceof String))
configFileSectionsMap.put(SettingsFile.FILE_NAME_GFX, Arrays.asList(SECTION_GFX_SETTINGS, SECTION_GFX_ENHANCEMENTS, SECTION_GFX_HACKS, SECTION_STEREOSCOPY)); {
configFileSectionsMap.put(SettingsFile.FILE_NAME_WIIMOTE, Arrays.asList(SECTION_WIIMOTE + 1, SECTION_WIIMOTE + 2, SECTION_WIIMOTE + 3, SECTION_WIIMOTE + 4)); return null;
}
String stringKey = (String) key;
if (!super.containsKey(stringKey))
{
SettingSection section = new SettingSection(stringKey);
super.put(stringKey, section);
return section;
}
return super.get(key);
}
}
private HashMap<String, SettingSection> sections = new Settings.SettingsSectionMap();
public SettingSection getSection(String sectionName)
{
return sections.get(sectionName);
}
public boolean isEmpty()
{
return sections.isEmpty();
}
public HashMap<String, SettingSection> getSections()
{
return sections;
}
public void loadSettings(SettingsActivityView view)
{
sections = new Settings.SettingsSectionMap();
HashSet<String> filesToExclude = new HashSet<>();
if (!TextUtils.isEmpty(gameId))
{
// for per-game settings, don't load the WiiMoteNew.ini settings
filesToExclude.add(SettingsFile.FILE_NAME_WIIMOTE);
} }
/** loadDolphinSettings(view, filesToExclude);
* A HashMap<String, SettingSection> that constructs a new SettingSection instead of returning null
* when getting a key not already in the map if (!TextUtils.isEmpty(gameId))
*/
public static final class SettingsSectionMap extends HashMap<String, SettingSection>
{ {
@Override loadGenericGameSettings(gameId, view);
public SettingSection get(Object key) loadCustomGameSettings(gameId, view);
}
}
private void loadDolphinSettings(SettingsActivityView view, HashSet<String> filesToExclude)
{
for (Map.Entry<String, List<String>> entry : configFileSectionsMap.entrySet())
{
String fileName = entry.getKey();
if (filesToExclude == null || !filesToExclude.contains(fileName))
{
sections.putAll(SettingsFile.readFile(fileName, view));
}
}
}
private void loadGenericGameSettings(String gameId, SettingsActivityView view)
{
// generic game settings
mergeSections(SettingsFile.readGenericGameSettings(gameId, view));
mergeSections(SettingsFile.readGenericGameSettingsForAllRegions(gameId, view));
}
private void loadCustomGameSettings(String gameId, SettingsActivityView view)
{
// custom game settings
mergeSections(SettingsFile.readCustomGameSettings(gameId, view));
}
private void mergeSections(HashMap<String, SettingSection> updatedSections)
{
for (Map.Entry<String, SettingSection> entry : updatedSections.entrySet())
{
if (sections.containsKey(entry.getKey()))
{
SettingSection originalSection = sections.get(entry.getKey());
SettingSection updatedSection = entry.getValue();
originalSection.mergeSection(updatedSection);
}
else
{
sections.put(entry.getKey(), entry.getValue());
}
}
}
public void loadSettings(String gameId, SettingsActivityView view)
{
this.gameId = gameId;
loadSettings(view);
}
public void saveSettings(SettingsActivityView view)
{
if (TextUtils.isEmpty(gameId))
{
view.showToastMessage("Saved settings to INI files");
for (Map.Entry<String, List<String>> entry : configFileSectionsMap.entrySet())
{
String fileName = entry.getKey();
List<String> sectionNames = entry.getValue();
TreeMap<String, SettingSection> iniSections = new TreeMap<>();
for (String section : sectionNames)
{ {
if (!(key instanceof String)) iniSections.put(section, sections.get(section));
{
return null;
}
String stringKey = (String) key;
if (!super.containsKey(stringKey))
{
SettingSection section = new SettingSection(stringKey);
super.put(stringKey, section);
return section;
}
return super.get(key);
}
}
private HashMap<String, SettingSection> sections = new Settings.SettingsSectionMap();
public SettingSection getSection(String sectionName)
{
return sections.get(sectionName);
}
public boolean isEmpty()
{
return sections.isEmpty();
}
public HashMap<String, SettingSection> getSections()
{
return sections;
}
public void loadSettings(SettingsActivityView view)
{
sections = new Settings.SettingsSectionMap();
HashSet<String> filesToExclude = new HashSet<>();
if (!TextUtils.isEmpty(gameId))
{
// for per-game settings, don't load the WiiMoteNew.ini settings
filesToExclude.add(SettingsFile.FILE_NAME_WIIMOTE);
} }
loadDolphinSettings(view, filesToExclude); SettingsFile.saveFile(fileName, iniSections, view);
}
if (!TextUtils.isEmpty(gameId))
{
loadGenericGameSettings(gameId, view);
loadCustomGameSettings(gameId, view);
}
} }
else
private void loadDolphinSettings(SettingsActivityView view, HashSet<String> filesToExclude)
{ {
for (Map.Entry<String, List<String>> entry : configFileSectionsMap.entrySet()) // custom game settings
{ view.showToastMessage("Saved settings for " + gameId);
String fileName = entry.getKey(); SettingsFile.saveCustomGameSettings(gameId, sections);
if(filesToExclude == null || !filesToExclude.contains(fileName))
{
sections.putAll(SettingsFile.readFile(fileName, view));
}
}
} }
private void loadGenericGameSettings(String gameId, SettingsActivityView view) }
{
// generic game settings
mergeSections(SettingsFile.readGenericGameSettings(gameId, view));
mergeSections(SettingsFile.readGenericGameSettingsForAllRegions(gameId, view));
}
private void loadCustomGameSettings(String gameId, SettingsActivityView view)
{
// custom game settings
mergeSections(SettingsFile.readCustomGameSettings(gameId, view));
}
private void mergeSections(HashMap<String, SettingSection> updatedSections)
{
for (Map.Entry<String, SettingSection> entry : updatedSections.entrySet())
{
if (sections.containsKey(entry.getKey()))
{
SettingSection originalSection = sections.get(entry.getKey());
SettingSection updatedSection = entry.getValue();
originalSection.mergeSection(updatedSection);
}
else
{
sections.put(entry.getKey(), entry.getValue());
}
}
}
public void loadSettings(String gameId, SettingsActivityView view)
{
this.gameId = gameId;
loadSettings(view);
}
public void saveSettings(SettingsActivityView view)
{
if (TextUtils.isEmpty(gameId))
{
view.showToastMessage("Saved settings to INI files");
for (Map.Entry<String, List<String>> entry : configFileSectionsMap.entrySet())
{
String fileName = entry.getKey();
List<String> sectionNames = entry.getValue();
TreeMap<String, SettingSection> iniSections = new TreeMap<>();
for (String section : sectionNames)
{
iniSections.put(section, sections.get(section));
}
SettingsFile.saveFile(fileName, iniSections, view);
}
}
else
{
// custom game settings
view.showToastMessage("Saved settings for " + gameId);
SettingsFile.saveCustomGameSettings(gameId, sections);
}
}
} }

View File

@ -2,27 +2,27 @@ package org.dolphinemu.dolphinemu.features.settings.model;
public final class StringSetting extends Setting public final class StringSetting extends Setting
{ {
private String mValue; private String mValue;
public StringSetting(String key, String section, String value) public StringSetting(String key, String section, String value)
{ {
super(key, section); super(key, section);
mValue = value; mValue = value;
} }
public String getValue() public String getValue()
{ {
return mValue; return mValue;
} }
public void setValue(String value) public void setValue(String value)
{ {
mValue = value; mValue = value;
} }
@Override @Override
public String getValueAsString() public String getValueAsString()
{ {
return mValue; return mValue;
} }
} }

View File

@ -5,51 +5,52 @@ import org.dolphinemu.dolphinemu.features.settings.model.Setting;
public final class CheckBoxSetting extends SettingsItem public final class CheckBoxSetting extends SettingsItem
{ {
private boolean mDefaultValue; private boolean mDefaultValue;
public CheckBoxSetting(String key, String section, int titleId, int descriptionId, boolean defaultValue, Setting setting) public CheckBoxSetting(String key, String section, int titleId, int descriptionId,
{ boolean defaultValue, Setting setting)
super(key, section, setting, titleId, descriptionId); {
mDefaultValue = defaultValue; super(key, section, setting, titleId, descriptionId);
} mDefaultValue = defaultValue;
}
public boolean isChecked() public boolean isChecked()
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
return mDefaultValue; return mDefaultValue;
} }
BooleanSetting setting = (BooleanSetting) getSetting(); BooleanSetting setting = (BooleanSetting) getSetting();
return setting.getValue(); return setting.getValue();
} }
/** /**
* Write a value to the backing boolean. If that boolean was previously null, * 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. * initializes a new one and returns it, so it can be added to the Hashmap.
* *
* @param checked Pretty self explanatory. * @param checked Pretty self explanatory.
* @return null if overwritten successfully; otherwise, a newly created BooleanSetting. * @return null if overwritten successfully; otherwise, a newly created BooleanSetting.
*/ */
public BooleanSetting setChecked(boolean checked) public BooleanSetting setChecked(boolean checked)
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
BooleanSetting setting = new BooleanSetting(getKey(), getSection(), checked); BooleanSetting setting = new BooleanSetting(getKey(), getSection(), checked);
setSetting(setting); setSetting(setting);
return setting; return setting;
} }
else else
{ {
BooleanSetting setting = (BooleanSetting) getSetting(); BooleanSetting setting = (BooleanSetting) getSetting();
setting.setValue(checked); setting.setValue(checked);
return null; return null;
} }
} }
@Override @Override
public int getType() public int getType()
{ {
return TYPE_CHECKBOX; return TYPE_CHECKBOX;
} }
} }

View File

@ -4,14 +4,14 @@ import org.dolphinemu.dolphinemu.features.settings.model.Setting;
public final class HeaderSetting extends SettingsItem public final class HeaderSetting extends SettingsItem
{ {
public HeaderSetting(String key, Setting setting, int titleId, int descriptionId) public HeaderSetting(String key, Setting setting, int titleId, int descriptionId)
{ {
super(key, null, setting, titleId, descriptionId); super(key, null, setting, titleId, descriptionId);
} }
@Override @Override
public int getType() public int getType()
{ {
return SettingsItem.TYPE_HEADER; return SettingsItem.TYPE_HEADER;
} }
} }

View File

@ -5,48 +5,48 @@ import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
public final class InputBindingSetting extends SettingsItem public final class InputBindingSetting extends SettingsItem
{ {
public InputBindingSetting(String key, String section, int titleId, Setting setting) public InputBindingSetting(String key, String section, int titleId, Setting setting)
{ {
super(key, section, setting, titleId, 0); super(key, section, setting, titleId, 0);
} }
public String getValue() public String getValue()
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
return ""; return "";
} }
StringSetting setting = (StringSetting) getSetting(); StringSetting setting = (StringSetting) getSetting();
return setting.getValue(); return setting.getValue();
} }
/** /**
* Write a value to the backing string. If that string was previously null, * 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. * initializes a new one and returns it, so it can be added to the Hashmap.
* *
* @param bind The input that will be bound * @param bind The input that will be bound
* @return null if overwritten successfully; otherwise, a newly created StringSetting. * @return null if overwritten successfully; otherwise, a newly created StringSetting.
*/ */
public StringSetting setValue(String bind) public StringSetting setValue(String bind)
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
StringSetting setting = new StringSetting(getKey(), getSection(), bind); StringSetting setting = new StringSetting(getKey(), getSection(), bind);
setSetting(setting); setSetting(setting);
return setting; return setting;
} }
else else
{ {
StringSetting setting = (StringSetting) getSetting(); StringSetting setting = (StringSetting) getSetting();
setting.setValue(bind); setting.setValue(bind);
return null; return null;
} }
} }
@Override @Override
public int getType() public int getType()
{ {
return TYPE_INPUT_BINDING; return TYPE_INPUT_BINDING;
} }
} }

View File

@ -1,7 +1,7 @@
package org.dolphinemu.dolphinemu.features.settings.model.view; package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
/** /**
* ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments. * ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
@ -12,99 +12,95 @@ import org.dolphinemu.dolphinemu.features.settings.model.Setting;
*/ */
public abstract class SettingsItem public abstract class SettingsItem
{ {
public static final int TYPE_HEADER = 0; public static final int TYPE_HEADER = 0;
public static final int TYPE_CHECKBOX = 1; public static final int TYPE_CHECKBOX = 1;
public static final int TYPE_SINGLE_CHOICE = 2; public static final int TYPE_SINGLE_CHOICE = 2;
public static final int TYPE_SLIDER = 3; public static final int TYPE_SLIDER = 3;
public static final int TYPE_SUBMENU = 4; public static final int TYPE_SUBMENU = 4;
public static final int TYPE_INPUT_BINDING = 5; public static final int TYPE_INPUT_BINDING = 5;
public static final int TYPE_STRING_SINGLE_CHOICE = 6; public static final int TYPE_STRING_SINGLE_CHOICE = 6;
private String mKey; private String mKey;
private String mSection; private String mSection;
private Setting mSetting; private Setting mSetting;
private int mNameId; private int mNameId;
private int mDescriptionId; private int mDescriptionId;
/** /**
* Base constructor. Takes a key / section name in case the third parameter, the Setting, * 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. * 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 key Identifier for the Setting represented by this Item.
* @param section Section to which the Setting belongs. * @param section Section to which the Setting belongs.
* @param setting A possibly-null backing Setting, to be modified on UI events. * @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 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. * @param descriptionId Resource ID for a text string to be displayed as this setting's description.
*/ */
public SettingsItem(String key, String section, Setting setting, int nameId, int descriptionId) public SettingsItem(String key, String section, Setting setting, int nameId, int descriptionId)
{ {
mKey = key; mKey = key;
mSection = section; mSection = section;
mSetting = setting; mSetting = setting;
mNameId = nameId; mNameId = nameId;
mDescriptionId = descriptionId; mDescriptionId = descriptionId;
} }
/** /**
* * @return The identifier for the backing Setting.
* @return The identifier for the backing Setting. */
*/ public String getKey()
public String getKey() {
{ return mKey;
return mKey; }
}
/** /**
* * @return The header under which the backing Setting belongs.
* @return The header under which the backing Setting belongs. */
*/ public String getSection()
public String getSection() {
{ return mSection;
return mSection; }
}
/** /**
* * @return The backing Setting, possibly null.
* @return The backing Setting, possibly null. */
*/ public Setting getSetting()
public Setting getSetting() {
{ return mSetting;
return mSetting; }
}
/** /**
* Replace the backing setting with a new one. Generally used in cases where * Replace the backing setting with a new one. Generally used in cases where
* the backing setting is null. * the backing setting is null.
* *
* @param setting A non-null Setting. * @param setting A non-null Setting.
*/ */
public void setSetting(Setting setting) public void setSetting(Setting setting)
{ {
mSetting = setting; mSetting = setting;
} }
/** /**
* * @return A resource ID for a text string representing this Setting's name.
* @return A resource ID for a text string representing this Setting's name. */
*/ public int getNameId()
public int getNameId() {
{ return mNameId;
return mNameId; }
}
public int getDescriptionId() public int getDescriptionId()
{ {
return mDescriptionId; return mDescriptionId;
} }
/** /**
* Used by {@link SettingsAdapter}'s onCreateViewHolder() * Used by {@link SettingsAdapter}'s onCreateViewHolder()
* method to determine which type of ViewHolder should be created. * method to determine which type of ViewHolder should be created.
* *
* @return An integer (ideally, one of the constants defined in this file) * @return An integer (ideally, one of the constants defined in this file)
*/ */
public abstract int getType(); public abstract int getType();
} }

View File

@ -6,80 +6,82 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class SingleChoiceSetting extends SettingsItem public final class SingleChoiceSetting extends SettingsItem
{ {
private int mDefaultValue; private int mDefaultValue;
private int mChoicesId; private int mChoicesId;
private int mValuesId; private int mValuesId;
private MenuTag menuTag; private MenuTag menuTag;
public SingleChoiceSetting(String key, String section, int titleId, int descriptionId, int choicesId, int valuesId, int defaultValue, Setting setting, MenuTag menuTag) public SingleChoiceSetting(String key, String section, int titleId, int descriptionId,
{ int choicesId, int valuesId, int defaultValue, Setting setting, MenuTag menuTag)
super(key, section, setting, titleId, descriptionId); {
mValuesId = valuesId; super(key, section, setting, titleId, descriptionId);
mChoicesId = choicesId; mValuesId = valuesId;
mDefaultValue = defaultValue; mChoicesId = choicesId;
this.menuTag = menuTag; mDefaultValue = defaultValue;
} this.menuTag = menuTag;
}
public SingleChoiceSetting(String key, String section, int titleId, int descriptionId, int choicesId, int valuesId, int defaultValue, Setting setting) public SingleChoiceSetting(String key, String section, int titleId, int descriptionId,
{ int choicesId, int valuesId, int defaultValue, Setting setting)
this(key, section, titleId, descriptionId, choicesId, valuesId, defaultValue, setting, null); {
} this(key, section, titleId, descriptionId, choicesId, valuesId, defaultValue, setting, null);
}
public int getChoicesId() public int getChoicesId()
{ {
return mChoicesId; return mChoicesId;
} }
public int getValuesId() public int getValuesId()
{ {
return mValuesId; return mValuesId;
} }
public int getSelectedValue() public int getSelectedValue()
{ {
if (getSetting() != null) if (getSetting() != null)
{ {
IntSetting setting = (IntSetting) getSetting(); IntSetting setting = (IntSetting) getSetting();
return setting.getValue(); return setting.getValue();
} }
else else
{ {
return mDefaultValue; return mDefaultValue;
} }
} }
public MenuTag getMenuTag() public MenuTag getMenuTag()
{ {
return menuTag; return menuTag;
} }
/** /**
* Write a value to the backing int. If that int was previously null, * 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. * initializes a new one and returns it, so it can be added to the Hashmap.
* *
* @param selection New value of the int. * @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting. * @return null if overwritten successfully otherwise; a newly created IntSetting.
*/ */
public IntSetting setSelectedValue(int selection) public IntSetting setSelectedValue(int selection)
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
IntSetting setting = new IntSetting(getKey(), getSection(), selection); IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting); setSetting(setting);
return setting; return setting;
} }
else else
{ {
IntSetting setting = (IntSetting) getSetting(); IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection); setting.setValue(selection);
return null; return null;
} }
} }
@Override @Override
public int getType() public int getType()
{ {
return TYPE_SINGLE_CHOICE; return TYPE_SINGLE_CHOICE;
} }
} }

View File

@ -3,117 +3,118 @@ package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting; import org.dolphinemu.dolphinemu.features.settings.model.FloatSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Setting; import org.dolphinemu.dolphinemu.features.settings.model.Setting;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.Log;
public final class SliderSetting extends SettingsItem public final class SliderSetting extends SettingsItem
{ {
private int mMax; private int mMax;
private int mDefaultValue; private int mDefaultValue;
private String mUnits; private String mUnits;
public SliderSetting(String key, String section, int titleId, int descriptionId, int max, String units, int defaultValue, Setting setting) public SliderSetting(String key, String section, int titleId, int descriptionId, int max,
{ String units, int defaultValue, Setting setting)
super(key, section, setting, titleId, descriptionId); {
mMax = max; super(key, section, setting, titleId, descriptionId);
mUnits = units; mMax = max;
mDefaultValue = defaultValue; mUnits = units;
} mDefaultValue = defaultValue;
}
public int getMax() public int getMax()
{ {
return mMax; return mMax;
} }
public int getSelectedValue() public int getSelectedValue()
{ {
Setting setting = getSetting(); Setting setting = getSetting();
if (setting == null) if (setting == null)
{ {
return mDefaultValue; return mDefaultValue;
} }
if (setting instanceof IntSetting) if (setting instanceof IntSetting)
{ {
IntSetting intSetting = (IntSetting) setting; IntSetting intSetting = (IntSetting) setting;
return intSetting.getValue(); return intSetting.getValue();
} }
else if (setting instanceof FloatSetting) else if (setting instanceof FloatSetting)
{ {
FloatSetting floatSetting = (FloatSetting) setting; FloatSetting floatSetting = (FloatSetting) setting;
if (floatSetting.getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT) if (floatSetting.getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT)
|| floatSetting.getKey().equals(SettingsFile.KEY_SPEED_LIMIT)) || floatSetting.getKey().equals(SettingsFile.KEY_SPEED_LIMIT))
{ {
return Math.round(floatSetting.getValue() * 100); return Math.round(floatSetting.getValue() * 100);
} }
else else
{ {
return Math.round(floatSetting.getValue()); return Math.round(floatSetting.getValue());
} }
} }
else else
{ {
Log.error("[SliderSetting] Error casting setting type."); Log.error("[SliderSetting] Error casting setting type.");
return -1; return -1;
} }
} }
/** /**
* Write a value to the backing int. If that int was previously null, * 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. * initializes a new one and returns it, so it can be added to the Hashmap.
* *
* @param selection New value of the int. * @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting. * @return null if overwritten successfully otherwise; a newly created IntSetting.
*/ */
public IntSetting setSelectedValue(int selection) public IntSetting setSelectedValue(int selection)
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
IntSetting setting = new IntSetting(getKey(), getSection(), selection); IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting); setSetting(setting);
return setting; return setting;
} }
else else
{ {
IntSetting setting = (IntSetting) getSetting(); IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection); setting.setValue(selection);
return null; return null;
} }
} }
/** /**
* Write a value to the backing float. If that float was previously 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. * initializes a new one and returns it, so it can be added to the Hashmap.
* *
* @param selection New value of the float. * @param selection New value of the float.
* @return null if overwritten successfully otherwise; a newly created FloatSetting. * @return null if overwritten successfully otherwise; a newly created FloatSetting.
*/ */
public FloatSetting setSelectedValue(float selection) public FloatSetting setSelectedValue(float selection)
{ {
if (getSetting() == null) if (getSetting() == null)
{ {
FloatSetting setting = new FloatSetting(getKey(), getSection(), selection); FloatSetting setting = new FloatSetting(getKey(), getSection(), selection);
setSetting(setting); setSetting(setting);
return setting; return setting;
} }
else else
{ {
FloatSetting setting = (FloatSetting) getSetting(); FloatSetting setting = (FloatSetting) getSetting();
setting.setValue(selection); setting.setValue(selection);
return null; return null;
} }
} }
public String getUnits() public String getUnits()
{ {
return mUnits; return mUnits;
} }
@Override @Override
public int getType() public int getType()
{ {
return TYPE_SLIDER; return TYPE_SLIDER;
} }
} }

View File

@ -5,93 +5,98 @@ import org.dolphinemu.dolphinemu.features.settings.model.StringSetting;
public class StringSingleChoiceSetting extends SettingsItem public class StringSingleChoiceSetting extends SettingsItem
{ {
private String mDefaultValue; private String mDefaultValue;
private String[] mChoicesId; private String[] mChoicesId;
private String[] mValuesId; private String[] mValuesId;
public StringSingleChoiceSetting(String key, String section, int titleId, int descriptionId, String[] choicesId, String[] valuesId, String defaultValue, Setting setting) public StringSingleChoiceSetting(String key, String section, int titleId, int descriptionId,
String[] choicesId, String[] valuesId, String defaultValue, Setting setting)
{
super(key, section, setting, titleId, descriptionId);
mValuesId = valuesId;
mChoicesId = choicesId;
mDefaultValue = defaultValue;
}
public String[] getChoicesId()
{
return mChoicesId;
}
public String[] getValuesId()
{
return mValuesId;
}
public String getValueAt(int index)
{
if (mValuesId == null)
return null;
if (index >= 0 && index < mValuesId.length)
{ {
super(key, section, setting, titleId, descriptionId); return mValuesId[index];
mValuesId = valuesId;
mChoicesId = choicesId;
mDefaultValue = defaultValue;
} }
public String[] getChoicesId() return "";
}
public String getSelectedValue()
{
if (getSetting() != null)
{ {
return mChoicesId; StringSetting setting = (StringSetting) getSetting();
return setting.getValue();
} }
else
public String[] getValuesId()
{ {
return mValuesId; return mDefaultValue;
} }
}
public String getValueAt(int index) public int getSelectValueIndex()
{
String selectedValue = getSelectedValue();
for (int i = 0; i < mValuesId.length; i++)
{ {
if (mValuesId == null) if (mValuesId[i].equals(selectedValue))
return null; {
return i;
if (index >= 0 && index < mValuesId.length) }
{
return mValuesId[index];
}
return "";
} }
public String getSelectedValue() 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 StringSetting setSelectedValue(String selection)
{
if (getSetting() == null)
{ {
if (getSetting() != null) StringSetting setting = new StringSetting(getKey(), getSection(), selection);
{ setSetting(setting);
StringSetting setting = (StringSetting) getSetting(); return setting;
return setting.getValue();
}
else
{
return mDefaultValue;
}
} }
else
public int getSelectValueIndex() {
String selectedValue = getSelectedValue();
for(int i=0;i<mValuesId.length;i++) {
if(mValuesId[i].equals(selectedValue)) {
return i;
}
}
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 StringSetting setSelectedValue(String selection)
{ {
if (getSetting() == null) StringSetting setting = (StringSetting) getSetting();
{ setting.setValue(selection);
StringSetting setting = new StringSetting(getKey(), getSection(), selection); return null;
setSetting(setting);
return setting;
}
else
{
StringSetting setting = (StringSetting) getSetting();
setting.setValue(selection);
return null;
}
} }
}
@Override @Override
public int getType() public int getType()
{ {
return TYPE_STRING_SINGLE_CHOICE; return TYPE_STRING_SINGLE_CHOICE;
} }
} }

View File

@ -5,22 +5,23 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class SubmenuSetting extends SettingsItem public final class SubmenuSetting extends SettingsItem
{ {
private MenuTag mMenuKey; private MenuTag mMenuKey;
public SubmenuSetting(String key, Setting setting, int titleId, int descriptionId, MenuTag menuKey) public SubmenuSetting(String key, Setting setting, int titleId, int descriptionId,
{ MenuTag menuKey)
super(key, null, setting, titleId, descriptionId); {
mMenuKey = menuKey; super(key, null, setting, titleId, descriptionId);
} mMenuKey = menuKey;
}
public MenuTag getMenuKey() public MenuTag getMenuKey()
{ {
return mMenuKey; return mMenuKey;
} }
@Override @Override
public int getType() public int getType()
{ {
return TYPE_SUBMENU; return TYPE_SUBMENU;
} }
} }

View File

@ -2,105 +2,105 @@ package org.dolphinemu.dolphinemu.features.settings.ui;
public enum MenuTag public enum MenuTag
{ {
CONFIG("config"), CONFIG("config"),
CONFIG_GENERAL("config_general"), CONFIG_GENERAL("config_general"),
CONFIG_INTERFACE("config_interface"), CONFIG_INTERFACE("config_interface"),
CONFIG_GAME_CUBE("config_gamecube"), CONFIG_GAME_CUBE("config_gamecube"),
CONFIG_WII("config_wii"), CONFIG_WII("config_wii"),
WIIMOTE("wiimote"), WIIMOTE("wiimote"),
WIIMOTE_EXTENSION("wiimote_extension"), WIIMOTE_EXTENSION("wiimote_extension"),
GCPAD_TYPE("gc_pad_type"), GCPAD_TYPE("gc_pad_type"),
GRAPHICS("graphics"), GRAPHICS("graphics"),
HACKS("hacks"), HACKS("hacks"),
ENHANCEMENTS("enhancements"), ENHANCEMENTS("enhancements"),
STEREOSCOPY("stereoscopy"), STEREOSCOPY("stereoscopy"),
GCPAD_1("gcpad", 0), GCPAD_1("gcpad", 0),
GCPAD_2("gcpad", 1), GCPAD_2("gcpad", 1),
GCPAD_3("gcpad", 2), GCPAD_3("gcpad", 2),
GCPAD_4("gcpad", 3), GCPAD_4("gcpad", 3),
WIIMOTE_1("wiimote", 4), WIIMOTE_1("wiimote", 4),
WIIMOTE_2("wiimote", 5), WIIMOTE_2("wiimote", 5),
WIIMOTE_3("wiimote", 6), WIIMOTE_3("wiimote", 6),
WIIMOTE_4("wiimote", 7), WIIMOTE_4("wiimote", 7),
WIIMOTE_EXTENSION_1("wiimote_extension", 4), WIIMOTE_EXTENSION_1("wiimote_extension", 4),
WIIMOTE_EXTENSION_2("wiimote_extension", 5), WIIMOTE_EXTENSION_2("wiimote_extension", 5),
WIIMOTE_EXTENSION_3("wiimote_extension", 6), WIIMOTE_EXTENSION_3("wiimote_extension", 6),
WIIMOTE_EXTENSION_4("wiimote_extension", 7); WIIMOTE_EXTENSION_4("wiimote_extension", 7);
private String tag; private String tag;
private int subType = -1; private int subType = -1;
MenuTag(String tag) MenuTag(String tag)
{
this.tag = tag;
}
MenuTag(String tag, int subtype)
{
this.tag = tag;
this.subType = subtype;
}
@Override
public String toString()
{
if (subType != -1)
{ {
this.tag = tag; return tag + subType;
} }
MenuTag(String tag, int subtype) return tag;
}
public String getTag()
{
return tag;
}
public int getSubType()
{
return subType;
}
public boolean isGCPadMenu()
{
return this == GCPAD_1 || this == GCPAD_2 || this == GCPAD_3 || this == GCPAD_4;
}
public boolean isWiimoteMenu()
{
return this == WIIMOTE_1 || this == WIIMOTE_2 || this == WIIMOTE_3 || this == WIIMOTE_4;
}
public boolean isWiimoteExtensionMenu()
{
return this == WIIMOTE_EXTENSION_1 || this == WIIMOTE_EXTENSION_2
|| this == WIIMOTE_EXTENSION_3 || this == WIIMOTE_EXTENSION_4;
}
public static MenuTag getGCPadMenuTag(int subtype)
{
return getMenuTag("gcpad", subtype);
}
public static MenuTag getWiimoteMenuTag(int subtype)
{
return getMenuTag("wiimote", subtype);
}
public static MenuTag getWiimoteExtensionMenuTag(int subtype)
{
return getMenuTag("wiimote_extension", subtype);
}
private static MenuTag getMenuTag(String tag, int subtype)
{
for (MenuTag menuTag : MenuTag.values())
{ {
this.tag = tag; if (menuTag.tag.equals(tag) && menuTag.subType == subtype) return menuTag;
this.subType = subtype;
} }
@Override throw new IllegalArgumentException("You are asking for a menu that is not available or " +
public String toString() "passing a wrong subtype");
{ }
if (subType != -1)
{
return tag + subType;
}
return tag;
}
public String getTag()
{
return tag;
}
public int getSubType()
{
return subType;
}
public boolean isGCPadMenu()
{
return this == GCPAD_1 || this == GCPAD_2 || this == GCPAD_3 || this == GCPAD_4;
}
public boolean isWiimoteMenu()
{
return this == WIIMOTE_1 || this == WIIMOTE_2 || this == WIIMOTE_3 || this == WIIMOTE_4;
}
public boolean isWiimoteExtensionMenu()
{
return this == WIIMOTE_EXTENSION_1 || this == WIIMOTE_EXTENSION_2
|| this == WIIMOTE_EXTENSION_3 || this == WIIMOTE_EXTENSION_4;
}
public static MenuTag getGCPadMenuTag(int subtype)
{
return getMenuTag("gcpad", subtype);
}
public static MenuTag getWiimoteMenuTag(int subtype)
{
return getMenuTag("wiimote", subtype);
}
public static MenuTag getWiimoteExtensionMenuTag(int subtype)
{
return getMenuTag("wiimote_extension", subtype);
}
private static MenuTag getMenuTag(String tag, int subtype)
{
for (MenuTag menuTag : MenuTag.values())
{
if (menuTag.tag.equals(tag) && menuTag.subType == subtype) return menuTag;
}
throw new IllegalArgumentException("You are asking for a menu that is not available or " +
"passing a wrong subtype");
}
} }

View File

@ -15,247 +15,248 @@ import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.SettingSection;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver; import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver;
import java.util.HashMap;
public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView
{ {
private static final String ARG_MENU_TAG = "menu_tag"; private static final String ARG_MENU_TAG = "menu_tag";
private static final String ARG_GAME_ID = "game_id"; private static final String ARG_GAME_ID = "game_id";
private static final String FRAGMENT_TAG = "settings"; private static final String FRAGMENT_TAG = "settings";
private SettingsActivityPresenter mPresenter = new SettingsActivityPresenter(this); private SettingsActivityPresenter mPresenter = new SettingsActivityPresenter(this);
private ProgressDialog dialog; private ProgressDialog dialog;
public static void launch(Context context, MenuTag menuTag, String gameId) public static void launch(Context context, MenuTag menuTag, String gameId)
{ {
Intent settings = new Intent(context, SettingsActivity.class); Intent settings = new Intent(context, SettingsActivity.class);
settings.putExtra(ARG_MENU_TAG, menuTag); settings.putExtra(ARG_MENU_TAG, menuTag);
settings.putExtra(ARG_GAME_ID, gameId); settings.putExtra(ARG_GAME_ID, gameId);
context.startActivity(settings); context.startActivity(settings);
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings); setContentView(R.layout.activity_settings);
Intent launcher = getIntent(); Intent launcher = getIntent();
String gameID = launcher.getStringExtra(ARG_GAME_ID); String gameID = launcher.getStringExtra(ARG_GAME_ID);
MenuTag menuTag = (MenuTag) launcher.getSerializableExtra(ARG_MENU_TAG); MenuTag menuTag = (MenuTag) launcher.getSerializableExtra(ARG_MENU_TAG);
mPresenter.onCreate(savedInstanceState, menuTag, gameID); mPresenter.onCreate(savedInstanceState, menuTag, gameID);
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) public boolean onCreateOptionsMenu(Menu menu)
{ {
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_settings, menu); inflater.inflate(R.menu.menu_settings, menu);
return true; return true;
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) public boolean onOptionsItemSelected(MenuItem item)
{ {
return mPresenter.handleOptionsItem(item.getItemId()); return mPresenter.handleOptionsItem(item.getItemId());
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) protected void onSaveInstanceState(Bundle outState)
{ {
// Critical: If super method is not called, rotations will be busted. // Critical: If super method is not called, rotations will be busted.
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
mPresenter.saveState(outState); mPresenter.saveState(outState);
} }
@Override @Override
protected void onStart() protected void onStart()
{ {
super.onStart(); super.onStart();
mPresenter.onStart(); mPresenter.onStart();
} }
/** /**
* If this is called, the user has left the settings screen (potentially through the * 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 * home button) and will expect their changes to be persisted. So we kick off an
* IntentService which will do so on a background thread. * IntentService which will do so on a background thread.
*/ */
@Override @Override
protected void onStop() protected void onStop()
{ {
super.onStop(); super.onStop();
mPresenter.onStop(isFinishing()); mPresenter.onStop(isFinishing());
} }
@Override @Override
public void onBackPressed() public void onBackPressed()
{ {
mPresenter.onBackPressed(); mPresenter.onBackPressed();
} }
@Override @Override
public void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameID) public void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack,
{ String gameID)
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
if (addToStack) if (addToStack)
{ {
if (areSystemAnimationsEnabled()) if (areSystemAnimationsEnabled())
{ {
transaction.setCustomAnimations( transaction.setCustomAnimations(
R.animator.settings_enter, R.animator.settings_enter,
R.animator.settings_exit, R.animator.settings_exit,
R.animator.settings_pop_enter, R.animator.settings_pop_enter,
R.animator.setttings_pop_exit); R.animator.setttings_pop_exit);
} }
transaction.addToBackStack(null); transaction.addToBackStack(null);
mPresenter.addToStack(); mPresenter.addToStack();
} }
transaction.replace(R.id.frame_content, SettingsFragment.newInstance(menuTag, gameID, extras), FRAGMENT_TAG); transaction.replace(R.id.frame_content, SettingsFragment.newInstance(menuTag, gameID, extras),
FRAGMENT_TAG);
transaction.commit(); transaction.commit();
} }
private boolean areSystemAnimationsEnabled() private boolean areSystemAnimationsEnabled()
{ {
float duration = Settings.Global.getFloat( float duration = Settings.Global.getFloat(
getContentResolver(), getContentResolver(),
Settings.Global.ANIMATOR_DURATION_SCALE, 1); Settings.Global.ANIMATOR_DURATION_SCALE, 1);
float transition = Settings.Global.getFloat( float transition = Settings.Global.getFloat(
getContentResolver(), getContentResolver(),
Settings.Global.TRANSITION_ANIMATION_SCALE, 1); Settings.Global.TRANSITION_ANIMATION_SCALE, 1);
return duration != 0 && transition != 0; return duration != 0 && transition != 0;
} }
@Override @Override
public void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter) public void startDirectoryInitializationService(DirectoryStateReceiver receiver,
{ IntentFilter filter)
LocalBroadcastManager.getInstance(this).registerReceiver( {
receiver, LocalBroadcastManager.getInstance(this).registerReceiver(
filter); receiver,
DirectoryInitializationService.startService(this); filter);
} DirectoryInitializationService.startService(this);
}
@Override @Override
public void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver) public void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver)
{ {
LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
} }
@Override @Override
public void showLoading() public void showLoading()
{ {
if (dialog == null) if (dialog == null)
{ {
dialog = new ProgressDialog(this); dialog = new ProgressDialog(this);
dialog.setMessage(getString(R.string.load_settings)); dialog.setMessage(getString(R.string.load_settings));
dialog.setIndeterminate(true); dialog.setIndeterminate(true);
} }
dialog.show(); dialog.show();
} }
@Override @Override
public void hideLoading() public void hideLoading()
{ {
dialog.dismiss(); dialog.dismiss();
} }
@Override @Override
public void showPermissionNeededHint() public void showPermissionNeededHint()
{ {
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
.show(); .show();
} }
@Override @Override
public void showExternalStorageNotMountedHint() public void showExternalStorageNotMountedHint()
{ {
Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT) Toast.makeText(this, R.string.external_storage_not_mounted, Toast.LENGTH_SHORT)
.show(); .show();
} }
@Override @Override
public org.dolphinemu.dolphinemu.features.settings.model.Settings getSettings() public org.dolphinemu.dolphinemu.features.settings.model.Settings getSettings()
{ {
return mPresenter.getSettings(); return mPresenter.getSettings();
} }
@Override @Override
public void setSettings(org.dolphinemu.dolphinemu.features.settings.model.Settings settings) public void setSettings(org.dolphinemu.dolphinemu.features.settings.model.Settings settings)
{ {
mPresenter.setSettings(settings); mPresenter.setSettings(settings);
} }
@Override @Override
public void onSettingsFileLoaded(org.dolphinemu.dolphinemu.features.settings.model.Settings settings) public void onSettingsFileLoaded(
{ org.dolphinemu.dolphinemu.features.settings.model.Settings settings)
SettingsFragmentView fragment = getFragment(); {
SettingsFragmentView fragment = getFragment();
if (fragment != null) if (fragment != null)
{ {
fragment.onSettingsFileLoaded(settings); fragment.onSettingsFileLoaded(settings);
} }
} }
@Override @Override
public void onSettingsFileNotFound() public void onSettingsFileNotFound()
{ {
SettingsFragmentView fragment = getFragment(); SettingsFragmentView fragment = getFragment();
if (fragment != null) if (fragment != null)
{ {
fragment.loadDefaultSettings(); fragment.loadDefaultSettings();
} }
} }
@Override @Override
public void showToastMessage(String message) public void showToastMessage(String message)
{ {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
} }
@Override @Override
public void popBackStack() public void popBackStack()
{ {
getSupportFragmentManager().popBackStackImmediate(); getSupportFragmentManager().popBackStackImmediate();
} }
@Override @Override
public void onSettingChanged() public void onSettingChanged()
{ {
mPresenter.onSettingChanged(); mPresenter.onSettingChanged();
} }
@Override @Override
public void onGcPadSettingChanged(MenuTag key, int value) public void onGcPadSettingChanged(MenuTag key, int value)
{ {
mPresenter.onGcPadSettingChanged(key, value); mPresenter.onGcPadSettingChanged(key, value);
} }
@Override @Override
public void onWiimoteSettingChanged(MenuTag section, int value) public void onWiimoteSettingChanged(MenuTag section, int value)
{ {
mPresenter.onWiimoteSettingChanged(section, value); mPresenter.onWiimoteSettingChanged(section, value);
} }
@Override @Override
public void onExtensionSettingChanged(MenuTag menuTag, int value) public void onExtensionSettingChanged(MenuTag menuTag, int value)
{ {
mPresenter.onExtensionSettingChanged(menuTag, value); mPresenter.onExtensionSettingChanged(menuTag, value);
} }
private SettingsFragment getFragment() private SettingsFragment getFragment()
{ {
return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
} }
} }

View File

@ -13,194 +13,197 @@ import org.dolphinemu.dolphinemu.utils.Log;
public final class SettingsActivityPresenter public final class SettingsActivityPresenter
{ {
private static final String KEY_SHOULD_SAVE = "should_save"; private static final String KEY_SHOULD_SAVE = "should_save";
private SettingsActivityView mView; private SettingsActivityView mView;
private Settings mSettings = new Settings(); private Settings mSettings = new Settings();
private int mStackCount; private int mStackCount;
private boolean mShouldSave; private boolean mShouldSave;
private DirectoryStateReceiver directoryStateReceiver; private DirectoryStateReceiver directoryStateReceiver;
private MenuTag menuTag; private MenuTag menuTag;
private String gameId; private String gameId;
SettingsActivityPresenter(SettingsActivityView view) SettingsActivityPresenter(SettingsActivityView view)
{
mView = view;
}
public void onCreate(Bundle savedInstanceState, MenuTag menuTag, String gameId)
{
if (savedInstanceState == null)
{ {
mView = view; this.menuTag = menuTag;
this.gameId = gameId;
}
else
{
mShouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE);
}
}
public void onStart()
{
prepareDolphinDirectoriesIfNeeded();
}
private void loadSettingsUI()
{
if (mSettings.isEmpty())
{
if (!TextUtils.isEmpty(gameId))
{
mSettings.loadSettings(gameId, mView);
}
else
{
mSettings.loadSettings(mView);
}
} }
public void onCreate(Bundle savedInstanceState, MenuTag menuTag, String gameId) mView.showSettingsFragment(menuTag, null, false, gameId);
mView.onSettingsFileLoaded(mSettings);
}
private void prepareDolphinDirectoriesIfNeeded()
{
if (DirectoryInitializationService.areDolphinDirectoriesReady())
{ {
if (savedInstanceState == null) loadSettingsUI();
{ }
this.menuTag = menuTag; else
this.gameId = gameId; {
} mView.showLoading();
else IntentFilter statusIntentFilter = new IntentFilter(
{ DirectoryInitializationService.BROADCAST_ACTION);
mShouldSave = savedInstanceState.getBoolean(KEY_SHOULD_SAVE);
} 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);
}
}
public void setSettings(Settings settings)
{
mSettings = settings;
}
public Settings getSettings()
{
return mSettings;
}
public void onStop(boolean finishing)
{
if (directoryStateReceiver != null)
{
mView.stopListeningToDirectoryInitializationService(directoryStateReceiver);
directoryStateReceiver = null;
} }
public void onStart() if (mSettings != null && finishing && mShouldSave)
{ {
prepareDolphinDirectoriesIfNeeded(); Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...");
mSettings.saveSettings(mView);
}
}
public void addToStack()
{
mStackCount++;
}
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;
} }
private void loadSettingsUI() return false;
}
public void onSettingChanged()
{
mShouldSave = true;
}
public void saveState(Bundle outState)
{
outState.putBoolean(KEY_SHOULD_SAVE, mShouldSave);
}
public void onGcPadSettingChanged(MenuTag key, int value)
{
if (value != 0) // Not disabled
{ {
if (mSettings.isEmpty()) Bundle bundle = new Bundle();
{ bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value / 6);
if (!TextUtils.isEmpty(gameId)) mView.showSettingsFragment(key, bundle, true, gameId);
{
mSettings.loadSettings(gameId, mView);
}
else
{
mSettings.loadSettings(mView);
}
}
mView.showSettingsFragment(menuTag, null, false, gameId);
mView.onSettingsFileLoaded(mSettings);
} }
}
private void prepareDolphinDirectoriesIfNeeded() public void onWiimoteSettingChanged(MenuTag menuTag, int value)
{
switch (value)
{ {
if (DirectoryInitializationService.areDolphinDirectoriesReady()) case 1:
{ mView.showSettingsFragment(menuTag, null, true, gameId);
loadSettingsUI(); break;
}
else
{
mView.showLoading();
IntentFilter statusIntentFilter = new IntentFilter(
DirectoryInitializationService.BROADCAST_ACTION);
directoryStateReceiver = case 2:
new DirectoryStateReceiver(directoryInitializationState -> mView.showToastMessage("Please make sure Continuous Scanning is enabled in Core Settings.");
{ break;
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);
}
} }
}
public void setSettings(Settings settings) public void onExtensionSettingChanged(MenuTag menuTag, int value)
{
if (value != 0) // None
{ {
mSettings = settings; Bundle bundle = new Bundle();
} bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value);
mView.showSettingsFragment(menuTag, bundle, true, gameId);
public Settings getSettings()
{
return mSettings;
}
public void onStop(boolean finishing)
{
if (directoryStateReceiver != null)
{
mView.stopListeningToDirectoryInitializationService(directoryStateReceiver);
directoryStateReceiver = null;
}
if (mSettings != null && finishing && mShouldSave)
{
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...");
mSettings.saveSettings(mView);
}
}
public void addToStack()
{
mStackCount++;
}
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;
}
return false;
}
public void onSettingChanged()
{
mShouldSave = true;
}
public void saveState(Bundle outState)
{
outState.putBoolean(KEY_SHOULD_SAVE, mShouldSave);
}
public void onGcPadSettingChanged(MenuTag key, int value)
{
if (value != 0) // Not disabled
{
Bundle bundle = new Bundle();
bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value / 6);
mView.showSettingsFragment(key, bundle, true, gameId);
}
}
public void onWiimoteSettingChanged(MenuTag menuTag, int value)
{
switch (value)
{
case 1:
mView.showSettingsFragment(menuTag, null, true, gameId);
break;
case 2:
mView.showToastMessage("Please make sure Continuous Scanning is enabled in Core Settings.");
break;
}
}
public void onExtensionSettingChanged(MenuTag menuTag, int value)
{
if (value != 0) // None
{
Bundle bundle = new Bundle();
bundle.putInt(SettingsFragmentPresenter.ARG_CONTROLLER_TYPE, value);
mView.showSettingsFragment(menuTag, bundle, true, gameId);
}
} }
}
} }

View File

@ -11,126 +11,126 @@ import org.dolphinemu.dolphinemu.utils.DirectoryStateReceiver;
*/ */
public interface SettingsActivityView public interface SettingsActivityView
{ {
/** /**
* Show a new SettingsFragment. * Show a new SettingsFragment.
* *
* @param menuTag Identifier for the settings group that should be displayed. * @param menuTag Identifier for the settings group that should be displayed.
* @param addToStack Whether or not this fragment should replace a previous one. * @param addToStack Whether or not this fragment should replace a previous one.
*/ */
void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameId); void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameId);
/** /**
* Called by a contained Fragment to get access to the Setting HashMap * 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 * loaded from disk, so that each Fragment doesn't need to perform its own
* read operation. * read operation.
* *
* @return A possibly null HashMap of Settings. * @return A possibly null HashMap of Settings.
*/ */
Settings getSettings(); Settings getSettings();
/** /**
* Used to provide the Activity with Settings HashMaps if a Fragment already * 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, * 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. * but the Activity will, so the Activity needs to have its HashMaps resupplied.
* *
* @param settings The ArrayList of all the Settings HashMaps. * @param settings The ArrayList of all the Settings HashMaps.
*/ */
void setSettings(Settings settings); void setSettings(Settings settings);
/** /**
* Called when an asynchronous load operation completes. * Called when an asynchronous load operation completes.
* *
* @param settings The (possibly null) result of the ini load operation. * @param settings The (possibly null) result of the ini load operation.
*/ */
void onSettingsFileLoaded(Settings settings); void onSettingsFileLoaded(Settings settings);
/** /**
* Called when an asynchronous load operation fails. * Called when an asynchronous load operation fails.
*/ */
void onSettingsFileNotFound(); void onSettingsFileNotFound();
/** /**
* Display a popup text message on screen. * Display a popup text message on screen.
* *
* @param message The contents of the onscreen message. * @param message The contents of the onscreen message.
*/ */
void showToastMessage(String message); void showToastMessage(String message);
/** /**
* Show the previous fragment. * Show the previous fragment.
*/ */
void popBackStack(); void popBackStack();
/** /**
* End the activity. * End the activity.
*/ */
void finish(); void finish();
/** /**
* Called by a containing Fragment to tell the Activity that a setting was changed; * 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. * unless this has been called, the Activity will not save to disk.
*/ */
void onSettingChanged(); void onSettingChanged();
/** /**
* Called by a containing Fragment to tell the containing Activity that a GCPad's setting * Called by a containing Fragment to tell the containing Activity that a GCPad's setting
* was modified. * was modified.
* *
* @param menuTag Identifier for the GCPad that was modified. * @param menuTag Identifier for the GCPad that was modified.
* @param value New setting for the GCPad. * @param value New setting for the GCPad.
*/ */
void onGcPadSettingChanged(MenuTag menuTag, int value); void onGcPadSettingChanged(MenuTag menuTag, int value);
/** /**
* Called by a containing Fragment to tell the containing Activity that a Wiimote's setting * Called by a containing Fragment to tell the containing Activity that a Wiimote's setting
* was modified. * was modified.
* *
* @param menuTag Identifier for Wiimote that was modified. * @param menuTag Identifier for Wiimote that was modified.
* @param value New setting for the Wiimote. * @param value New setting for the Wiimote.
*/ */
void onWiimoteSettingChanged(MenuTag menuTag, int value); void onWiimoteSettingChanged(MenuTag menuTag, int value);
/** /**
* Called by a containing Fragment to tell the containing Activity that an extension setting * Called by a containing Fragment to tell the containing Activity that an extension setting
* was modified. * was modified.
* *
* @param menuTag Identifier for the extension that was modified. * @param menuTag Identifier for the extension that was modified.
* @param value New setting for the extension. * @param value New setting for the extension.
*/ */
void onExtensionSettingChanged(MenuTag menuTag, int value); void onExtensionSettingChanged(MenuTag menuTag, int value);
/** /**
* Show loading dialog while loading the settings * Show loading dialog while loading the settings
*/ */
void showLoading(); void showLoading();
/** /**
* Hide the loading the dialog * Hide the loading the dialog
*/ */
void hideLoading(); void hideLoading();
/** /**
* Show a hint to the user that the app needs write to external storage access * Show a hint to the user that the app needs write to external storage access
*/ */
void showPermissionNeededHint(); void showPermissionNeededHint();
/** /**
* Show a hint to the user that the app needs the external storage to be mounted * Show a hint to the user that the app needs the external storage to be mounted
*/ */
void showExternalStorageNotMountedHint(); void showExternalStorageNotMountedHint();
/** /**
* Start the DirectoryInitializationService and listen for the result. * Start the DirectoryInitializationService and listen for the result.
* *
* @param receiver the broadcast receiver for the DirectoryInitializationService * @param receiver the broadcast receiver for the DirectoryInitializationService
* @param filter the Intent broadcasts to be received. * @param filter the Intent broadcasts to be received.
*/ */
void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter); void startDirectoryInitializationService(DirectoryStateReceiver receiver, IntentFilter filter);
/** /**
* Stop listening to the DirectoryInitializationService. * Stop listening to the DirectoryInitializationService.
* *
* @param receiver The broadcast receiver to unregister. * @param receiver The broadcast receiver to unregister.
*/ */
void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver); void stopListeningToDirectoryInitializationService(DirectoryStateReceiver receiver);
} }

View File

@ -33,406 +33,417 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHold
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SliderViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SubmenuViewHolder;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.Log;
import java.util.ArrayList; import java.util.ArrayList;
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder> public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener
{ {
private SettingsFragmentView mView; private SettingsFragmentView mView;
private Context mContext; private Context mContext;
private ArrayList<SettingsItem> mSettings; private ArrayList<SettingsItem> mSettings;
private SettingsItem mClickedItem; private SettingsItem mClickedItem;
private int mSeekbarProgress; private int mSeekbarProgress;
private AlertDialog mDialog; private AlertDialog mDialog;
private TextView mTextSliderValue; private TextView mTextSliderValue;
public SettingsAdapter(SettingsFragmentView view, Context context) public SettingsAdapter(SettingsFragmentView view, Context context)
{ {
mView = view; mView = view;
mContext = context; mContext = context;
} }
@Override @Override
public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{ {
View view; View view;
LayoutInflater inflater = LayoutInflater.from(parent.getContext()); LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType) 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_STRING_SINGLE_CHOICE:
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_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);
default:
Log.error("[SettingsAdapter] Invalid view type: " + viewType);
return null;
}
}
@Override
public void onBindViewHolder(SettingViewHolder holder, int position)
{
holder.bind(getItem(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 getItemViewType(int position)
{
return getItem(position).getType();
}
public void setSettings(ArrayList<SettingsItem> settings)
{
mSettings = settings;
notifyDataSetChanged();
}
public void onBooleanClick(CheckBoxSetting item, int position, boolean checked)
{
BooleanSetting setting = item.setChecked(checked);
notifyItemChanged(position);
if (setting != null)
{
mView.putSetting(setting);
}
if (item.getKey().equals(SettingsFile.KEY_SKIP_EFB) || item.getKey().equals(SettingsFile.KEY_IGNORE_FORMAT))
{
mView.putSetting(new BooleanSetting(item.getKey(), item.getSection(), !checked));
}
mView.onSettingChanged();
}
public void onSingleChoiceClick(SingleChoiceSetting item)
{
mClickedItem = item;
int value = getSelectionForSingleChoiceValue(item);
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
mDialog = builder.show();
}
public void onStringSingleChoiceClick(StringSingleChoiceSetting item)
{
mClickedItem = item;
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this);
mDialog = builder.show();
}
public void onSliderClick(SliderSetting item)
{
mClickedItem = item;
mSeekbarProgress = item.getSelectedValue();
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
View view = inflater.inflate(R.layout.dialog_seekbar, null);
builder.setTitle(item.getNameId());
builder.setView(view);
builder.setPositiveButton(R.string.ok, this);
builder.setNegativeButton(R.string.cancel, this);
mDialog = builder.show();
mTextSliderValue = (TextView) view.findViewById(R.id.text_value);
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
TextView units = (TextView) view.findViewById(R.id.text_units);
units.setText(item.getUnits());
SeekBar seekbar = (SeekBar) view.findViewById(R.id.seekbar);
seekbar.setMax(item.getMax());
seekbar.setProgress(mSeekbarProgress);
seekbar.setKeyProgressIncrement(5);
seekbar.setOnSeekBarChangeListener(this);
}
public void onSubmenuClick(SubmenuSetting item)
{
mView.loadSubMenu(item.getMenuKey());
}
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_description), 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("");
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.getValue());
notifyItemChanged(position);
if (setting != null)
{
mView.putSetting(setting);
}
mView.onSettingChanged();
});
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
@Override
public void onClick(DialogInterface dialog, int which)
{
if (mClickedItem instanceof SingleChoiceSetting)
{
SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem;
int value = getValueForSingleChoiceSelection(scSetting, which);
MenuTag menuTag = scSetting.getMenuTag();
if(menuTag != null)
{
if (menuTag.isGCPadMenu())
{
mView.onGcPadSettingChanged(menuTag, value);
}
if (menuTag.isWiimoteMenu())
{
mView.onWiimoteSettingChanged(menuTag, value);
}
if (menuTag.isWiimoteExtensionMenu())
{
mView.onExtensionSettingChanged(menuTag, 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);
}
else
{
if (scSetting.getKey().equals(SettingsFile.KEY_VIDEO_BACKEND_INDEX))
{
putVideoBackendSetting(which);
}
else if (scSetting.getKey().equals(SettingsFile.KEY_WIIMOTE_EXTENSION))
{
putExtensionSetting(which, Character.getNumericValue(scSetting.getSection().charAt(scSetting.getSection().length() - 1)));
}
}
closeDialog();
}
else if (mClickedItem instanceof StringSingleChoiceSetting)
{
StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem;
String value = scSetting.getValueAt(which);
StringSetting setting = scSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
closeDialog();
}
else if (mClickedItem instanceof SliderSetting)
{
SliderSetting sliderSetting = (SliderSetting) mClickedItem;
if (sliderSetting.getSetting() instanceof FloatSetting)
{
float value;
if (sliderSetting.getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT)
|| sliderSetting.getKey().equals(SettingsFile.KEY_SPEED_LIMIT))
{
value = mSeekbarProgress / 100.0f;
}
else
{
value = (float) mSeekbarProgress;
}
FloatSetting setting = sliderSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
}
else
{
IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress);
if (setting != null)
{
mView.putSetting(setting);
}
}
}
mView.onSettingChanged();
mClickedItem = null;
mSeekbarProgress = -1;
}
public void closeDialog()
{
if (mDialog != null)
{
mDialog.dismiss();
mDialog = null;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
mSeekbarProgress = progress;
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which)
{
int valuesId = item.getValuesId();
if (valuesId > 0)
{
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
return valuesArray[which];
}
else
{
return which;
}
}
private int getSelectionForSingleChoiceValue(SingleChoiceSetting item)
{
int value = item.getSelectedValue();
int valuesId = item.getValuesId();
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;
}
private void putVideoBackendSetting(int which)
{ {
StringSetting gfxBackend = null; case SettingsItem.TYPE_HEADER:
switch (which) view = inflater.inflate(R.layout.list_item_settings_header, parent, false);
{ return new HeaderViewHolder(view, this);
case 0:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE, "OGL");
break;
case 1: case SettingsItem.TYPE_CHECKBOX:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE, "Vulkan"); view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false);
break; return new CheckBoxSettingViewHolder(view, this);
case 2: case SettingsItem.TYPE_STRING_SINGLE_CHOICE:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE, "Software Renderer"); case SettingsItem.TYPE_SINGLE_CHOICE:
break; view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new SingleChoiceViewHolder(view, this);
case 3: case SettingsItem.TYPE_SLIDER:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE, "Null"); view = inflater.inflate(R.layout.list_item_setting, parent, false);
break; return new SliderViewHolder(view, this);
}
mView.putSetting(gfxBackend); 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);
default:
Log.error("[SettingsAdapter] Invalid view type: " + viewType);
return null;
}
}
@Override
public void onBindViewHolder(SettingViewHolder holder, int position)
{
holder.bind(getItem(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 getItemViewType(int position)
{
return getItem(position).getType();
}
public void setSettings(ArrayList<SettingsItem> settings)
{
mSettings = settings;
notifyDataSetChanged();
}
public void onBooleanClick(CheckBoxSetting item, int position, boolean checked)
{
BooleanSetting setting = item.setChecked(checked);
notifyItemChanged(position);
if (setting != null)
{
mView.putSetting(setting);
} }
private void putExtensionSetting(int which, int wiimoteNumber) if (item.getKey().equals(SettingsFile.KEY_SKIP_EFB) ||
{ item.getKey().equals(SettingsFile.KEY_IGNORE_FORMAT))
StringSetting extension = new StringSetting(SettingsFile.KEY_WIIMOTE_EXTENSION, Settings.SECTION_WIIMOTE + wiimoteNumber, mContext.getResources().getStringArray(R.array.wiimoteExtensionsEntries)[which]); {
mView.putSetting(extension); mView.putSetting(new BooleanSetting(item.getKey(), item.getSection(), !checked));
} }
mView.onSettingChanged();
}
public void onSingleChoiceClick(SingleChoiceSetting item)
{
mClickedItem = item;
int value = getSelectionForSingleChoiceValue(item);
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
mDialog = builder.show();
}
public void onStringSingleChoiceClick(StringSingleChoiceSetting item)
{
mClickedItem = item;
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), item.getSelectValueIndex(), this);
mDialog = builder.show();
}
public void onSliderClick(SliderSetting item)
{
mClickedItem = item;
mSeekbarProgress = item.getSelectedValue();
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
View view = inflater.inflate(R.layout.dialog_seekbar, null);
builder.setTitle(item.getNameId());
builder.setView(view);
builder.setPositiveButton(R.string.ok, this);
builder.setNegativeButton(R.string.cancel, this);
mDialog = builder.show();
mTextSliderValue = (TextView) view.findViewById(R.id.text_value);
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
TextView units = (TextView) view.findViewById(R.id.text_units);
units.setText(item.getUnits());
SeekBar seekbar = (SeekBar) view.findViewById(R.id.seekbar);
seekbar.setMax(item.getMax());
seekbar.setProgress(mSeekbarProgress);
seekbar.setKeyProgressIncrement(5);
seekbar.setOnSeekBarChangeListener(this);
}
public void onSubmenuClick(SubmenuSetting item)
{
mView.loadSubMenu(item.getMenuKey());
}
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_description),
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("");
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.getValue());
notifyItemChanged(position);
if (setting != null)
{
mView.putSetting(setting);
}
mView.onSettingChanged();
});
dialog.setCanceledOnTouchOutside(false);
dialog.show();
}
@Override
public void onClick(DialogInterface dialog, int which)
{
if (mClickedItem instanceof SingleChoiceSetting)
{
SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem;
int value = getValueForSingleChoiceSelection(scSetting, which);
MenuTag menuTag = scSetting.getMenuTag();
if (menuTag != null)
{
if (menuTag.isGCPadMenu())
{
mView.onGcPadSettingChanged(menuTag, value);
}
if (menuTag.isWiimoteMenu())
{
mView.onWiimoteSettingChanged(menuTag, value);
}
if (menuTag.isWiimoteExtensionMenu())
{
mView.onExtensionSettingChanged(menuTag, 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);
}
else
{
if (scSetting.getKey().equals(SettingsFile.KEY_VIDEO_BACKEND_INDEX))
{
putVideoBackendSetting(which);
}
else if (scSetting.getKey().equals(SettingsFile.KEY_WIIMOTE_EXTENSION))
{
putExtensionSetting(which, Character.getNumericValue(
scSetting.getSection().charAt(scSetting.getSection().length() - 1)));
}
}
closeDialog();
}
else if (mClickedItem instanceof StringSingleChoiceSetting)
{
StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem;
String value = scSetting.getValueAt(which);
StringSetting setting = scSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
closeDialog();
}
else if (mClickedItem instanceof SliderSetting)
{
SliderSetting sliderSetting = (SliderSetting) mClickedItem;
if (sliderSetting.getSetting() instanceof FloatSetting)
{
float value;
if (sliderSetting.getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT)
|| sliderSetting.getKey().equals(SettingsFile.KEY_SPEED_LIMIT))
{
value = mSeekbarProgress / 100.0f;
}
else
{
value = (float) mSeekbarProgress;
}
FloatSetting setting = sliderSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
}
else
{
IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress);
if (setting != null)
{
mView.putSetting(setting);
}
}
}
mView.onSettingChanged();
mClickedItem = null;
mSeekbarProgress = -1;
}
public void closeDialog()
{
if (mDialog != null)
{
mDialog.dismiss();
mDialog = null;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
mSeekbarProgress = progress;
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which)
{
int valuesId = item.getValuesId();
if (valuesId > 0)
{
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
return valuesArray[which];
}
else
{
return which;
}
}
private int getSelectionForSingleChoiceValue(SingleChoiceSetting item)
{
int value = item.getSelectedValue();
int valuesId = item.getValuesId();
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;
}
private void putVideoBackendSetting(int which)
{
StringSetting gfxBackend = null;
switch (which)
{
case 0:
gfxBackend =
new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE, "OGL");
break;
case 1:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE,
"Vulkan");
break;
case 2:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE,
"Software Renderer");
break;
case 3:
gfxBackend = new StringSetting(SettingsFile.KEY_VIDEO_BACKEND, Settings.SECTION_INI_CORE,
"Null");
break;
}
mView.putSetting(gfxBackend);
}
private void putExtensionSetting(int which, int wiimoteNumber)
{
StringSetting extension = new StringSetting(SettingsFile.KEY_WIIMOTE_EXTENSION,
Settings.SECTION_WIIMOTE + wiimoteNumber,
mContext.getResources().getStringArray(R.array.wiimoteExtensionsEntries)[which]);
mView.putSetting(extension);
}
} }

View File

@ -20,170 +20,173 @@ import java.util.ArrayList;
public final class SettingsFragment extends Fragment implements SettingsFragmentView public final class SettingsFragment extends Fragment implements SettingsFragmentView
{ {
private static final String ARGUMENT_MENU_TAG = "menu_tag"; private static final String ARGUMENT_MENU_TAG = "menu_tag";
private static final String ARGUMENT_GAME_ID = "game_id"; private static final String ARGUMENT_GAME_ID = "game_id";
private SettingsFragmentPresenter mPresenter = new SettingsFragmentPresenter(this); private SettingsFragmentPresenter mPresenter = new SettingsFragmentPresenter(this);
private SettingsActivityView mActivity; private SettingsActivityView mActivity;
private SettingsAdapter mAdapter; private SettingsAdapter mAdapter;
public static Fragment newInstance(MenuTag menuTag, String gameId, Bundle extras) public static Fragment newInstance(MenuTag menuTag, String gameId, Bundle extras)
{ {
SettingsFragment fragment = new SettingsFragment(); SettingsFragment fragment = new SettingsFragment();
Bundle arguments = new Bundle(); Bundle arguments = new Bundle();
if(extras != null) if (extras != null)
{ {
arguments.putAll(extras); arguments.putAll(extras);
} }
arguments.putSerializable(ARGUMENT_MENU_TAG, menuTag); arguments.putSerializable(ARGUMENT_MENU_TAG, menuTag);
arguments.putString(ARGUMENT_GAME_ID, gameId); arguments.putString(ARGUMENT_GAME_ID, gameId);
fragment.setArguments(arguments); fragment.setArguments(arguments);
return fragment; return fragment;
} }
@Override @Override
public void onAttach(Context context) public void onAttach(Context context)
{ {
super.onAttach(context); super.onAttach(context);
mActivity = (SettingsActivityView) context; mActivity = (SettingsActivityView) context;
mPresenter.onAttach(); mPresenter.onAttach();
} }
/** /**
* This version of onAttach is needed for versions below Marshmallow. * This version of onAttach is needed for versions below Marshmallow.
* *
* @param activity * @param activity
*/ */
@Override @Override
public void onAttach(Activity activity) public void onAttach(Activity activity)
{ {
super.onAttach(activity); super.onAttach(activity);
mActivity = (SettingsActivityView) activity; mActivity = (SettingsActivityView) activity;
mPresenter.onAttach(); mPresenter.onAttach();
} }
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setRetainInstance(true); setRetainInstance(true);
Bundle args = getArguments(); Bundle args = getArguments();
MenuTag menuTag = (MenuTag) args.getSerializable(ARGUMENT_MENU_TAG); MenuTag menuTag = (MenuTag) args.getSerializable(ARGUMENT_MENU_TAG);
String gameId = getArguments().getString(ARGUMENT_GAME_ID); String gameId = getArguments().getString(ARGUMENT_GAME_ID);
mAdapter = new SettingsAdapter(this, getActivity()); mAdapter = new SettingsAdapter(this, getActivity());
mPresenter.onCreate(menuTag, gameId, args); mPresenter.onCreate(menuTag, gameId, args);
} }
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
{ @Nullable Bundle savedInstanceState)
return inflater.inflate(R.layout.fragment_settings, container, false); {
} return inflater.inflate(R.layout.fragment_settings, container, false);
}
@Override @Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
{ {
LinearLayoutManager manager = new LinearLayoutManager(getActivity()); LinearLayoutManager manager = new LinearLayoutManager(getActivity());
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list_settings); RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list_settings);
recyclerView.setAdapter(mAdapter); recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(manager); recyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null)); recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
SettingsActivityView activity = (SettingsActivityView) getActivity(); SettingsActivityView activity = (SettingsActivityView) getActivity();
mPresenter.onViewCreated(activity.getSettings()); mPresenter.onViewCreated(activity.getSettings());
} }
@Override @Override
public void onDetach() public void onDetach()
{ {
super.onDetach(); super.onDetach();
mActivity = null; mActivity = null;
if (mAdapter != null) if (mAdapter != null)
{ {
mAdapter.closeDialog(); mAdapter.closeDialog();
} }
} }
@Override @Override
public void onSettingsFileLoaded(org.dolphinemu.dolphinemu.features.settings.model.Settings settings) public void onSettingsFileLoaded(
{ org.dolphinemu.dolphinemu.features.settings.model.Settings settings)
mPresenter.setSettings(settings); {
} mPresenter.setSettings(settings);
}
@Override @Override
public void passSettingsToActivity(org.dolphinemu.dolphinemu.features.settings.model.Settings settings) public void passSettingsToActivity(
{ org.dolphinemu.dolphinemu.features.settings.model.Settings settings)
if (mActivity != null) {
{ if (mActivity != null)
mActivity.setSettings(settings); {
} mActivity.setSettings(settings);
} }
}
@Override @Override
public void showSettingsList(ArrayList<SettingsItem> settingsList) public void showSettingsList(ArrayList<SettingsItem> settingsList)
{ {
mAdapter.setSettings(settingsList); mAdapter.setSettings(settingsList);
} }
@Override @Override
public void loadDefaultSettings() public void loadDefaultSettings()
{ {
mPresenter.loadDefaultSettings(); mPresenter.loadDefaultSettings();
} }
@Override @Override
public void loadSubMenu(MenuTag menuKey) public void loadSubMenu(MenuTag menuKey)
{ {
mActivity.showSettingsFragment(menuKey, null, true, getArguments().getString(ARGUMENT_GAME_ID)); mActivity.showSettingsFragment(menuKey, null, true, getArguments().getString(ARGUMENT_GAME_ID));
} }
@Override @Override
public void showToastMessage(String message) public void showToastMessage(String message)
{ {
mActivity.showToastMessage(message); mActivity.showToastMessage(message);
} }
@Override @Override
public void putSetting(Setting setting) public void putSetting(Setting setting)
{ {
mPresenter.putSetting(setting); mPresenter.putSetting(setting);
} }
@Override @Override
public void onSettingChanged() public void onSettingChanged()
{ {
mActivity.onSettingChanged(); mActivity.onSettingChanged();
} }
@Override @Override
public void onGcPadSettingChanged(MenuTag menuTag, int value) public void onGcPadSettingChanged(MenuTag menuTag, int value)
{ {
mActivity.onGcPadSettingChanged(menuTag, value); mActivity.onGcPadSettingChanged(menuTag, value);
} }
@Override @Override
public void onWiimoteSettingChanged(MenuTag menuTag, int value) public void onWiimoteSettingChanged(MenuTag menuTag, int value)
{ {
mActivity.onWiimoteSettingChanged(menuTag, value); mActivity.onWiimoteSettingChanged(menuTag, value);
} }
@Override @Override
public void onExtensionSettingChanged(MenuTag menuTag, int value) public void onExtensionSettingChanged(MenuTag menuTag, int value)
{ {
mActivity.onExtensionSettingChanged(menuTag, value); mActivity.onExtensionSettingChanged(menuTag, value);
} }
} }

View File

@ -14,89 +14,89 @@ import java.util.ArrayList;
*/ */
public interface SettingsFragmentView public interface SettingsFragmentView
{ {
/** /**
* Called by the containing Activity to notify the Fragment that an * Called by the containing Activity to notify the Fragment that an
* asynchronous load operation completed. * asynchronous load operation completed.
* *
* @param settings The (possibly null) result of the ini load operation. * @param settings The (possibly null) result of the ini load operation.
*/ */
void onSettingsFileLoaded(Settings settings); void onSettingsFileLoaded(Settings settings);
/** /**
* Pass a settings HashMap to the containing activity, so that it can * Pass a settings HashMap to the containing activity, so that it can
* share the HashMap with other SettingsFragments; useful so that rotations * share the HashMap with other SettingsFragments; useful so that rotations
* do not require an additional load operation. * do not require an additional load operation.
* *
* @param settings An ArrayList containing all the settings HashMaps. * @param settings An ArrayList containing all the settings HashMaps.
*/ */
void passSettingsToActivity(Settings settings); void passSettingsToActivity(Settings settings);
/** /**
* Pass an ArrayList to the View so that it can be displayed on screen. * 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 * @param settingsList The result of converting the HashMap to an ArrayList
*/ */
void showSettingsList(ArrayList<SettingsItem> settingsList); void showSettingsList(ArrayList<SettingsItem> settingsList);
/** /**
* Called by the containing Activity when an asynchronous load operation fails. * Called by the containing Activity when an asynchronous load operation fails.
* Instructs the Fragment to load the settings screen with defaults selected. * Instructs the Fragment to load the settings screen with defaults selected.
*/ */
void loadDefaultSettings(); void loadDefaultSettings();
/** /**
* @return The Fragment's containing activity. * @return The Fragment's containing activity.
*/ */
FragmentActivity getActivity(); FragmentActivity getActivity();
/** /**
* Tell the Fragment to tell the containing Activity to show a new * Tell the Fragment to tell the containing Activity to show a new
* Fragment containing a submenu of settings. * Fragment containing a submenu of settings.
* *
* @param menuKey Identifier for the settings group that should be shown. * @param menuKey Identifier for the settings group that should be shown.
*/ */
void loadSubMenu(MenuTag menuKey); void loadSubMenu(MenuTag menuKey);
/** /**
* Tell the Fragment to tell the containing activity to display a toast message. * Tell the Fragment to tell the containing activity to display a toast message.
* *
* @param message Text to be shown in the Toast * @param message Text to be shown in the Toast
*/ */
void showToastMessage(String message); void showToastMessage(String message);
/** /**
* Have the fragment add a setting to the HashMap. * Have the fragment add a setting to the HashMap.
* *
* @param setting The (possibly previously missing) new setting. * @param setting The (possibly previously missing) new setting.
*/ */
void putSetting(Setting setting); void putSetting(Setting setting);
/** /**
* Have the fragment tell the containing Activity that a setting was modified. * Have the fragment tell the containing Activity that a setting was modified.
*/ */
void onSettingChanged(); void onSettingChanged();
/** /**
* Have the fragment tell the containing Activity that a GCPad's setting was modified. * Have the fragment tell the containing Activity that a GCPad's setting was modified.
* *
* @param menuTag Identifier for the GCPad that was modified. * @param menuTag Identifier for the GCPad that was modified.
* @param value New setting for the GCPad. * @param value New setting for the GCPad.
*/ */
void onGcPadSettingChanged(MenuTag menuTag, int value); void onGcPadSettingChanged(MenuTag menuTag, int value);
/** /**
* Have the fragment tell the containing Activity that a Wiimote's setting was modified. * Have the fragment tell the containing Activity that a Wiimote's setting was modified.
* *
* @param menuTag Identifier for Wiimote that was modified. * @param menuTag Identifier for Wiimote that was modified.
* @param value New setting for the Wiimote. * @param value New setting for the Wiimote.
*/ */
void onWiimoteSettingChanged(MenuTag menuTag, int value); void onWiimoteSettingChanged(MenuTag menuTag, int value);
/** /**
* Have the fragment tell the containing Activity that an extension setting was modified. * Have the fragment tell the containing Activity that an extension setting was modified.
* *
* @param menuTag Identifier for the extension that was modified. * @param menuTag Identifier for the extension that was modified.
* @param value New setting for the extension. * @param value New setting for the extension.
*/ */
void onExtensionSettingChanged(MenuTag menuTag, int value); void onExtensionSettingChanged(MenuTag menuTag, int value);
} }

View File

@ -9,48 +9,48 @@ import android.widget.FrameLayout;
*/ */
public final class SettingsFrameLayout extends FrameLayout public final class SettingsFrameLayout extends FrameLayout
{ {
private float mVisibleness = 1.0f; private float mVisibleness = 1.0f;
public SettingsFrameLayout(Context context) public SettingsFrameLayout(Context context)
{ {
super(context); super(context);
} }
public SettingsFrameLayout(Context context, AttributeSet attrs) public SettingsFrameLayout(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
} }
public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr)
{ {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
} }
public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) public SettingsFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{ {
super(context, attrs, defStyleAttr, defStyleRes); super(context, attrs, defStyleAttr, defStyleRes);
} }
public float getYFraction() public float getYFraction()
{ {
return getY() / getHeight(); return getY() / getHeight();
} }
public void setYFraction(float yFraction) public void setYFraction(float yFraction)
{ {
final int height = getHeight(); final int height = getHeight();
setY((height > 0) ? (yFraction * height) : -9999); setY((height > 0) ? (yFraction * height) : -9999);
} }
public float getVisibleness() public float getVisibleness()
{ {
return mVisibleness; return mVisibleness;
} }
public void setVisibleness(float visibleness) public void setVisibleness(float visibleness)
{ {
setScaleX(visibleness); setScaleX(visibleness);
setScaleY(visibleness); setScaleY(visibleness);
setAlpha(visibleness); setAlpha(visibleness);
} }
} }

View File

@ -11,46 +11,46 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class CheckBoxSettingViewHolder extends SettingViewHolder public final class CheckBoxSettingViewHolder extends SettingViewHolder
{ {
private CheckBoxSetting mItem; private CheckBoxSetting mItem;
private TextView mTextSettingName; private TextView mTextSettingName;
private TextView mTextSettingDescription; private TextView mTextSettingDescription;
private CheckBox mCheckbox; private CheckBox mCheckbox;
public CheckBoxSettingViewHolder(View itemView, SettingsAdapter adapter) public CheckBoxSettingViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView, adapter); super(itemView, adapter);
} }
@Override @Override
protected void findViews(View root) protected void findViews(View root)
{ {
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name); mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description); mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
mCheckbox = (CheckBox) root.findViewById(R.id.checkbox); mCheckbox = (CheckBox) root.findViewById(R.id.checkbox);
} }
@Override @Override
public void bind(SettingsItem item) public void bind(SettingsItem item)
{ {
mItem = (CheckBoxSetting) item; mItem = (CheckBoxSetting) item;
mTextSettingName.setText(item.getNameId()); mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0) if (item.getDescriptionId() > 0)
{ {
mTextSettingDescription.setText(item.getDescriptionId()); mTextSettingDescription.setText(item.getDescriptionId());
} }
mCheckbox.setChecked(mItem.isChecked()); mCheckbox.setChecked(mItem.isChecked());
} }
@Override @Override
public void onClick(View clicked) public void onClick(View clicked)
{ {
mCheckbox.toggle(); mCheckbox.toggle();
getAdapter().onBooleanClick(mItem, getAdapterPosition(), mCheckbox.isChecked()); getAdapter().onBooleanClick(mItem, getAdapterPosition(), mCheckbox.isChecked());
} }
} }

View File

@ -9,29 +9,29 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class HeaderViewHolder extends SettingViewHolder public final class HeaderViewHolder extends SettingViewHolder
{ {
private TextView mHeaderName; private TextView mHeaderName;
public HeaderViewHolder(View itemView, SettingsAdapter adapter) public HeaderViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView, adapter); super(itemView, adapter);
itemView.setOnClickListener(null); itemView.setOnClickListener(null);
} }
@Override @Override
protected void findViews(View root) protected void findViews(View root)
{ {
mHeaderName = (TextView) root.findViewById(R.id.text_header_name); mHeaderName = (TextView) root.findViewById(R.id.text_header_name);
} }
@Override @Override
public void bind(SettingsItem item) public void bind(SettingsItem item)
{ {
mHeaderName.setText(item.getNameId()); mHeaderName.setText(item.getNameId());
} }
@Override @Override
public void onClick(View clicked) public void onClick(View clicked)
{ {
// no-op // no-op
} }
} }

View File

@ -13,41 +13,41 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class InputBindingSettingViewHolder extends SettingViewHolder public final class InputBindingSettingViewHolder extends SettingViewHolder
{ {
private InputBindingSetting mItem; private InputBindingSetting mItem;
private TextView mTextSettingName; private TextView mTextSettingName;
private TextView mTextSettingDescription; private TextView mTextSettingDescription;
private Context mContext; private Context mContext;
public InputBindingSettingViewHolder(View itemView, SettingsAdapter adapter, Context context) public InputBindingSettingViewHolder(View itemView, SettingsAdapter adapter, Context context)
{ {
super(itemView, adapter); super(itemView, adapter);
mContext = context; mContext = context;
} }
@Override @Override
protected void findViews(View root) protected void findViews(View root)
{ {
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name); mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description); mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
} }
@Override @Override
public void bind(SettingsItem item) public void bind(SettingsItem item)
{ {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
mItem = (InputBindingSetting) item; mItem = (InputBindingSetting) item;
mTextSettingName.setText(item.getNameId()); mTextSettingName.setText(item.getNameId());
mTextSettingDescription.setText(sharedPreferences.getString(mItem.getKey(), "")); mTextSettingDescription.setText(sharedPreferences.getString(mItem.getKey(), ""));
} }
@Override @Override
public void onClick(View clicked) public void onClick(View clicked)
{ {
getAdapter().onInputBindingClick(mItem, getAdapterPosition()); getAdapter().onInputBindingClick(mItem, getAdapterPosition());
} }
} }

View File

@ -6,46 +6,47 @@ import android.view.View;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public abstract class SettingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener public abstract class SettingViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener
{ {
private SettingsAdapter mAdapter; private SettingsAdapter mAdapter;
public SettingViewHolder(View itemView, SettingsAdapter adapter) public SettingViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView); super(itemView);
mAdapter = adapter; mAdapter = adapter;
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
findViews(itemView); findViews(itemView);
} }
protected SettingsAdapter getAdapter() protected SettingsAdapter getAdapter()
{ {
return mAdapter; return mAdapter;
} }
/** /**
* Gets handles to all this ViewHolder's child views using their XML-defined identifiers. * Gets handles to all this ViewHolder's child views using their XML-defined identifiers.
* *
* @param root The newly inflated top-level view. * @param root The newly inflated top-level view.
*/ */
protected abstract void findViews(View root); protected abstract void findViews(View root);
/** /**
* Called by the adapter to set this ViewHolder's child views to display the list item * Called by the adapter to set this ViewHolder's child views to display the list item
* it must now represent. * it must now represent.
* *
* @param item The list item that should be represented by this ViewHolder. * @param item The list item that should be represented by this ViewHolder.
*/ */
public abstract void bind(SettingsItem item); public abstract void bind(SettingsItem item);
/** /**
* Called when this ViewHolder's view is clicked on. Implementations should usually pass * Called when this ViewHolder's view is clicked on. Implementations should usually pass
* this event up to the adapter. * this event up to the adapter.
* *
* @param clicked The view that was clicked on. * @param clicked The view that was clicked on.
*/ */
public abstract void onClick(View clicked); public abstract void onClick(View clicked);
} }

View File

@ -11,46 +11,46 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class SingleChoiceViewHolder extends SettingViewHolder public final class SingleChoiceViewHolder extends SettingViewHolder
{ {
private SettingsItem mItem; private SettingsItem mItem;
private TextView mTextSettingName; private TextView mTextSettingName;
private TextView mTextSettingDescription; private TextView mTextSettingDescription;
public SingleChoiceViewHolder(View itemView, SettingsAdapter adapter) public SingleChoiceViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView, adapter); super(itemView, adapter);
} }
@Override @Override
protected void findViews(View root) protected void findViews(View root)
{ {
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name); mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description); mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
} }
@Override @Override
public void bind(SettingsItem item) public void bind(SettingsItem item)
{ {
mItem = item; mItem = item;
mTextSettingName.setText(item.getNameId()); mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0) if (item.getDescriptionId() > 0)
{ {
mTextSettingDescription.setText(item.getDescriptionId()); mTextSettingDescription.setText(item.getDescriptionId());
} }
} }
@Override @Override
public void onClick(View clicked) public void onClick(View clicked)
{ {
if (mItem instanceof SingleChoiceSetting) if (mItem instanceof SingleChoiceSetting)
{ {
getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem); getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem);
} }
else if (mItem instanceof StringSingleChoiceSetting) else if (mItem instanceof StringSingleChoiceSetting)
{ {
getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem); getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem);
} }
} }
} }

View File

@ -10,40 +10,40 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class SliderViewHolder extends SettingViewHolder public final class SliderViewHolder extends SettingViewHolder
{ {
private SliderSetting mItem; private SliderSetting mItem;
private TextView mTextSettingName; private TextView mTextSettingName;
private TextView mTextSettingDescription; private TextView mTextSettingDescription;
public SliderViewHolder(View itemView, SettingsAdapter adapter) public SliderViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView, adapter); super(itemView, adapter);
} }
@Override @Override
protected void findViews(View root) protected void findViews(View root)
{ {
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name); mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description); mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
} }
@Override @Override
public void bind(SettingsItem item) public void bind(SettingsItem item)
{ {
mItem = (SliderSetting) item; mItem = (SliderSetting) item;
mTextSettingName.setText(item.getNameId()); mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0) if (item.getDescriptionId() > 0)
{ {
mTextSettingDescription.setText(item.getDescriptionId()); mTextSettingDescription.setText(item.getDescriptionId());
} }
} }
@Override @Override
public void onClick(View clicked) public void onClick(View clicked)
{ {
getAdapter().onSliderClick(mItem); getAdapter().onSliderClick(mItem);
} }
} }

View File

@ -10,39 +10,39 @@ import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
public final class SubmenuViewHolder extends SettingViewHolder public final class SubmenuViewHolder extends SettingViewHolder
{ {
private SubmenuSetting mItem; private SubmenuSetting mItem;
private TextView mTextSettingName; private TextView mTextSettingName;
private TextView mTextSettingDescription; private TextView mTextSettingDescription;
public SubmenuViewHolder(View itemView, SettingsAdapter adapter) public SubmenuViewHolder(View itemView, SettingsAdapter adapter)
{ {
super(itemView, adapter); super(itemView, adapter);
} }
@Override @Override
protected void findViews(View root) protected void findViews(View root)
{ {
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name); mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description); mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
} }
@Override @Override
public void bind(SettingsItem item) public void bind(SettingsItem item)
{ {
mItem = (SubmenuSetting) item; mItem = (SubmenuSetting) item;
mTextSettingName.setText(item.getNameId()); mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0) if (item.getDescriptionId() > 0)
{ {
mTextSettingDescription.setText(item.getDescriptionId()); mTextSettingDescription.setText(item.getDescriptionId());
} }
} }
@Override @Override
public void onClick(View clicked) public void onClick(View clicked)
{ {
getAdapter().onSubmenuClick(mItem); getAdapter().onSubmenuClick(mItem);
} }
} }

View File

@ -10,13 +10,13 @@ import java.io.File;
public class CustomFilePickerFragment extends FilePickerFragment public class CustomFilePickerFragment extends FilePickerFragment
{ {
@NonNull @NonNull
@Override @Override
public Uri toUri(@NonNull final File file) public Uri toUri(@NonNull final File file)
{ {
return FileProvider return FileProvider
.getUriForFile(getContext(), .getUriForFile(getContext(),
getContext().getApplicationContext().getPackageName() + ".filesprovider", getContext().getApplicationContext().getPackageName() + ".filesprovider",
file); file);
} }
} }

View File

@ -30,433 +30,438 @@ import java.io.File;
public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback
{ {
private static final String KEY_GAMEPATH = "gamepath"; private static final String KEY_GAMEPATH = "gamepath";
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;
public static EmulationFragment newInstance(String gamePath) public static EmulationFragment newInstance(String gamePath)
{ {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(KEY_GAMEPATH, gamePath); args.putString(KEY_GAMEPATH, gamePath);
EmulationFragment fragment = new EmulationFragment(); EmulationFragment fragment = new EmulationFragment();
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
} }
@Override @Override
public void onAttach(Context context) public void onAttach(Context context)
{ {
super.onAttach(context); super.onAttach(context);
if (context instanceof EmulationActivity) if (context instanceof EmulationActivity)
{ {
activity = (EmulationActivity)context; activity = (EmulationActivity) context;
NativeLibrary.setEmulationActivity((EmulationActivity) context); NativeLibrary.setEmulationActivity((EmulationActivity) context);
} }
else else
{ {
throw new IllegalStateException("EmulationFragment must have EmulationActivity parent"); throw new IllegalStateException("EmulationFragment must have EmulationActivity parent");
} }
} }
/** /**
* Initialize anything that doesn't depend on the layout / views in here. * Initialize anything that doesn't depend on the layout / views in here.
*/ */
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// So this fragment doesn't restart on configuration changes; i.e. rotation. // So this fragment doesn't restart on configuration changes; i.e. rotation.
setRetainInstance(true); setRetainInstance(true);
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
String gamePath = getArguments().getString(KEY_GAMEPATH); String gamePath = getArguments().getString(KEY_GAMEPATH);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
boolean firstOpen = preferences.getBoolean(StartupHandler.NEW_SESSION, true); boolean firstOpen = preferences.getBoolean(StartupHandler.NEW_SESSION, true);
SharedPreferences.Editor sPrefsEditor = preferences.edit(); SharedPreferences.Editor sPrefsEditor = preferences.edit();
sPrefsEditor.putBoolean(StartupHandler.NEW_SESSION, false); sPrefsEditor.putBoolean(StartupHandler.NEW_SESSION, false);
sPrefsEditor.apply(); sPrefsEditor.apply();
mEmulationState = new EmulationState(gamePath, getTemporaryStateFilePath(), firstOpen); mEmulationState = new EmulationState(gamePath, getTemporaryStateFilePath(), firstOpen);
} }
/** /**
* Initialize the UI and start emulation in here. * Initialize the UI and start emulation in here.
*/ */
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{ {
View contents = inflater.inflate(R.layout.fragment_emulation, container, false); View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation); SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation);
surfaceView.getHolder().addCallback(this); surfaceView.getHolder().addCallback(this);
mInputOverlay = contents.findViewById(R.id.surface_input_overlay); mInputOverlay = contents.findViewById(R.id.surface_input_overlay);
if (mInputOverlay != null) if (mInputOverlay != null)
{ {
// If the input overlay was previously disabled, then don't show it. // If the input overlay was previously disabled, then don't show it.
if (!mPreferences.getBoolean("showInputOverlay", true)) if (!mPreferences.getBoolean("showInputOverlay", true))
{ {
mInputOverlay.setVisibility(View.GONE); mInputOverlay.setVisibility(View.GONE);
} }
} }
Button doneButton = contents.findViewById(R.id.done_control_config); Button doneButton = contents.findViewById(R.id.done_control_config);
if (doneButton != null) if (doneButton != null)
{ {
doneButton.setOnClickListener(v -> stopConfiguringControls()); doneButton.setOnClickListener(v -> stopConfiguringControls());
} }
// The new Surface created here will get passed to the native code via onSurfaceChanged. // The new Surface created here will get passed to the native code via onSurfaceChanged.
return contents; return contents;
} }
@Override @Override
public void onResume() public void onResume()
{ {
super.onResume(); super.onResume();
if (DirectoryInitializationService.areDolphinDirectoriesReady()) if (DirectoryInitializationService.areDolphinDirectoriesReady())
{ {
mEmulationState.run(activity.isActivityRecreated()); mEmulationState.run(activity.isActivityRecreated());
} }
else else
{ {
setupDolphinDirectoriesThenStartEmulation(); setupDolphinDirectoriesThenStartEmulation();
} }
} }
@Override @Override
public void onPause() public void onPause()
{ {
if (directoryStateReceiver != null) if (directoryStateReceiver != null)
{ {
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(directoryStateReceiver); LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(directoryStateReceiver);
directoryStateReceiver = null; directoryStateReceiver = null;
} }
mEmulationState.pause(); mEmulationState.pause();
super.onPause(); super.onPause();
} }
@Override @Override
public void onDetach() public void onDetach()
{ {
NativeLibrary.clearEmulationActivity(); NativeLibrary.clearEmulationActivity();
super.onDetach(); super.onDetach();
} }
private void setupDolphinDirectoriesThenStartEmulation() { private void setupDolphinDirectoriesThenStartEmulation()
IntentFilter statusIntentFilter = new IntentFilter( {
DirectoryInitializationService.BROADCAST_ACTION); IntentFilter statusIntentFilter = new IntentFilter(
DirectoryInitializationService.BROADCAST_ACTION);
directoryStateReceiver = directoryStateReceiver =
new DirectoryStateReceiver(directoryInitializationState -> new DirectoryStateReceiver(directoryInitializationState ->
{ {
if (directoryInitializationState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) if (directoryInitializationState ==
{ DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
mEmulationState.run(activity.isActivityRecreated()); {
} mEmulationState.run(activity.isActivityRecreated());
else if (directoryInitializationState == DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED) }
{ else if (directoryInitializationState ==
Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT) DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED)
.show(); {
} Toast.makeText(getContext(), R.string.write_permission_needed, Toast.LENGTH_SHORT)
else if (directoryInitializationState == DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) .show();
{ }
Toast.makeText(getContext(), R.string.external_storage_not_mounted, Toast.LENGTH_SHORT) else if (directoryInitializationState ==
.show(); 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 // Registers the DirectoryStateReceiver and its intent filters
LocalBroadcastManager.getInstance(getActivity()).registerReceiver( LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
directoryStateReceiver, directoryStateReceiver,
statusIntentFilter); statusIntentFilter);
DirectoryInitializationService.startService(getActivity()); DirectoryInitializationService.startService(getActivity());
} }
public void toggleInputOverlayVisibility() public void toggleInputOverlayVisibility()
{ {
SharedPreferences.Editor editor = mPreferences.edit(); SharedPreferences.Editor editor = mPreferences.edit();
// If the overlay is currently set to INVISIBLE // If the overlay is currently set to INVISIBLE
if (!mPreferences.getBoolean("showInputOverlay", false)) if (!mPreferences.getBoolean("showInputOverlay", false))
{ {
// Set it to VISIBLE // Set it to VISIBLE
mInputOverlay.setVisibility(View.VISIBLE); mInputOverlay.setVisibility(View.VISIBLE);
editor.putBoolean("showInputOverlay", true); editor.putBoolean("showInputOverlay", true);
} }
else else
{ {
// Set it to INVISIBLE // Set it to INVISIBLE
mInputOverlay.setVisibility(View.GONE); mInputOverlay.setVisibility(View.GONE);
editor.putBoolean("showInputOverlay", false); editor.putBoolean("showInputOverlay", false);
} }
editor.apply(); editor.apply();
} }
public void refreshInputOverlay() public void refreshInputOverlay()
{ {
mInputOverlay.refreshControls(); mInputOverlay.refreshControls();
} }
@Override @Override
public void surfaceCreated(SurfaceHolder holder) public void surfaceCreated(SurfaceHolder holder)
{ {
// We purposely don't do anything here. // We purposely don't do anything here.
// All work is done in surfaceChanged, which we are guaranteed to get even for surface creation. // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
} }
@Override @Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{ {
Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height); Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height);
mEmulationState.newSurface(holder.getSurface()); mEmulationState.newSurface(holder.getSurface());
} }
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) public void surfaceDestroyed(SurfaceHolder holder)
{ {
mEmulationState.clearSurface(); mEmulationState.clearSurface();
} }
public void stopEmulation() public void stopEmulation()
{ {
mEmulationState.stop(); mEmulationState.stop();
} }
public void startConfiguringControls() public void startConfiguringControls()
{ {
getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE); getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE);
mInputOverlay.setIsInEditMode(true); mInputOverlay.setIsInEditMode(true);
} }
public void stopConfiguringControls() public void stopConfiguringControls()
{ {
getView().findViewById(R.id.done_control_config).setVisibility(View.GONE); getView().findViewById(R.id.done_control_config).setVisibility(View.GONE);
mInputOverlay.setIsInEditMode(false); mInputOverlay.setIsInEditMode(false);
} }
public boolean isConfiguringControls() public boolean isConfiguringControls()
{ {
return mInputOverlay.isInEditMode(); return mInputOverlay.isInEditMode();
} }
private static class EmulationState private static class EmulationState
{ {
private enum State private enum State
{ {
STOPPED, RUNNING, PAUSED STOPPED, RUNNING, PAUSED
} }
private final String mGamePath; private final String mGamePath;
private Thread mEmulationThread; private Thread mEmulationThread;
private State state; private State state;
private Surface mSurface; private Surface mSurface;
private boolean mRunWhenSurfaceIsValid; private boolean mRunWhenSurfaceIsValid;
private boolean loadPreviousTemporaryState; private boolean loadPreviousTemporaryState;
private boolean firstOpen; private boolean firstOpen;
private final String temporaryStatePath; private final String temporaryStatePath;
EmulationState(String gamePath, String temporaryStatePath, boolean firstOpen) EmulationState(String gamePath, String temporaryStatePath, boolean firstOpen)
{ {
this.firstOpen = firstOpen; this.firstOpen = firstOpen;
mGamePath = gamePath; mGamePath = gamePath;
this.temporaryStatePath = temporaryStatePath; this.temporaryStatePath = temporaryStatePath;
// Starting state is stopped. // Starting state is stopped.
state = State.STOPPED; state = State.STOPPED;
} }
// Getters for the current state // Getters for the current state
public synchronized boolean isStopped() public synchronized boolean isStopped()
{ {
return state == State.STOPPED; return state == State.STOPPED;
} }
public synchronized boolean isPaused() public synchronized boolean isPaused()
{ {
return state == State.PAUSED; return state == State.PAUSED;
} }
public synchronized boolean isRunning() public synchronized boolean isRunning()
{ {
return state == State.RUNNING; return state == State.RUNNING;
} }
// State changing methods // State changing methods
public synchronized void stop() public synchronized void stop()
{ {
if (state != State.STOPPED) if (state != State.STOPPED)
{ {
Log.debug("[EmulationFragment] Stopping emulation."); Log.debug("[EmulationFragment] Stopping emulation.");
state = State.STOPPED; state = State.STOPPED;
NativeLibrary.StopEmulation(); NativeLibrary.StopEmulation();
} }
else else
{ {
Log.warning("[EmulationFragment] Stop called while already stopped."); Log.warning("[EmulationFragment] Stop called while already stopped.");
} }
} }
public synchronized void pause() public synchronized void pause()
{ {
if (state != State.PAUSED) if (state != State.PAUSED)
{ {
state = State.PAUSED; state = State.PAUSED;
Log.debug("[EmulationFragment] Pausing emulation."); Log.debug("[EmulationFragment] Pausing emulation.");
// Release the surface before pausing, since emulation has to be running for that. // Release the surface before pausing, since emulation has to be running for that.
NativeLibrary.SurfaceDestroyed(); NativeLibrary.SurfaceDestroyed();
NativeLibrary.PauseEmulation(); NativeLibrary.PauseEmulation();
} }
else else
{ {
Log.warning("[EmulationFragment] Pause called while already paused."); Log.warning("[EmulationFragment] Pause called while already paused.");
} }
} }
public synchronized void run(boolean isActivityRecreated) public synchronized void run(boolean isActivityRecreated)
{ {
if (isActivityRecreated) if (isActivityRecreated)
{ {
if (NativeLibrary.IsRunning()) if (NativeLibrary.IsRunning())
{ {
loadPreviousTemporaryState = false; loadPreviousTemporaryState = false;
state = State.PAUSED; state = State.PAUSED;
deleteFile(temporaryStatePath); deleteFile(temporaryStatePath);
} }
else else
{ {
loadPreviousTemporaryState = true; loadPreviousTemporaryState = true;
} }
} }
else else
{ {
Log.debug("[EmulationFragment] activity resumed or fresh start"); Log.debug("[EmulationFragment] activity resumed or fresh start");
loadPreviousTemporaryState = false; loadPreviousTemporaryState = false;
// activity resumed without being killed or this is the first run // activity resumed without being killed or this is the first run
deleteFile(temporaryStatePath); deleteFile(temporaryStatePath);
} }
// If the surface is set, run now. Otherwise, wait for it to get set. // If the surface is set, run now. Otherwise, wait for it to get set.
if (mSurface != null) if (mSurface != null)
{ {
runWithValidSurface(); runWithValidSurface();
} }
else else
{ {
mRunWhenSurfaceIsValid = true; mRunWhenSurfaceIsValid = true;
} }
} }
// Surface callbacks // Surface callbacks
public synchronized void newSurface(Surface surface) public synchronized void newSurface(Surface surface)
{ {
mSurface = surface; mSurface = surface;
if (mRunWhenSurfaceIsValid) if (mRunWhenSurfaceIsValid)
{ {
runWithValidSurface(); runWithValidSurface();
} }
} }
public synchronized void clearSurface() public synchronized void clearSurface()
{ {
if (mSurface == null) if (mSurface == null)
{ {
Log.warning("[EmulationFragment] clearSurface called, but surface already null."); Log.warning("[EmulationFragment] clearSurface called, but surface already null.");
} }
else else
{ {
mSurface = null; mSurface = null;
Log.debug("[EmulationFragment] Surface destroyed."); Log.debug("[EmulationFragment] Surface destroyed.");
if (state == State.RUNNING) if (state == State.RUNNING)
{ {
NativeLibrary.SurfaceDestroyed(); NativeLibrary.SurfaceDestroyed();
state = State.PAUSED; state = State.PAUSED;
} }
else if (state == State.PAUSED) else if (state == State.PAUSED)
{ {
Log.warning("[EmulationFragment] Surface cleared while emulation paused."); Log.warning("[EmulationFragment] Surface cleared while emulation paused.");
} }
else else
{ {
Log.warning("[EmulationFragment] Surface cleared while emulation stopped."); Log.warning("[EmulationFragment] Surface cleared while emulation stopped.");
} }
} }
} }
private void runWithValidSurface() private void runWithValidSurface()
{ {
mRunWhenSurfaceIsValid = false; mRunWhenSurfaceIsValid = false;
if (state == State.STOPPED) if (state == State.STOPPED)
{ {
mEmulationThread = new Thread(() -> mEmulationThread = new Thread(() ->
{ {
NativeLibrary.SurfaceChanged(mSurface); NativeLibrary.SurfaceChanged(mSurface);
if (loadPreviousTemporaryState) if (loadPreviousTemporaryState)
{ {
Log.debug("[EmulationFragment] Starting emulation thread from previous state."); Log.debug("[EmulationFragment] Starting emulation thread from previous state.");
NativeLibrary.Run(mGamePath, temporaryStatePath, true); NativeLibrary.Run(mGamePath, temporaryStatePath, true);
} }
else else
{ {
Log.debug("[EmulationFragment] Starting emulation thread."); Log.debug("[EmulationFragment] Starting emulation thread.");
NativeLibrary.Run(mGamePath, firstOpen); NativeLibrary.Run(mGamePath, firstOpen);
} }
}, "NativeEmulation"); }, "NativeEmulation");
mEmulationThread.start(); mEmulationThread.start();
} }
else if (state == State.PAUSED) else if (state == State.PAUSED)
{ {
Log.debug("[EmulationFragment] Resuming emulation."); Log.debug("[EmulationFragment] Resuming emulation.");
NativeLibrary.SurfaceChanged(mSurface); NativeLibrary.SurfaceChanged(mSurface);
NativeLibrary.UnPauseEmulation(); NativeLibrary.UnPauseEmulation();
} }
else else
{ {
Log.debug("[EmulationFragment] Bug, run called while already running."); Log.debug("[EmulationFragment] Bug, run called while already running.");
} }
state = State.RUNNING; state = State.RUNNING;
} }
} }
public void saveTemporaryState() public void saveTemporaryState()
{ {
NativeLibrary.SaveStateAs(getTemporaryStateFilePath(), true); NativeLibrary.SaveStateAs(getTemporaryStateFilePath(), true);
} }
private String getTemporaryStateFilePath() private String getTemporaryStateFilePath()
{ {
return getContext().getFilesDir() + File.separator + "temp.sav"; return getContext().getFilesDir() + File.separator + "temp.sav";
} }
private static void deleteFile(String path) private static void deleteFile(String path)
{ {
try try
{ {
File file = new File(path); File file = new File(path);
file.delete(); file.delete();
} }
catch (Exception ex) catch (Exception ex)
{ {
// fail safely // fail safely
} }
} }
} }

View File

@ -16,62 +16,68 @@ import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public final class MenuFragment extends Fragment implements View.OnClickListener public final class MenuFragment extends Fragment implements View.OnClickListener
{ {
private static final String KEY_TITLE = "title"; private static final String KEY_TITLE = "title";
private static SparseIntArray buttonsActionsMap = new SparseIntArray(); private static SparseIntArray buttonsActionsMap = new SparseIntArray();
static {
buttonsActionsMap.append(R.id.menu_take_screenshot, EmulationActivity.MENU_ACTION_TAKE_SCREENSHOT);
buttonsActionsMap.append(R.id.menu_quicksave, EmulationActivity.MENU_ACTION_QUICK_SAVE);
buttonsActionsMap.append(R.id.menu_quickload, EmulationActivity.MENU_ACTION_QUICK_LOAD);
buttonsActionsMap.append(R.id.menu_emulation_save_root, EmulationActivity.MENU_ACTION_SAVE_ROOT);
buttonsActionsMap.append(R.id.menu_emulation_load_root, EmulationActivity.MENU_ACTION_LOAD_ROOT);
buttonsActionsMap.append(R.id.menu_refresh_wiimotes, EmulationActivity.MENU_ACTION_REFRESH_WIIMOTES);
buttonsActionsMap.append(R.id.menu_change_disc, EmulationActivity.MENU_ACTION_CHANGE_DISC);
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
}
public static MenuFragment newInstance(String title) static
{ {
MenuFragment fragment = new MenuFragment(); buttonsActionsMap
.append(R.id.menu_take_screenshot, EmulationActivity.MENU_ACTION_TAKE_SCREENSHOT);
buttonsActionsMap.append(R.id.menu_quicksave, EmulationActivity.MENU_ACTION_QUICK_SAVE);
buttonsActionsMap.append(R.id.menu_quickload, EmulationActivity.MENU_ACTION_QUICK_LOAD);
buttonsActionsMap
.append(R.id.menu_emulation_save_root, EmulationActivity.MENU_ACTION_SAVE_ROOT);
buttonsActionsMap
.append(R.id.menu_emulation_load_root, EmulationActivity.MENU_ACTION_LOAD_ROOT);
buttonsActionsMap
.append(R.id.menu_refresh_wiimotes, EmulationActivity.MENU_ACTION_REFRESH_WIIMOTES);
buttonsActionsMap.append(R.id.menu_change_disc, EmulationActivity.MENU_ACTION_CHANGE_DISC);
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
}
Bundle arguments = new Bundle(); public static MenuFragment newInstance(String title)
arguments.putSerializable(KEY_TITLE, title); {
fragment.setArguments(arguments); MenuFragment fragment = new MenuFragment();
return fragment; Bundle arguments = new Bundle();
} arguments.putSerializable(KEY_TITLE, title);
fragment.setArguments(arguments);
@Nullable return fragment;
@Override }
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false);
LinearLayout options = (LinearLayout) rootView.findViewById(R.id.layout_options); @Nullable
for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++) @Override
{ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
Button button = (Button) options.getChildAt(childIndex); {
View rootView = inflater.inflate(R.layout.fragment_ingame_menu, container, false);
button.setOnClickListener(this); LinearLayout options = (LinearLayout) rootView.findViewById(R.id.layout_options);
} for (int childIndex = 0; childIndex < options.getChildCount(); childIndex++)
{
Button button = (Button) options.getChildAt(childIndex);
TextView titleText = rootView.findViewById(R.id.text_game_title); button.setOnClickListener(this);
String title = getArguments().getString(KEY_TITLE); }
if (title != null)
{
titleText.setText(title);
}
return rootView; TextView titleText = rootView.findViewById(R.id.text_game_title);
} String title = getArguments().getString(KEY_TITLE);
if (title != null)
{
titleText.setText(title);
}
@SuppressWarnings("WrongConstant") return rootView;
@Override }
public void onClick(View button)
{ @SuppressWarnings("WrongConstant")
int action = buttonsActionsMap.get(button.getId()); @Override
if (action >= 0) public void onClick(View button)
{ {
((EmulationActivity) getActivity()).handleMenuAction(action); int action = buttonsActionsMap.get(button.getId());
} if (action >= 0)
} {
((EmulationActivity) getActivity()).handleMenuAction(action);
}
}
} }

View File

@ -15,86 +15,104 @@ import org.dolphinemu.dolphinemu.activities.EmulationActivity;
public final class SaveLoadStateFragment extends Fragment implements View.OnClickListener public final class SaveLoadStateFragment extends Fragment implements View.OnClickListener
{ {
public enum SaveOrLoad public enum SaveOrLoad
{ {
SAVE, LOAD SAVE, LOAD
} }
private static final String KEY_SAVEORLOAD = "saveorload"; private static final String KEY_SAVEORLOAD = "saveorload";
private static SparseIntArray saveButtonsActionsMap = new SparseIntArray(); private static SparseIntArray saveButtonsActionsMap = new SparseIntArray();
static {
saveButtonsActionsMap.append(R.id.loadsave_state_button_1, EmulationActivity.MENU_ACTION_SAVE_SLOT1);
saveButtonsActionsMap.append(R.id.loadsave_state_button_2, EmulationActivity.MENU_ACTION_SAVE_SLOT2);
saveButtonsActionsMap.append(R.id.loadsave_state_button_3, EmulationActivity.MENU_ACTION_SAVE_SLOT3);
saveButtonsActionsMap.append(R.id.loadsave_state_button_4, EmulationActivity.MENU_ACTION_SAVE_SLOT4);
saveButtonsActionsMap.append(R.id.loadsave_state_button_5, EmulationActivity.MENU_ACTION_SAVE_SLOT5);
saveButtonsActionsMap.append(R.id.loadsave_state_button_6, EmulationActivity.MENU_ACTION_SAVE_SLOT6);
}
private static SparseIntArray loadButtonsActionsMap = new SparseIntArray();
static {
loadButtonsActionsMap.append(R.id.loadsave_state_button_1, EmulationActivity.MENU_ACTION_LOAD_SLOT1);
loadButtonsActionsMap.append(R.id.loadsave_state_button_2, EmulationActivity.MENU_ACTION_LOAD_SLOT2);
loadButtonsActionsMap.append(R.id.loadsave_state_button_3, EmulationActivity.MENU_ACTION_LOAD_SLOT3);
loadButtonsActionsMap.append(R.id.loadsave_state_button_4, EmulationActivity.MENU_ACTION_LOAD_SLOT4);
loadButtonsActionsMap.append(R.id.loadsave_state_button_5, EmulationActivity.MENU_ACTION_LOAD_SLOT5);
loadButtonsActionsMap.append(R.id.loadsave_state_button_6, EmulationActivity.MENU_ACTION_LOAD_SLOT6);
}
private SaveOrLoad mSaveOrLoad;
public static SaveLoadStateFragment newInstance(SaveOrLoad saveOrLoad) static
{ {
SaveLoadStateFragment fragment = new SaveLoadStateFragment(); saveButtonsActionsMap
.append(R.id.loadsave_state_button_1, EmulationActivity.MENU_ACTION_SAVE_SLOT1);
saveButtonsActionsMap
.append(R.id.loadsave_state_button_2, EmulationActivity.MENU_ACTION_SAVE_SLOT2);
saveButtonsActionsMap
.append(R.id.loadsave_state_button_3, EmulationActivity.MENU_ACTION_SAVE_SLOT3);
saveButtonsActionsMap
.append(R.id.loadsave_state_button_4, EmulationActivity.MENU_ACTION_SAVE_SLOT4);
saveButtonsActionsMap
.append(R.id.loadsave_state_button_5, EmulationActivity.MENU_ACTION_SAVE_SLOT5);
saveButtonsActionsMap
.append(R.id.loadsave_state_button_6, EmulationActivity.MENU_ACTION_SAVE_SLOT6);
}
Bundle arguments = new Bundle(); private static SparseIntArray loadButtonsActionsMap = new SparseIntArray();
arguments.putSerializable(KEY_SAVEORLOAD, saveOrLoad);
fragment.setArguments(arguments);
return fragment; static
} {
loadButtonsActionsMap
.append(R.id.loadsave_state_button_1, EmulationActivity.MENU_ACTION_LOAD_SLOT1);
loadButtonsActionsMap
.append(R.id.loadsave_state_button_2, EmulationActivity.MENU_ACTION_LOAD_SLOT2);
loadButtonsActionsMap
.append(R.id.loadsave_state_button_3, EmulationActivity.MENU_ACTION_LOAD_SLOT3);
loadButtonsActionsMap
.append(R.id.loadsave_state_button_4, EmulationActivity.MENU_ACTION_LOAD_SLOT4);
loadButtonsActionsMap
.append(R.id.loadsave_state_button_5, EmulationActivity.MENU_ACTION_LOAD_SLOT5);
loadButtonsActionsMap
.append(R.id.loadsave_state_button_6, EmulationActivity.MENU_ACTION_LOAD_SLOT6);
}
@Override private SaveOrLoad mSaveOrLoad;
public void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mSaveOrLoad = (SaveOrLoad) getArguments().getSerializable(KEY_SAVEORLOAD); public static SaveLoadStateFragment newInstance(SaveOrLoad saveOrLoad)
} {
SaveLoadStateFragment fragment = new SaveLoadStateFragment();
@Nullable Bundle arguments = new Bundle();
@Override arguments.putSerializable(KEY_SAVEORLOAD, saveOrLoad);
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) fragment.setArguments(arguments);
{
View rootView = inflater.inflate(R.layout.fragment_saveload_state, container, false);
GridLayout grid = (GridLayout) rootView.findViewById(R.id.grid_state_slots); return fragment;
for (int childIndex = 0; childIndex < grid.getChildCount(); childIndex++) }
{
Button button = (Button) grid.getChildAt(childIndex);
button.setOnClickListener(this);
}
// So that item clicked to start this Fragment is no longer the focused item. @Override
grid.requestFocus(); public void onCreate(@Nullable Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
return rootView; mSaveOrLoad = (SaveOrLoad) getArguments().getSerializable(KEY_SAVEORLOAD);
} }
@SuppressWarnings("WrongConstant") @Nullable
@Override @Override
public void onClick(View button) public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{ {
int action = 0; View rootView = inflater.inflate(R.layout.fragment_saveload_state, container, false);
switch(mSaveOrLoad)
{ GridLayout grid = (GridLayout) rootView.findViewById(R.id.grid_state_slots);
case SAVE: for (int childIndex = 0; childIndex < grid.getChildCount(); childIndex++)
action = saveButtonsActionsMap.get(button.getId(), -1); {
break; Button button = (Button) grid.getChildAt(childIndex);
case LOAD: button.setOnClickListener(this);
action = loadButtonsActionsMap.get(button.getId(), -1); }
}
if (action >= 0) // So that item clicked to start this Fragment is no longer the focused item.
{ grid.requestFocus();
((EmulationActivity) getActivity()).handleMenuAction(action);
} return rootView;
} }
@SuppressWarnings("WrongConstant")
@Override
public void onClick(View button)
{
int action = 0;
switch (mSaveOrLoad)
{
case SAVE:
action = saveButtonsActionsMap.get(button.getId(), -1);
break;
case LOAD:
action = loadButtonsActionsMap.get(button.getId(), -1);
}
if (action >= 0)
{
((EmulationActivity) getActivity()).handleMenuAction(action);
}
}
} }

View File

@ -1,50 +1,56 @@
package org.dolphinemu.dolphinemu.model; package org.dolphinemu.dolphinemu.model;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment; import android.os.Environment;
import org.dolphinemu.dolphinemu.utils.CoverHelper;
public class GameFile public class GameFile
{ {
private long mPointer; // Do not rename or move without editing the native code private long mPointer; // Do not rename or move without editing the native code
private GameFile(long pointer) private GameFile(long pointer)
{ {
mPointer = pointer; mPointer = pointer;
} }
@Override @Override
public native void finalize(); public native void finalize();
public native int getPlatform(); public native int getPlatform();
public native String getTitle();
public native String getDescription();
public native String getCompany();
public native int getCountry();
public native int getRegion();
public native String getPath();
public native String getGameId();
public native int[] getBanner();
public native int getBannerWidth();
public native int getBannerHeight();
public String getCoverPath() public native String getTitle();
{
return Environment.getExternalStorageDirectory().getPath() +
"/dolphin-emu/Cache/GameCovers/" + getGameId() + ".png";
}
public String getCustomCoverPath() public native String getDescription();
{
return getPath().substring(0, getPath().lastIndexOf(".")) + ".cover.png";
}
public String getScreenshotPath() public native String getCompany();
{
String gameId = getGameId(); public native int getCountry();
return "file://" + Environment.getExternalStorageDirectory().getPath() +
"/dolphin-emu/ScreenShots/" + gameId + "/" + gameId + "-1.png"; public native int getRegion();
}
public native String getPath();
public native String getGameId();
public native int[] getBanner();
public native int getBannerWidth();
public native int getBannerHeight();
public String getCoverPath()
{
return Environment.getExternalStorageDirectory().getPath() +
"/dolphin-emu/Cache/GameCovers/" + getGameId() + ".png";
}
public String getCustomCoverPath()
{
return getPath().substring(0, getPath().lastIndexOf(".")) + ".cover.png";
}
public String getScreenshotPath()
{
String gameId = getGameId();
return "file://" + Environment.getExternalStorageDirectory().getPath() +
"/dolphin-emu/ScreenShots/" + gameId + "/" + gameId + "-1.png";
}
} }

View File

@ -10,80 +10,86 @@ import java.util.Set;
public class GameFileCache public class GameFileCache
{ {
private static final String GAME_FOLDER_PATHS_PREFERENCE = "gameFolderPaths"; private static final String GAME_FOLDER_PATHS_PREFERENCE = "gameFolderPaths";
private static final Set<String> EMPTY_SET = new HashSet<>(); private static final Set<String> EMPTY_SET = new HashSet<>();
private long mPointer; // Do not rename or move without editing the native code private long mPointer; // Do not rename or move without editing the native code
public GameFileCache(String path) public GameFileCache(String path)
{ {
mPointer = newGameFileCache(path); mPointer = newGameFileCache(path);
} }
private static native long newGameFileCache(String path); private static native long newGameFileCache(String path);
@Override @Override
public native void finalize(); public native void finalize();
public static void addGameFolder(String path, Context context) public static void addGameFolder(String path, Context context)
{ {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET); Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
Set<String> newFolderPaths = new HashSet<>(folderPaths); Set<String> newFolderPaths = new HashSet<>(folderPaths);
newFolderPaths.add(path); newFolderPaths.add(path);
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths); editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
editor.apply(); editor.apply();
} }
private void removeNonExistentGameFolders(Context context) private void removeNonExistentGameFolders(Context context)
{ {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET); Set<String> folderPaths = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
Set<String> newFolderPaths = new HashSet<>(); Set<String> newFolderPaths = new HashSet<>();
for (String folderPath : folderPaths) for (String folderPath : folderPaths)
{ {
File folder = new File(folderPath); File folder = new File(folderPath);
if (folder.exists()) if (folder.exists())
{ {
newFolderPaths.add(folderPath); newFolderPaths.add(folderPath);
} }
} }
if (folderPaths.size() != newFolderPaths.size()) if (folderPaths.size() != newFolderPaths.size())
{ {
// One or more folders are being deleted // One or more folders are being deleted
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths); editor.putStringSet(GAME_FOLDER_PATHS_PREFERENCE, newFolderPaths);
editor.apply(); editor.apply();
} }
} }
/** /**
* Scans through the file system and updates the cache to match. * Scans through the file system and updates the cache to match.
* @return true if the cache was modified *
*/ * @return true if the cache was modified
public boolean scanLibrary(Context context) */
{ public boolean scanLibrary(Context context)
removeNonExistentGameFolders(context); {
removeNonExistentGameFolders(context);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Set<String> folderPathsSet = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET); Set<String> folderPathsSet = preferences.getStringSet(GAME_FOLDER_PATHS_PREFERENCE, EMPTY_SET);
String[] folderPaths = folderPathsSet.toArray(new String[folderPathsSet.size()]); String[] folderPaths = folderPathsSet.toArray(new String[folderPathsSet.size()]);
boolean cacheChanged = update(folderPaths); boolean cacheChanged = update(folderPaths);
cacheChanged |= updateAdditionalMetadata(); cacheChanged |= updateAdditionalMetadata();
if (cacheChanged) if (cacheChanged)
{ {
save(); save();
} }
return cacheChanged; return cacheChanged;
} }
public native GameFile[] getAllGames(); public native GameFile[] getAllGames();
public native GameFile addOrGet(String gamePath);
private native boolean update(String[] folderPaths); public native GameFile addOrGet(String gamePath);
private native boolean updateAdditionalMetadata();
public native boolean load(); private native boolean update(String[] folderPaths);
private native boolean save();
private native boolean updateAdditionalMetadata();
public native boolean load();
private native boolean save();
} }

View File

@ -6,59 +6,59 @@ package org.dolphinemu.dolphinemu.model;
public class HomeScreenChannel public class HomeScreenChannel
{ {
private long channelId; private long channelId;
private String name; private String name;
private String description; private String description;
private String appLinkIntentUri; private String appLinkIntentUri;
public HomeScreenChannel() public HomeScreenChannel()
{ {
} }
public HomeScreenChannel(String name, String description, String appLinkIntentUri) public HomeScreenChannel(String name, String description, String appLinkIntentUri)
{ {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.appLinkIntentUri = appLinkIntentUri; this.appLinkIntentUri = appLinkIntentUri;
} }
public long getChannelId() public long getChannelId()
{ {
return channelId; return channelId;
} }
public void setChannelId(long channelId) public void setChannelId(long channelId)
{ {
this.channelId = channelId; this.channelId = channelId;
} }
public String getName() public String getName()
{ {
return name; return name;
} }
public void setName(String name) public void setName(String name)
{ {
this.name = name; this.name = name;
} }
public String getDescription() public String getDescription()
{ {
return description; return description;
} }
public void setDescription(String description) public void setDescription(String description)
{ {
this.description = description; this.description = description;
} }
public String getAppLinkIntentUri() public String getAppLinkIntentUri()
{ {
return appLinkIntentUri; return appLinkIntentUri;
} }
public void setAppLinkIntentUri(String appLinkIntentUri) public void setAppLinkIntentUri(String appLinkIntentUri)
{ {
this.appLinkIntentUri = appLinkIntentUri; this.appLinkIntentUri = appLinkIntentUri;
} }
} }

View File

@ -2,29 +2,29 @@ package org.dolphinemu.dolphinemu.model;
public final class TvSettingsItem public final class TvSettingsItem
{ {
private final int mItemId; private final int mItemId;
private final int mIconId; private final int mIconId;
private final int mLabelId; private final int mLabelId;
public TvSettingsItem(int itemId, int iconId, int labelId) public TvSettingsItem(int itemId, int iconId, int labelId)
{ {
mItemId = itemId; mItemId = itemId;
mIconId = iconId; mIconId = iconId;
mLabelId = labelId; mLabelId = labelId;
} }
public int getItemId() public int getItemId()
{ {
return mItemId; return mItemId;
} }
public int getIconId() public int getIconId()
{ {
return mIconId; return mIconId;
} }
public int getLabelId() public int getLabelId()
{ {
return mLabelId; return mLabelId;
} }
} }

View File

@ -19,117 +19,119 @@ import android.view.MotionEvent;
*/ */
public final class InputOverlayDrawableButton public final class InputOverlayDrawableButton
{ {
// The ID identifying what type of button this Drawable represents. // The ID identifying what type of button this Drawable represents.
private int mButtonType; private int mButtonType;
private int mTrackId; private int mTrackId;
private int mPreviousTouchX, mPreviousTouchY; private int mPreviousTouchX, mPreviousTouchY;
private int mControlPositionX, mControlPositionY; private int mControlPositionX, mControlPositionY;
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
private BitmapDrawable mDefaultStateBitmap; private BitmapDrawable mDefaultStateBitmap;
private BitmapDrawable mPressedStateBitmap; private BitmapDrawable mPressedStateBitmap;
private boolean mPressedState = false; private boolean mPressedState = false;
/** /**
* Constructor * Constructor
* *
* @param res {@link Resources} instance. * @param res {@link Resources} instance.
* @param defaultStateBitmap {@link Bitmap} to use with the default state Drawable. * @param defaultStateBitmap {@link Bitmap} to use with the default state Drawable.
* @param pressedStateBitmap {@link Bitmap} to use with the pressed state Drawable. * @param pressedStateBitmap {@link Bitmap} to use with the pressed state Drawable.
* @param buttonType Identifier for this type of button. * @param buttonType Identifier for this type of button.
*/ */
public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap, Bitmap pressedStateBitmap, int buttonType) public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap,
{ Bitmap pressedStateBitmap, int buttonType)
mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); {
mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap); mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap);
mButtonType = buttonType; mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap);
mButtonType = buttonType;
mWidth = mDefaultStateBitmap.getIntrinsicWidth(); mWidth = mDefaultStateBitmap.getIntrinsicWidth();
mHeight = mDefaultStateBitmap.getIntrinsicHeight(); mHeight = mDefaultStateBitmap.getIntrinsicHeight();
} }
/** /**
* Gets this InputOverlayDrawableButton's button ID. * Gets this InputOverlayDrawableButton's button ID.
* *
* @return this InputOverlayDrawableButton's button ID. * @return this InputOverlayDrawableButton's button ID.
*/ */
public int getId() public int getId()
{ {
return mButtonType; return mButtonType;
} }
public void setTrackId(int trackId) public void setTrackId(int trackId)
{ {
mTrackId = trackId; mTrackId = trackId;
} }
public int getTrackId() public int getTrackId()
{ {
return mTrackId; return mTrackId;
} }
public boolean onConfigureTouch(MotionEvent event) public boolean onConfigureTouch(MotionEvent event)
{ {
int pointerIndex = event.getActionIndex(); int pointerIndex = event.getActionIndex();
int fingerPositionX = (int)event.getX(pointerIndex); int fingerPositionX = (int) event.getX(pointerIndex);
int fingerPositionY = (int)event.getY(pointerIndex); int fingerPositionY = (int) event.getY(pointerIndex);
switch (event.getAction()) switch (event.getAction())
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
mPreviousTouchX = fingerPositionX; mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY; mPreviousTouchY = fingerPositionY;
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
mControlPositionX += fingerPositionX - mPreviousTouchX; mControlPositionX += fingerPositionX - mPreviousTouchX;
mControlPositionY += fingerPositionY - mPreviousTouchY; mControlPositionY += fingerPositionY - mPreviousTouchY;
setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX, getHeight() + mControlPositionY); setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX,
mPreviousTouchX = fingerPositionX; getHeight() + mControlPositionY);
mPreviousTouchY = fingerPositionY; mPreviousTouchX = fingerPositionX;
break; mPreviousTouchY = fingerPositionY;
break;
} }
return true; return true;
} }
public void setPosition(int x, int y) public void setPosition(int x, int y)
{ {
mControlPositionX = x; mControlPositionX = x;
mControlPositionY = y; mControlPositionY = y;
} }
public void draw(Canvas canvas) public void draw(Canvas canvas)
{ {
getCurrentStateBitmapDrawable().draw(canvas); getCurrentStateBitmapDrawable().draw(canvas);
} }
private BitmapDrawable getCurrentStateBitmapDrawable() private BitmapDrawable getCurrentStateBitmapDrawable()
{ {
return mPressedState ? mPressedStateBitmap : mDefaultStateBitmap; return mPressedState ? mPressedStateBitmap : mDefaultStateBitmap;
} }
public void setBounds(int left, int top, int right, int bottom) public void setBounds(int left, int top, int right, int bottom)
{ {
mDefaultStateBitmap.setBounds(left, top, right, bottom); mDefaultStateBitmap.setBounds(left, top, right, bottom);
mPressedStateBitmap.setBounds(left, top, right, bottom); mPressedStateBitmap.setBounds(left, top, right, bottom);
} }
public Rect getBounds() public Rect getBounds()
{ {
return mDefaultStateBitmap.getBounds(); return mDefaultStateBitmap.getBounds();
} }
public int getWidth() public int getWidth()
{ {
return mWidth; return mWidth;
} }
public int getHeight() public int getHeight()
{ {
return mHeight; return mHeight;
} }
public void setPressedState(boolean isPressed) public void setPressedState(boolean isPressed)
{ {
mPressedState = isPressed; mPressedState = isPressed;
} }
} }

View File

@ -19,183 +19,188 @@ import android.view.MotionEvent;
*/ */
public final class InputOverlayDrawableDpad public final class InputOverlayDrawableDpad
{ {
// The ID identifying what type of button this Drawable represents. // The ID identifying what type of button this Drawable represents.
private int[] mButtonType = new int[4]; private int[] mButtonType = new int[4];
private int mTrackId; private int mTrackId;
private int mPreviousTouchX, mPreviousTouchY; private int mPreviousTouchX, mPreviousTouchY;
private int mControlPositionX, mControlPositionY; private int mControlPositionX, mControlPositionY;
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
private BitmapDrawable mDefaultStateBitmap; private BitmapDrawable mDefaultStateBitmap;
private BitmapDrawable mPressedOneDirectionStateBitmap; private BitmapDrawable mPressedOneDirectionStateBitmap;
private BitmapDrawable mPressedTwoDirectionsStateBitmap; private BitmapDrawable mPressedTwoDirectionsStateBitmap;
private int mPressState = STATE_DEFAULT; private int mPressState = STATE_DEFAULT;
public static final int STATE_DEFAULT = 0; public static final int STATE_DEFAULT = 0;
public static final int STATE_PRESSED_UP = 1; public static final int STATE_PRESSED_UP = 1;
public static final int STATE_PRESSED_DOWN = 2; public static final int STATE_PRESSED_DOWN = 2;
public static final int STATE_PRESSED_LEFT = 3; public static final int STATE_PRESSED_LEFT = 3;
public static final int STATE_PRESSED_RIGHT = 4; public static final int STATE_PRESSED_RIGHT = 4;
public static final int STATE_PRESSED_UP_LEFT = 5; public static final int STATE_PRESSED_UP_LEFT = 5;
public static final int STATE_PRESSED_UP_RIGHT = 6; 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_LEFT = 7;
public static final int STATE_PRESSED_DOWN_RIGHT = 8; public static final int STATE_PRESSED_DOWN_RIGHT = 8;
/** /**
* Constructor * Constructor
* *
* @param res {@link Resources} instance. * @param res {@link Resources} instance.
* @param defaultStateBitmap {@link Bitmap} of the default state. * @param defaultStateBitmap {@link Bitmap} of the default state.
* @param pressedOneDirectionStateBitmap {@link Bitmap} of the pressed state in one direction. * @param pressedOneDirectionStateBitmap {@link Bitmap} of the pressed state in one direction.
* @param pressedTwoDirectionsStateBitmap {@link Bitmap} of the pressed state in two direction. * @param pressedTwoDirectionsStateBitmap {@link Bitmap} of the pressed state in two direction.
* @param buttonUp Identifier for the up button. * @param buttonUp Identifier for the up button.
* @param buttonDown Identifier for the down button. * @param buttonDown Identifier for the down button.
* @param buttonLeft Identifier for the left button. * @param buttonLeft Identifier for the left button.
* @param buttonRight Identifier for the right button. * @param buttonRight Identifier for the right button.
*/ */
public InputOverlayDrawableDpad(Resources res, public InputOverlayDrawableDpad(Resources res,
Bitmap defaultStateBitmap, Bitmap defaultStateBitmap,
Bitmap pressedOneDirectionStateBitmap, Bitmap pressedOneDirectionStateBitmap,
Bitmap pressedTwoDirectionsStateBitmap, Bitmap pressedTwoDirectionsStateBitmap,
int buttonUp, int buttonDown, int buttonUp, int buttonDown,
int buttonLeft, int buttonRight) int buttonLeft, int buttonRight)
{ {
mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap);
mPressedOneDirectionStateBitmap = new BitmapDrawable(res, pressedOneDirectionStateBitmap); mPressedOneDirectionStateBitmap = new BitmapDrawable(res, pressedOneDirectionStateBitmap);
mPressedTwoDirectionsStateBitmap = new BitmapDrawable(res, pressedTwoDirectionsStateBitmap); mPressedTwoDirectionsStateBitmap = new BitmapDrawable(res, pressedTwoDirectionsStateBitmap);
mWidth = mDefaultStateBitmap.getIntrinsicWidth(); mWidth = mDefaultStateBitmap.getIntrinsicWidth();
mHeight = mDefaultStateBitmap.getIntrinsicHeight(); mHeight = mDefaultStateBitmap.getIntrinsicHeight();
mButtonType[0] = buttonUp; mButtonType[0] = buttonUp;
mButtonType[1] = buttonDown; mButtonType[1] = buttonDown;
mButtonType[2] = buttonLeft; mButtonType[2] = buttonLeft;
mButtonType[3] = buttonRight; mButtonType[3] = buttonRight;
} }
public void draw(Canvas canvas) public void draw(Canvas canvas)
{ {
int px = mControlPositionX + (getWidth()/2); int px = mControlPositionX + (getWidth() / 2);
int py = mControlPositionY + (getHeight()/2); int py = mControlPositionY + (getHeight() / 2);
switch (mPressState) { switch (mPressState)
case STATE_DEFAULT: {
mDefaultStateBitmap.draw(canvas); case STATE_DEFAULT:
break; mDefaultStateBitmap.draw(canvas);
case STATE_PRESSED_UP: break;
mPressedOneDirectionStateBitmap.draw(canvas); case STATE_PRESSED_UP:
break; mPressedOneDirectionStateBitmap.draw(canvas);
case STATE_PRESSED_RIGHT: break;
canvas.save(); case STATE_PRESSED_RIGHT:
canvas.rotate(90, px, py); canvas.save();
mPressedOneDirectionStateBitmap.draw(canvas); canvas.rotate(90, px, py);
canvas.restore(); mPressedOneDirectionStateBitmap.draw(canvas);
break; canvas.restore();
case STATE_PRESSED_DOWN: break;
canvas.save(); case STATE_PRESSED_DOWN:
canvas.rotate(180, px, py); canvas.save();
mPressedOneDirectionStateBitmap.draw(canvas); canvas.rotate(180, px, py);
canvas.restore(); mPressedOneDirectionStateBitmap.draw(canvas);
break; canvas.restore();
case STATE_PRESSED_LEFT: break;
canvas.save(); case STATE_PRESSED_LEFT:
canvas.rotate(270, px, py); canvas.save();
mPressedOneDirectionStateBitmap.draw(canvas); canvas.rotate(270, px, py);
canvas.restore(); mPressedOneDirectionStateBitmap.draw(canvas);
break; canvas.restore();
case STATE_PRESSED_UP_LEFT: break;
mPressedTwoDirectionsStateBitmap.draw(canvas); case STATE_PRESSED_UP_LEFT:
break; mPressedTwoDirectionsStateBitmap.draw(canvas);
case STATE_PRESSED_UP_RIGHT: break;
canvas.save(); case STATE_PRESSED_UP_RIGHT:
canvas.rotate(90, px, py); canvas.save();
mPressedTwoDirectionsStateBitmap.draw(canvas); canvas.rotate(90, px, py);
canvas.restore(); mPressedTwoDirectionsStateBitmap.draw(canvas);
break; canvas.restore();
case STATE_PRESSED_DOWN_RIGHT: break;
canvas.save(); case STATE_PRESSED_DOWN_RIGHT:
canvas.rotate(180, px, py); canvas.save();
mPressedTwoDirectionsStateBitmap.draw(canvas); canvas.rotate(180, px, py);
canvas.restore(); mPressedTwoDirectionsStateBitmap.draw(canvas);
break; canvas.restore();
case STATE_PRESSED_DOWN_LEFT: break;
canvas.save(); case STATE_PRESSED_DOWN_LEFT:
canvas.rotate(270, px, py); canvas.save();
mPressedTwoDirectionsStateBitmap.draw(canvas); canvas.rotate(270, px, py);
canvas.restore(); mPressedTwoDirectionsStateBitmap.draw(canvas);
break; canvas.restore();
} break;
} }
}
/** /**
* Gets one of the InputOverlayDrawableDpad's button IDs. * Gets one of the InputOverlayDrawableDpad's button IDs.
* *
* @return the requested InputOverlayDrawableDpad's button ID. * @return the requested InputOverlayDrawableDpad's button ID.
*/ */
public int getId(int direction) public int getId(int direction)
{ {
return mButtonType[direction]; return mButtonType[direction];
} }
public void setTrackId(int trackId) public void setTrackId(int trackId)
{ {
mTrackId = trackId; mTrackId = trackId;
} }
public int getTrackId() public int getTrackId()
{ {
return mTrackId; return mTrackId;
} }
public boolean onConfigureTouch(MotionEvent event) public boolean onConfigureTouch(MotionEvent event)
{ {
int pointerIndex = event.getActionIndex(); int pointerIndex = event.getActionIndex();
int fingerPositionX = (int)event.getX(pointerIndex); int fingerPositionX = (int) event.getX(pointerIndex);
int fingerPositionY = (int)event.getY(pointerIndex); int fingerPositionY = (int) event.getY(pointerIndex);
switch (event.getAction()) switch (event.getAction())
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
mPreviousTouchX = fingerPositionX; mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY; mPreviousTouchY = fingerPositionY;
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
mControlPositionX += fingerPositionX - mPreviousTouchX; mControlPositionX += fingerPositionX - mPreviousTouchX;
mControlPositionY += fingerPositionY - mPreviousTouchY; mControlPositionY += fingerPositionY - mPreviousTouchY;
setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX, getHeight() + mControlPositionY); setBounds(mControlPositionX, mControlPositionY, getWidth() + mControlPositionX,
mPreviousTouchX = fingerPositionX; getHeight() + mControlPositionY);
mPreviousTouchY = fingerPositionY; mPreviousTouchX = fingerPositionX;
break; mPreviousTouchY = fingerPositionY;
break;
} }
return true; return true;
} }
public void setPosition(int x, int y) public void setPosition(int x, int y)
{ {
mControlPositionX = x; mControlPositionX = x;
mControlPositionY = y; mControlPositionY = y;
} }
public void setBounds(int left, int top, int right, int bottom) public void setBounds(int left, int top, int right, int bottom)
{ {
mDefaultStateBitmap.setBounds(left, top, right, bottom); mDefaultStateBitmap.setBounds(left, top, right, bottom);
mPressedOneDirectionStateBitmap.setBounds(left, top, right, bottom); mPressedOneDirectionStateBitmap.setBounds(left, top, right, bottom);
mPressedTwoDirectionsStateBitmap.setBounds(left, top, right, bottom); mPressedTwoDirectionsStateBitmap.setBounds(left, top, right, bottom);
} }
public Rect getBounds() public Rect getBounds()
{ {
return mDefaultStateBitmap.getBounds(); return mDefaultStateBitmap.getBounds();
} }
public int getWidth() { public int getWidth()
return mWidth; {
} return mWidth;
}
public int getHeight() { public int getHeight()
return mHeight; {
} return mHeight;
}
public void setState(int pressState) { public void setState(int pressState)
mPressState = pressState; {
} mPressState = pressState;
}
} }

View File

@ -6,7 +6,6 @@
package org.dolphinemu.dolphinemu.overlay; package org.dolphinemu.dolphinemu.overlay;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -21,246 +20,258 @@ import android.view.MotionEvent;
*/ */
public final class InputOverlayDrawableJoystick public final class InputOverlayDrawableJoystick
{ {
private SharedPreferences mPreferences; private SharedPreferences mPreferences;
private final int[] axisIDs = {0, 0, 0, 0}; private final int[] axisIDs = {0, 0, 0, 0};
private final float[] axises = {0f, 0f}; private final float[] axises = {0f, 0f};
private int trackId = -1; private int trackId = -1;
private int mJoystickType; private int mJoystickType;
private int mControlPositionX, mControlPositionY; private int mControlPositionX, mControlPositionY;
private int mPreviousTouchX, mPreviousTouchY; private int mPreviousTouchX, mPreviousTouchY;
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
private Rect mVirtBounds; private Rect mVirtBounds;
private Rect mOrigBounds; private Rect mOrigBounds;
private BitmapDrawable mOuterBitmap; private BitmapDrawable mOuterBitmap;
private BitmapDrawable mDefaultStateInnerBitmap; private BitmapDrawable mDefaultStateInnerBitmap;
private BitmapDrawable mPressedStateInnerBitmap; private BitmapDrawable mPressedStateInnerBitmap;
private BitmapDrawable mBoundsBoxBitmap; private BitmapDrawable mBoundsBoxBitmap;
private boolean mPressedState = false; private boolean mPressedState = false;
/** /**
* Constructor * Constructor
* *
* @param res {@link Resources} instance. * @param res {@link Resources} instance.
* @param bitmapOuter {@link Bitmap} which represents the outer non-movable part of the joystick. * @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 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 bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick.
* @param rectOuter {@link Rect} which represents the outer joystick bounds. * @param rectOuter {@link Rect} which represents the outer joystick bounds.
* @param rectInner {@link Rect} which represents the inner joystick bounds. * @param rectInner {@link Rect} which represents the inner joystick bounds.
* @param joystick Identifier for which joystick this is. * @param joystick Identifier for which joystick this is.
*/ */
public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter,
Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed, Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed,
Rect rectOuter, Rect rectInner, int joystick, SharedPreferences prefsHandle) Rect rectOuter, Rect rectInner, int joystick, SharedPreferences prefsHandle)
{ {
axisIDs[0] = joystick + 1; axisIDs[0] = joystick + 1;
axisIDs[1] = joystick + 2; axisIDs[1] = joystick + 2;
axisIDs[2] = joystick + 3; axisIDs[2] = joystick + 3;
axisIDs[3] = joystick + 4; axisIDs[3] = joystick + 4;
mJoystickType = joystick; mJoystickType = joystick;
mPreferences = prefsHandle; mPreferences = prefsHandle;
mOuterBitmap = new BitmapDrawable(res, bitmapOuter); mOuterBitmap = new BitmapDrawable(res, bitmapOuter);
mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault); mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault);
mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed); mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed);
mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter); mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter);
mWidth = bitmapOuter.getWidth(); mWidth = bitmapOuter.getWidth();
mHeight = bitmapOuter.getHeight(); mHeight = bitmapOuter.getHeight();
setBounds(rectOuter); setBounds(rectOuter);
mDefaultStateInnerBitmap.setBounds(rectInner); mDefaultStateInnerBitmap.setBounds(rectInner);
mPressedStateInnerBitmap.setBounds(rectInner); mPressedStateInnerBitmap.setBounds(rectInner);
mVirtBounds = getBounds(); mVirtBounds = getBounds();
mOrigBounds = mOuterBitmap.copyBounds(); mOrigBounds = mOuterBitmap.copyBounds();
mBoundsBoxBitmap.setAlpha(0); mBoundsBoxBitmap.setAlpha(0);
mBoundsBoxBitmap.setBounds(getVirtBounds()); mBoundsBoxBitmap.setBounds(getVirtBounds());
SetInnerBounds(); SetInnerBounds();
} }
/** /**
* Gets this InputOverlayDrawableJoystick's button ID. * Gets this InputOverlayDrawableJoystick's button ID.
* *
* @return this InputOverlayDrawableJoystick's button ID. * @return this InputOverlayDrawableJoystick's button ID.
*/ */
public int getId() public int getId()
{ {
return mJoystickType; return mJoystickType;
} }
public void draw(Canvas canvas) public void draw(Canvas canvas)
{ {
mOuterBitmap.draw(canvas); mOuterBitmap.draw(canvas);
getCurrentStateBitmapDrawable().draw(canvas); getCurrentStateBitmapDrawable().draw(canvas);
mBoundsBoxBitmap.draw(canvas); mBoundsBoxBitmap.draw(canvas);
} }
public void TrackEvent(MotionEvent event) public void TrackEvent(MotionEvent event)
{ {
boolean reCenter = mPreferences.getBoolean("joystickRelCenter", true); boolean reCenter = mPreferences.getBoolean("joystickRelCenter", true);
int pointerIndex = event.getActionIndex(); int pointerIndex = event.getActionIndex();
switch(event.getAction() & MotionEvent.ACTION_MASK) switch (event.getAction() & MotionEvent.ACTION_MASK)
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_POINTER_DOWN:
if (getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex))) if (getBounds().contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex)))
{ {
mPressedState = true; mPressedState = true;
mOuterBitmap.setAlpha(0); mOuterBitmap.setAlpha(0);
mBoundsBoxBitmap.setAlpha(255); mBoundsBoxBitmap.setAlpha(255);
if (reCenter) if (reCenter)
{ {
getVirtBounds().offset((int)event.getX(pointerIndex) - getVirtBounds().centerX(), (int)event.getY(pointerIndex) - getVirtBounds().centerY()); getVirtBounds().offset((int) event.getX(pointerIndex) - getVirtBounds().centerX(),
} (int) event.getY(pointerIndex) - getVirtBounds().centerY());
mBoundsBoxBitmap.setBounds(getVirtBounds()); }
trackId = event.getPointerId(pointerIndex); mBoundsBoxBitmap.setBounds(getVirtBounds());
} trackId = event.getPointerId(pointerIndex);
break; }
case MotionEvent.ACTION_UP: break;
case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_UP:
if (trackId == event.getPointerId(pointerIndex)) case MotionEvent.ACTION_POINTER_UP:
{ if (trackId == event.getPointerId(pointerIndex))
mPressedState = false; {
axises[0] = axises[1] = 0.0f; mPressedState = false;
mOuterBitmap.setAlpha(255); axises[0] = axises[1] = 0.0f;
mBoundsBoxBitmap.setAlpha(0); mOuterBitmap.setAlpha(255);
setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, mOrigBounds.bottom)); mBoundsBoxBitmap.setAlpha(0);
setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, mOrigBounds.bottom)); setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
SetInnerBounds(); mOrigBounds.bottom));
trackId = -1; setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
} mOrigBounds.bottom));
break; SetInnerBounds();
} trackId = -1;
}
break;
}
if (trackId == -1) if (trackId == -1)
return; return;
for (int i = 0; i < event.getPointerCount(); i++) for (int i = 0; i < event.getPointerCount(); i++)
{ {
if (trackId == event.getPointerId(i)) if (trackId == event.getPointerId(i))
{ {
float touchX = event.getX(i); float touchX = event.getX(i);
float touchY = event.getY(i); float touchY = event.getY(i);
float maxY = getVirtBounds().bottom; float maxY = getVirtBounds().bottom;
float maxX = getVirtBounds().right; float maxX = getVirtBounds().right;
touchX -= getVirtBounds().centerX(); touchX -= getVirtBounds().centerX();
maxX -= getVirtBounds().centerX(); maxX -= getVirtBounds().centerX();
touchY -= getVirtBounds().centerY(); touchY -= getVirtBounds().centerY();
maxY -= getVirtBounds().centerY(); maxY -= getVirtBounds().centerY();
final float AxisX = touchX / maxX; final float AxisX = touchX / maxX;
final float AxisY = touchY / maxY; final float AxisY = touchY / maxY;
axises[0] = AxisY; axises[0] = AxisY;
axises[1] = AxisX; axises[1] = AxisX;
SetInnerBounds(); SetInnerBounds();
} }
} }
} }
public boolean onConfigureTouch(MotionEvent event) public boolean onConfigureTouch(MotionEvent event)
{ {
int pointerIndex = event.getActionIndex(); int pointerIndex = event.getActionIndex();
int fingerPositionX = (int)event.getX(pointerIndex); int fingerPositionX = (int) event.getX(pointerIndex);
int fingerPositionY = (int)event.getY(pointerIndex); int fingerPositionY = (int) event.getY(pointerIndex);
switch (event.getAction()) switch (event.getAction())
{ {
case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_DOWN:
mPreviousTouchX = fingerPositionX; mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY; mPreviousTouchY = fingerPositionY;
break; break;
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
int deltaX = fingerPositionX - mPreviousTouchX; int deltaX = fingerPositionX - mPreviousTouchX;
int deltaY = fingerPositionY - mPreviousTouchY; int deltaY = fingerPositionY - mPreviousTouchY;
mControlPositionX += deltaX; mControlPositionX += deltaX;
mControlPositionY += deltaY; mControlPositionY += deltaY;
setBounds(new Rect(mControlPositionX, mControlPositionY, setBounds(new Rect(mControlPositionX, mControlPositionY,
mOuterBitmap.getIntrinsicWidth() + mControlPositionX, mOuterBitmap.getIntrinsicWidth() + mControlPositionX,
mOuterBitmap.getIntrinsicHeight() + mControlPositionY)); mOuterBitmap.getIntrinsicHeight() + mControlPositionY));
setVirtBounds(new Rect(mControlPositionX, mControlPositionY, setVirtBounds(new Rect(mControlPositionX, mControlPositionY,
mOuterBitmap.getIntrinsicWidth() + mControlPositionX, mOuterBitmap.getIntrinsicWidth() + mControlPositionX,
mOuterBitmap.getIntrinsicHeight() + mControlPositionY)); mOuterBitmap.getIntrinsicHeight() + mControlPositionY));
SetInnerBounds(); SetInnerBounds();
setOrigBounds(new Rect(new Rect(mControlPositionX, mControlPositionY, setOrigBounds(new Rect(new Rect(mControlPositionX, mControlPositionY,
mOuterBitmap.getIntrinsicWidth() + mControlPositionX, mOuterBitmap.getIntrinsicWidth() + mControlPositionX,
mOuterBitmap.getIntrinsicHeight() + mControlPositionY))); mOuterBitmap.getIntrinsicHeight() + mControlPositionY)));
mPreviousTouchX = fingerPositionX; mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY; mPreviousTouchY = fingerPositionY;
break; break;
} }
return true; return true;
} }
public float[] getAxisValues() public float[] getAxisValues()
{ {
float[] joyaxises = {0f, 0f, 0f, 0f}; float[] joyaxises = {0f, 0f, 0f, 0f};
joyaxises[1] = Math.min(axises[0], 1.0f); joyaxises[1] = Math.min(axises[0], 1.0f);
joyaxises[0] = Math.min(axises[0], 0.0f); joyaxises[0] = Math.min(axises[0], 0.0f);
joyaxises[3] = Math.min(axises[1], 1.0f); joyaxises[3] = Math.min(axises[1], 1.0f);
joyaxises[2] = Math.min(axises[1], 0.0f); joyaxises[2] = Math.min(axises[1], 0.0f);
return joyaxises; return joyaxises;
} }
public int[] getAxisIDs() public int[] getAxisIDs()
{ {
return axisIDs; return axisIDs;
} }
private void SetInnerBounds() private void SetInnerBounds()
{ {
int X = getVirtBounds().centerX() + (int)((axises[1]) * (getVirtBounds().width() / 2)); int X = getVirtBounds().centerX() + (int) ((axises[1]) * (getVirtBounds().width() / 2));
int Y = getVirtBounds().centerY() + (int)((axises[0]) * (getVirtBounds().height() / 2)); int Y = getVirtBounds().centerY() + (int) ((axises[0]) * (getVirtBounds().height() / 2));
if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2)) if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2))
X = getVirtBounds().centerX() + (getVirtBounds().width() / 2); X = getVirtBounds().centerX() + (getVirtBounds().width() / 2);
if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2)) if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2))
X = getVirtBounds().centerX() - (getVirtBounds().width() / 2); X = getVirtBounds().centerX() - (getVirtBounds().width() / 2);
if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2)) if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2))
Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2); Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2);
if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2)) if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2))
Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2); Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2);
int width = mPressedStateInnerBitmap.getBounds().width() / 2; int width = mPressedStateInnerBitmap.getBounds().width() / 2;
int height = mPressedStateInnerBitmap.getBounds().height() / 2; int height = mPressedStateInnerBitmap.getBounds().height() / 2;
mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height); mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height);
mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds()); mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds());
} }
public void setPosition(int x, int y) public void setPosition(int x, int y)
{ {
mControlPositionX = x; mControlPositionX = x;
mControlPositionY = y; mControlPositionY = y;
} }
private BitmapDrawable getCurrentStateBitmapDrawable() private BitmapDrawable getCurrentStateBitmapDrawable()
{ {
return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap; return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap;
} }
public void setBounds(Rect bounds) public void setBounds(Rect bounds)
{ {
mOuterBitmap.setBounds(bounds); mOuterBitmap.setBounds(bounds);
} }
public Rect getBounds() public Rect getBounds()
{ {
return mOuterBitmap.getBounds(); return mOuterBitmap.getBounds();
} }
private void setVirtBounds(Rect bounds) { mVirtBounds = 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;
}
public int getWidth() public int getWidth()
{ {
return mWidth; return mWidth;
} }
public int getHeight() public int getHeight()
{ {
return mHeight; return mHeight;
} }
} }

View File

@ -32,231 +32,239 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/ */
public final class DirectoryInitializationService extends IntentService public final class DirectoryInitializationService extends IntentService
{ {
public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.DIRECTORY_INITIALIZATION"; public static final String BROADCAST_ACTION =
"org.dolphinemu.dolphinemu.DIRECTORY_INITIALIZATION";
public static final String EXTRA_STATE = "directoryState"; public static final String EXTRA_STATE = "directoryState";
private static volatile DirectoryInitializationState directoryState = null; private static volatile DirectoryInitializationState directoryState = null;
private static String userPath; private static String userPath;
private static String internalPath; private static String internalPath;
private static AtomicBoolean isDolphinDirectoryInitializationRunning = new AtomicBoolean(false); private static AtomicBoolean isDolphinDirectoryInitializationRunning = new AtomicBoolean(false);
public enum DirectoryInitializationState 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 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)
{ {
DOLPHIN_DIRECTORIES_INITIALIZED, if (PermissionsHandler.hasWriteAccess(this))
EXTERNAL_STORAGE_PERMISSION_NEEDED, {
CANT_FIND_EXTERNAL_STORAGE if (setDolphinUserDirectory())
}
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);
}
@Override
protected void onHandleIntent(Intent intent)
{
isDolphinDirectoryInitializationRunning.set(true);
if (directoryState != DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
{ {
if (PermissionsHandler.hasWriteAccess(this)) initializeInternalStorage();
{ initializeExternalStorage();
if (setDolphinUserDirectory())
{
initializeInternalStorage();
initializeExternalStorage();
directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; directoryState = DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED;
}
else
{
directoryState = DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE;
}
}
else
{
directoryState = DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED;
}
} }
else
isDolphinDirectoryInitializationRunning.set(false); {
sendBroadcastState(directoryState); directoryState = DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE;
}
}
else
{
directoryState = DirectoryInitializationState.EXTERNAL_STORAGE_PERMISSION_NEEDED;
}
} }
private boolean setDolphinUserDirectory() isDolphinDirectoryInitializationRunning.set(false);
sendBroadcastState(directoryState);
}
private boolean setDolphinUserDirectory()
{
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{ {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) File externalPath = Environment.getExternalStorageDirectory();
{ if (externalPath != null)
File externalPath = Environment.getExternalStorageDirectory(); {
if (externalPath != null) userPath = externalPath.getAbsolutePath() + "/dolphin-emu";
{ Log.debug("[DirectoryInitializationService] User Dir: " + userPath);
userPath = externalPath.getAbsolutePath() + "/dolphin-emu"; NativeLibrary.SetUserDirectory(userPath);
Log.debug("[DirectoryInitializationService] User Dir: " + userPath); return true;
NativeLibrary.SetUserDirectory(userPath); }
return true;
}
}
return false;
} }
private void initializeInternalStorage() return false;
}
private void initializeInternalStorage()
{
File sysDirectory = new File(getFilesDir(), "Sys");
internalPath = sysDirectory.getAbsolutePath();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
String revision = NativeLibrary.GetGitRevision();
if (!preferences.getString("sysDirectoryVersion", "").equals(revision))
{ {
File sysDirectory = new File(getFilesDir(), "Sys"); // There is no extracted Sys directory, or there is a Sys directory from another
internalPath = sysDirectory.getAbsolutePath(); // version of Dolphin that might contain outdated files. Let's (re-)extract Sys.
deleteDirectoryRecursively(sysDirectory);
copyAssetFolder("Sys", sysDirectory, true);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences.Editor editor = preferences.edit();
String revision = NativeLibrary.GetGitRevision(); editor.putString("sysDirectoryVersion", revision);
if (!preferences.getString("sysDirectoryVersion", "").equals(revision)) editor.apply();
{
// 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 void initializeExternalStorage() // Let the native code know where the Sys directory is.
SetSysDirectory(sysDirectory.getPath());
}
private void initializeExternalStorage()
{
// Create User directory structure and copy some NAND files from the extracted Sys directory.
CreateUserDirectories();
// GCPadNew.ini and WiimoteNew.ini must contain specific values in order for controller
// input to work as intended (they aren't user configurable), so we overwrite them just
// in case the user has tried to modify them manually.
//
// ...Except WiimoteNew.ini contains the user configurable settings for Wii Remote
// extensions in addition to all of its lines that aren't user configurable, so since we
// don't want to lose the selected extensions, we don't overwrite that file if it exists.
//
// TODO: Redo the Android controller system so that we don't have to extract these INIs.
String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config";
copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true);
copyAsset("WiimoteNew.ini", new File(configDirectory, "WiimoteNew.ini"), false);
}
private static void deleteDirectoryRecursively(File file)
{
if (file.isDirectory())
{ {
// Create User directory structure and copy some NAND files from the extracted Sys directory. for (File child : file.listFiles())
CreateUserDirectories(); deleteDirectoryRecursively(child);
// GCPadNew.ini and WiimoteNew.ini must contain specific values in order for controller
// input to work as intended (they aren't user configurable), so we overwrite them just
// in case the user has tried to modify them manually.
//
// ...Except WiimoteNew.ini contains the user configurable settings for Wii Remote
// extensions in addition to all of its lines that aren't user configurable, so since we
// don't want to lose the selected extensions, we don't overwrite that file if it exists.
//
// TODO: Redo the Android controller system so that we don't have to extract these INIs.
String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config";
copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true);
copyAsset("WiimoteNew.ini", new File(configDirectory,"WiimoteNew.ini"), false);
} }
file.delete();
}
private static void deleteDirectoryRecursively(File file) public static boolean areDolphinDirectoriesReady()
{
return directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED;
}
public static String getUserDirectory()
{
if (directoryState == null)
{ {
if (file.isDirectory()) throw new IllegalStateException("DirectoryInitializationService has to run at least once!");
{
for (File child : file.listFiles())
deleteDirectoryRecursively(child);
}
file.delete();
} }
else if (isDolphinDirectoryInitializationRunning.get())
public static boolean areDolphinDirectoriesReady()
{ {
return directoryState == DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED; throw new IllegalStateException(
"DirectoryInitializationService has to finish running first!");
} }
return userPath;
public static String getUserDirectory() }
public static String getDolphinInternalDirectory()
{
if (directoryState == null)
{ {
if (directoryState == null) throw new IllegalStateException("DirectoryInitializationService has to run at least once!");
{
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;
} }
else if (isDolphinDirectoryInitializationRunning.get())
public static String getDolphinInternalDirectory()
{ {
if (directoryState == null) throw new IllegalStateException(
{ "DirectoryInitializationService has to finish running first!");
throw new IllegalStateException("DirectoryInitializationService has to run at least once!");
}
else if (isDolphinDirectoryInitializationRunning.get())
{
throw new IllegalStateException("DirectoryInitializationService has to finish running first!");
}
return internalPath;
} }
return internalPath;
private void sendBroadcastState(DirectoryInitializationState state) }
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
{ {
Intent localIntent = if (!output.exists() || overwrite)
new Intent(BROADCAST_ACTION) {
.putExtra(EXTRA_STATE, state); InputStream in = getAssets().open(asset);
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); OutputStream out = new FileOutputStream(output);
copyFile(in, out);
in.close();
out.close();
}
} }
catch (IOException e)
private void copyAsset(String asset, File output, Boolean overwrite)
{ {
Log.verbose("[DirectoryInitializationService] Copying File " + asset + " to " + output); Log.error("[DirectoryInitializationService] Failed to copy asset file: " + asset +
e.getMessage());
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) private void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite)
{
Log.verbose("[DirectoryInitializationService] Copying Folder " + assetFolder + " to " +
outputFolder);
try
{ {
Log.verbose("[DirectoryInitializationService] Copying Folder " + assetFolder + " to " + outputFolder); boolean createdFolder = false;
for (String file : getAssets().list(assetFolder))
try {
if (!createdFolder)
{ {
boolean createdFolder = false; outputFolder.mkdir();
for (String file : getAssets().list(assetFolder)) createdFolder = true;
{
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());
} }
copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file),
overwrite);
copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite);
}
} }
catch (IOException e)
private void copyFile(InputStream in, OutputStream out) throws IOException
{ {
byte[] buffer = new byte[1024]; Log.error("[DirectoryInitializationService] Failed to copy asset folder: " + assetFolder +
int read; e.getMessage());
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
} }
}
private static native void CreateUserDirectories(); private void copyFile(InputStream in, OutputStream out) throws IOException
private static native void SetSysDirectory(String path); {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
}
private static native void CreateUserDirectories();
private static native void SetSysDirectory(String path);
} }

View File

@ -20,117 +20,117 @@ import java.util.concurrent.atomic.AtomicReference;
*/ */
public final class GameFileCacheService extends IntentService public final class GameFileCacheService extends IntentService
{ {
public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_UPDATED"; public static final String BROADCAST_ACTION = "org.dolphinemu.dolphinemu.GAME_FILE_CACHE_UPDATED";
private static final String ACTION_LOAD = "org.dolphinemu.dolphinemu.LOAD_GAME_FILE_CACHE"; private static final String ACTION_LOAD = "org.dolphinemu.dolphinemu.LOAD_GAME_FILE_CACHE";
private static final String ACTION_RESCAN = "org.dolphinemu.dolphinemu.RESCAN_GAME_FILE_CACHE"; private static final String ACTION_RESCAN = "org.dolphinemu.dolphinemu.RESCAN_GAME_FILE_CACHE";
private static GameFileCache gameFileCache = null; private static GameFileCache gameFileCache = null;
private static AtomicReference<GameFile[]> gameFiles = new AtomicReference<>(new GameFile[]{}); private static AtomicReference<GameFile[]> gameFiles = new AtomicReference<>(new GameFile[]{});
public GameFileCacheService() public GameFileCacheService()
{ {
// Superclass constructor is called to name the thread on which this service executes. // Superclass constructor is called to name the thread on which this service executes.
super("GameFileCacheService"); super("GameFileCacheService");
} }
public static List<GameFile> getGameFilesForPlatform(Platform platform) public static List<GameFile> getGameFilesForPlatform(Platform platform)
{ {
GameFile[] allGames = gameFiles.get(); GameFile[] allGames = gameFiles.get();
ArrayList<GameFile> platformGames = new ArrayList<>(); ArrayList<GameFile> platformGames = new ArrayList<>();
for (GameFile game : allGames) for (GameFile game : allGames)
{ {
if (Platform.fromNativeInt(game.getPlatform()) == platform) if (Platform.fromNativeInt(game.getPlatform()) == platform)
{ {
platformGames.add(game); platformGames.add(game);
} }
} }
return platformGames; return platformGames;
} }
public static GameFile getGameFileByGameId(String gameId) public static GameFile getGameFileByGameId(String gameId)
{ {
GameFile[] allGames = gameFiles.get(); GameFile[] allGames = gameFiles.get();
for (GameFile game : allGames) for (GameFile game : allGames)
{ {
if (game.getGameId().equals(gameId)) if (game.getGameId().equals(gameId))
{ {
return game; return game;
} }
} }
return null; return null;
} }
private static void startService(Context context, String action) private static void startService(Context context, String action)
{ {
Intent intent = new Intent(context, GameFileCacheService.class); Intent intent = new Intent(context, GameFileCacheService.class);
intent.setAction(action); intent.setAction(action);
context.startService(intent); context.startService(intent);
} }
/** /**
* Asynchronously loads the game file cache from disk without checking * Asynchronously loads the game file cache from disk without checking
* which games are present on the file system. * which games are present on the file system.
*/ */
public static void startLoad(Context context) public static void startLoad(Context context)
{ {
startService(context, ACTION_LOAD); startService(context, ACTION_LOAD);
} }
/** /**
* Asynchronously scans for games in the user's configured folders, * Asynchronously scans for games in the user's configured folders,
* updating the game file cache with the results. * updating the game file cache with the results.
* If startLoad hasn't been called before this, this has no effect. * If startLoad hasn't been called before this, this has no effect.
*/ */
public static void startRescan(Context context) public static void startRescan(Context context)
{ {
startService(context, ACTION_RESCAN); startService(context, ACTION_RESCAN);
} }
public static GameFile addOrGet(String gamePath) public static GameFile addOrGet(String gamePath)
{ {
// The existence of this one function, which is called from one // The existence of this one function, which is called from one
// single place, forces us to use synchronization in onHandleIntent... // single place, forces us to use synchronization in onHandleIntent...
// A bit annoying, but should be good enough for now // A bit annoying, but should be good enough for now
synchronized (gameFileCache) synchronized (gameFileCache)
{ {
return gameFileCache.addOrGet(gamePath); return gameFileCache.addOrGet(gamePath);
} }
} }
@Override @Override
protected void onHandleIntent(Intent intent) protected void onHandleIntent(Intent intent)
{ {
// Load the game list cache if it isn't already loaded, otherwise do nothing // Load the game list cache if it isn't already loaded, otherwise do nothing
if (ACTION_LOAD.equals(intent.getAction()) && gameFileCache == null) if (ACTION_LOAD.equals(intent.getAction()) && gameFileCache == null)
{ {
GameFileCache temp = new GameFileCache(getCacheDir() + File.separator + "gamelist.cache"); GameFileCache temp = new GameFileCache(getCacheDir() + File.separator + "gamelist.cache");
synchronized (temp) synchronized (temp)
{ {
gameFileCache = temp; gameFileCache = temp;
gameFileCache.load(); gameFileCache.load();
updateGameFileArray(); updateGameFileArray();
} }
} }
// Rescan the file system and update the game list cache with the results // Rescan the file system and update the game list cache with the results
if (ACTION_RESCAN.equals(intent.getAction()) && gameFileCache != null) if (ACTION_RESCAN.equals(intent.getAction()) && gameFileCache != null)
{ {
synchronized (gameFileCache) synchronized (gameFileCache)
{ {
if (gameFileCache.scanLibrary(this)) if (gameFileCache.scanLibrary(this))
{ {
updateGameFileArray(); updateGameFileArray();
} }
} }
} }
} }
private void updateGameFileArray() private void updateGameFileArray()
{ {
GameFile[] gameFilesTemp = gameFileCache.getAllGames(); GameFile[] gameFilesTemp = gameFileCache.getAllGames();
Arrays.sort(gameFilesTemp, (lhs, rhs) -> lhs.getTitle().compareToIgnoreCase(rhs.getTitle())); Arrays.sort(gameFilesTemp, (lhs, rhs) -> lhs.getTitle().compareToIgnoreCase(rhs.getTitle()));
gameFiles.set(gameFilesTemp); gameFiles.set(gameFilesTemp);
LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_ACTION)); LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_ACTION));
} }
} }

View File

@ -25,142 +25,143 @@ import java.util.List;
public class SyncChannelJobService extends JobService public class SyncChannelJobService extends JobService
{ {
private static final String TAG = "ChannelJobSvc"; private static final String TAG = "ChannelJobSvc";
private SyncChannelTask mSyncChannelTask; private SyncChannelTask mSyncChannelTask;
@Override @Override
public boolean onStartJob(final JobParameters jobParameters) public boolean onStartJob(final JobParameters jobParameters)
{ {
Log.d(TAG, "Starting channel creation job"); Log.d(TAG, "Starting channel creation job");
mSyncChannelTask = mSyncChannelTask =
new SyncChannelTask(getApplicationContext()) new SyncChannelTask(getApplicationContext())
{ {
@Override @Override
protected void onPostExecute(Boolean success) protected void onPostExecute(Boolean success)
{ {
super.onPostExecute(success); super.onPostExecute(success);
jobFinished(jobParameters, !success); jobFinished(jobParameters, !success);
} }
}; };
mSyncChannelTask.execute(); mSyncChannelTask.execute();
return true; return true;
} }
@Override @Override
public boolean onStopJob(JobParameters jobParameters) public boolean onStopJob(JobParameters jobParameters)
{ {
if (mSyncChannelTask != null) if (mSyncChannelTask != null)
{ {
mSyncChannelTask.cancel(true); mSyncChannelTask.cancel(true);
} }
return true; return true;
} }
private static class SyncChannelTask extends AsyncTask<Void, Void, Boolean> private static class SyncChannelTask extends AsyncTask<Void, Void, Boolean>
{ {
private Context context; private Context context;
SyncChannelTask(Context context) SyncChannelTask(Context context)
{ {
this.context = context; this.context = context;
} }
/** /**
* Setup channels * Setup channels
*/ */
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
@Override @Override
protected Boolean doInBackground(Void... voids) protected Boolean doInBackground(Void... voids)
{ {
List<HomeScreenChannel> subscriptions; List<HomeScreenChannel> subscriptions;
List<Channel> channels = TvUtil.getAllChannels(context); List<Channel> channels = TvUtil.getAllChannels(context);
List<Long> channelIds = new ArrayList<>(); List<Long> channelIds = new ArrayList<>();
// Checks if the default channels are added. // Checks if the default channels are added.
// If not, create the channels // If not, create the channels
if (!channels.isEmpty()) if (!channels.isEmpty())
{ {
channels.forEach(channel -> channelIds.add(channel.getId())); channels.forEach(channel -> channelIds.add(channel.getId()));
} }
else else
{ {
subscriptions = TvUtil.createUniversalSubscriptions(); subscriptions = TvUtil.createUniversalSubscriptions();
for (HomeScreenChannel subscription : subscriptions) for (HomeScreenChannel subscription : subscriptions)
{ {
long channelId = createChannel(subscription); long channelId = createChannel(subscription);
channelIds.add(channelId); channelIds.add(channelId);
subscription.setChannelId(channelId); subscription.setChannelId(channelId);
// Only the first channel added can be browsable without user intervention. // Only the first channel added can be browsable without user intervention.
TvContractCompat.requestChannelBrowsable(context, channelId); TvContractCompat.requestChannelBrowsable(context, channelId);
} }
} }
// Schedule triggers to update programs // Schedule triggers to update programs
channelIds.forEach(channel -> TvUtil.scheduleSyncingProgramsForChannel(context, channel)); channelIds.forEach(channel -> TvUtil.scheduleSyncingProgramsForChannel(context, channel));
// Update all channels // Update all channels
TvUtil.updateAllChannels(context); TvUtil.updateAllChannels(context);
return true; return true;
} }
private long createChannel(HomeScreenChannel subscription) private long createChannel(HomeScreenChannel subscription)
{ {
long channelId = getChannelIdFromTvProvider(context, subscription); long channelId = getChannelIdFromTvProvider(context, subscription);
if (channelId != -1L) if (channelId != -1L)
{ {
return channelId; return channelId;
} }
// Create the channel since it has not been added to the TV Provider. // Create the channel since it has not been added to the TV Provider.
Uri appLinkIntentUri = Uri.parse(subscription.getAppLinkIntentUri()); Uri appLinkIntentUri = Uri.parse(subscription.getAppLinkIntentUri());
Channel.Builder builder = new Channel.Builder(); Channel.Builder builder = new Channel.Builder();
builder.setType(TvContractCompat.Channels.TYPE_PREVIEW) builder.setType(TvContractCompat.Channels.TYPE_PREVIEW)
.setDisplayName(subscription.getName()) .setDisplayName(subscription.getName())
.setDescription(subscription.getDescription()) .setDescription(subscription.getDescription())
.setAppLinkIntentUri(appLinkIntentUri); .setAppLinkIntentUri(appLinkIntentUri);
Log.d(TAG, "Creating channel: " + subscription.getName()); Log.d(TAG, "Creating channel: " + subscription.getName());
Uri channelUrl = Uri channelUrl =
context.getContentResolver() context.getContentResolver()
.insert( .insert(
TvContractCompat.Channels.CONTENT_URI, TvContractCompat.Channels.CONTENT_URI,
builder.build().toContentValues()); builder.build().toContentValues());
channelId = ContentUris.parseId(channelUrl); channelId = ContentUris.parseId(channelUrl);
Bitmap bitmap = TvUtil.convertToBitmap(context, R.drawable.ic_launcher); Bitmap bitmap = TvUtil.convertToBitmap(context, R.drawable.ic_launcher);
ChannelLogoUtils.storeChannelLogo(context, channelId, bitmap); ChannelLogoUtils.storeChannelLogo(context, channelId, bitmap);
return channelId; return channelId;
} }
private long getChannelIdFromTvProvider(Context context, HomeScreenChannel subscription) private long getChannelIdFromTvProvider(Context context, HomeScreenChannel subscription)
{ {
Cursor cursor = Cursor cursor =
context.getContentResolver().query( context.getContentResolver().query(
TvContractCompat.Channels.CONTENT_URI, TvContractCompat.Channels.CONTENT_URI,
new String[]{ new String[]{
TvContractCompat.Channels._ID, TvContractCompat.Channels._ID,
TvContract.Channels.COLUMN_DISPLAY_NAME TvContract.Channels.COLUMN_DISPLAY_NAME
}, },
null, null,
null, null,
null); null);
if (cursor != null && cursor.moveToFirst()) if (cursor != null && cursor.moveToFirst())
{ {
do do
{ {
Channel channel = Channel.fromCursor(cursor); Channel channel = Channel.fromCursor(cursor);
if (subscription.getName().equals(channel.getDisplayName())) if (subscription.getName().equals(channel.getDisplayName()))
{ {
Log.d( Log.d(
TAG, TAG,
"Channel already exists. Returning channel " "Channel already exists. Returning channel "
+ channel.getId() + channel.getId()
+ " from TV Provider."); + " from TV Provider.");
return channel.getId(); return channel.getId();
} }
} while (cursor.moveToNext()); }
} while (cursor.moveToNext());
return -1L; }
} return -1L;
} }
}
} }

View File

@ -2,9 +2,7 @@ package org.dolphinemu.dolphinemu.services;
import android.app.job.JobParameters; import android.app.job.JobParameters;
import android.app.job.JobService; import android.app.job.JobService;
import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.PersistableBundle; import android.os.PersistableBundle;
@ -25,138 +23,138 @@ import java.util.List;
public class SyncProgramsJobService extends JobService public class SyncProgramsJobService extends JobService
{ {
private static final String TAG = "SyncProgramsJobService"; private static final String TAG = "SyncProgramsJobService";
private SyncProgramsTask mSyncProgramsTask; private SyncProgramsTask mSyncProgramsTask;
@Override @Override
public boolean onStartJob(final JobParameters jobParameters) public boolean onStartJob(final JobParameters jobParameters)
{ {
Log.d(TAG, "onStartJob(): " + jobParameters); Log.d(TAG, "onStartJob(): " + jobParameters);
final long channelId = getChannelId(jobParameters); final long channelId = getChannelId(jobParameters);
if (channelId == -1L) if (channelId == -1L)
{ {
Log.d(TAG, "Failed to find channel"); Log.d(TAG, "Failed to find channel");
return false; return false;
} }
mSyncProgramsTask = mSyncProgramsTask =
new SyncProgramsTask(getApplicationContext()) new SyncProgramsTask(getApplicationContext())
{ {
@Override @Override
protected void onPostExecute(Boolean finished) protected void onPostExecute(Boolean finished)
{ {
super.onPostExecute(finished); super.onPostExecute(finished);
mSyncProgramsTask = null; mSyncProgramsTask = null;
jobFinished(jobParameters, !finished); jobFinished(jobParameters, !finished);
} }
}; };
mSyncProgramsTask.execute(channelId); mSyncProgramsTask.execute(channelId);
return true; return true;
} }
@Override @Override
public boolean onStopJob(JobParameters jobParameters) public boolean onStopJob(JobParameters jobParameters)
{ {
if (mSyncProgramsTask != null) if (mSyncProgramsTask != null)
{ {
mSyncProgramsTask.cancel(true); mSyncProgramsTask.cancel(true);
} }
return true; return true;
} }
private long getChannelId(JobParameters jobParameters) private long getChannelId(JobParameters jobParameters)
{ {
PersistableBundle extras = jobParameters.getExtras(); PersistableBundle extras = jobParameters.getExtras();
return extras.getLong(TvContractCompat.EXTRA_CHANNEL_ID, -1L); return extras.getLong(TvContractCompat.EXTRA_CHANNEL_ID, -1L);
} }
private static class SyncProgramsTask extends AsyncTask<Long, Void, Boolean> private static class SyncProgramsTask extends AsyncTask<Long, Void, Boolean>
{ {
private Context context; private Context context;
private List<GameFile> updatePrograms; private List<GameFile> updatePrograms;
private SyncProgramsTask(Context context) private SyncProgramsTask(Context context)
{ {
this.context = context; this.context = context;
updatePrograms = new ArrayList<>(); updatePrograms = new ArrayList<>();
} }
/** /**
* Determines which channel to update, get the game files for the channel, * Determines which channel to update, get the game files for the channel,
* then updates the list * then updates the list
*/ */
@Override @Override
protected Boolean doInBackground(Long... channelIds) protected Boolean doInBackground(Long... channelIds)
{ {
List<Long> params = Arrays.asList(channelIds); List<Long> params = Arrays.asList(channelIds);
if (!params.isEmpty()) if (!params.isEmpty())
{ {
for (Long channelId : params) for (Long channelId : params)
{ {
Channel channel = TvUtil.getChannelById(context, channelId); Channel channel = TvUtil.getChannelById(context, channelId);
for (Platform platform : Platform.values()) for (Platform platform : Platform.values())
{ {
if (channel != null && channel.getDisplayName().equals(platform.getHeaderName())) if (channel != null && channel.getDisplayName().equals(platform.getHeaderName()))
{ {
getGamesByPlatform(platform); getGamesByPlatform(platform);
syncPrograms(channelId); syncPrograms(channelId);
} }
} }
} }
} }
return true; return true;
} }
private void getGamesByPlatform(Platform platform) private void getGamesByPlatform(Platform platform)
{ {
updatePrograms = GameFileCacheService.getGameFilesForPlatform(platform); updatePrograms = GameFileCacheService.getGameFilesForPlatform(platform);
} }
private void syncPrograms(long channelId) private void syncPrograms(long channelId)
{ {
Log.d(TAG, "Sync programs for channel: " + channelId); Log.d(TAG, "Sync programs for channel: " + channelId);
deletePrograms(channelId); deletePrograms(channelId);
createPrograms(channelId); createPrograms(channelId);
} }
private void createPrograms(long channelId) private void createPrograms(long channelId)
{ {
for (GameFile game : updatePrograms) for (GameFile game : updatePrograms)
{ {
PreviewProgram previewProgram = buildProgram(channelId, game); PreviewProgram previewProgram = buildProgram(channelId, game);
context.getContentResolver() context.getContentResolver()
.insert( .insert(
TvContractCompat.PreviewPrograms.CONTENT_URI, TvContractCompat.PreviewPrograms.CONTENT_URI,
previewProgram.toContentValues()); previewProgram.toContentValues());
} }
} }
private void deletePrograms(long channelId) private void deletePrograms(long channelId)
{ {
context.getContentResolver().delete( context.getContentResolver().delete(
TvContractCompat.buildPreviewProgramsUriForChannel(channelId), TvContractCompat.buildPreviewProgramsUriForChannel(channelId),
null, null,
null); null);
} }
private PreviewProgram buildProgram(long channelId, GameFile game) private PreviewProgram buildProgram(long channelId, GameFile game)
{ {
Uri appLinkUri = AppLinkHelper.buildGameUri(channelId, game.getGameId()); Uri appLinkUri = AppLinkHelper.buildGameUri(channelId, game.getGameId());
Uri banner = TvUtil.buildBanner(game, context); Uri banner = TvUtil.buildBanner(game, context);
if (banner == null) if (banner == null)
banner = TvUtil.getUriToResource(context, R.drawable.banner_tv); banner = TvUtil.getUriToResource(context, R.drawable.banner_tv);
PreviewProgram.Builder builder = new PreviewProgram.Builder(); PreviewProgram.Builder builder = new PreviewProgram.Builder();
builder.setChannelId(channelId) builder.setChannelId(channelId)
.setType(TvContractCompat.PreviewProgramColumns.TYPE_GAME) .setType(TvContractCompat.PreviewProgramColumns.TYPE_GAME)
.setTitle(game.getTitle()) .setTitle(game.getTitle())
.setDescription(game.getDescription()) .setDescription(game.getDescription())
.setPosterArtUri(banner) .setPosterArtUri(banner)
.setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3) .setPosterArtAspectRatio(TvContractCompat.PreviewPrograms.ASPECT_RATIO_2_3)
.setIntentUri(appLinkUri); .setIntentUri(appLinkUri);
return builder.build(); return builder.build();
} }
} }
} }

View File

@ -5,10 +5,15 @@ import android.content.Intent;
public final class USBPermService extends IntentService public final class USBPermService extends IntentService
{ {
public USBPermService() { super("USBPermService"); } public USBPermService()
{
super("USBPermService");
}
// Needed when extending IntentService. // Needed when extending IntentService.
// We don't care about the results of the intent handler for this. // We don't care about the results of the intent handler for this.
@Override @Override
protected void onHandleIntent(Intent intent) {} protected void onHandleIntent(Intent intent)
{
}
} }

View File

@ -17,141 +17,141 @@ import android.view.View;
public final class DividerItemDecoration extends RecyclerView.ItemDecoration public final class DividerItemDecoration extends RecyclerView.ItemDecoration
{ {
private Drawable mDivider; private Drawable mDivider;
private boolean mShowFirstDivider = false; private boolean mShowFirstDivider = false;
private boolean mShowLastDivider = false; private boolean mShowLastDivider = false;
public DividerItemDecoration(Context context, AttributeSet attrs) public DividerItemDecoration(Context context, AttributeSet attrs)
{ {
final TypedArray a = context final TypedArray a = context
.obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider}); .obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider});
mDivider = a.getDrawable(0); mDivider = a.getDrawable(0);
a.recycle(); a.recycle();
} }
public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider, public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider,
boolean showLastDivider) boolean showLastDivider)
{ {
this(context, attrs); this(context, attrs);
mShowFirstDivider = showFirstDivider; mShowFirstDivider = showFirstDivider;
mShowLastDivider = showLastDivider; mShowLastDivider = showLastDivider;
} }
public DividerItemDecoration(Drawable divider) public DividerItemDecoration(Drawable divider)
{ {
mDivider = divider; mDivider = divider;
} }
public DividerItemDecoration(Drawable divider, boolean showFirstDivider, public DividerItemDecoration(Drawable divider, boolean showFirstDivider,
boolean showLastDivider) boolean showLastDivider)
{ {
this(divider); this(divider);
mShowFirstDivider = showFirstDivider; mShowFirstDivider = showFirstDivider;
mShowLastDivider = showLastDivider; mShowLastDivider = showLastDivider;
} }
@Override @Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) RecyclerView.State state)
{ {
super.getItemOffsets(outRect, view, parent, state); super.getItemOffsets(outRect, view, parent, state);
if (mDivider == null) if (mDivider == null)
{ {
return; return;
} }
if (parent.getChildPosition(view) < 1) if (parent.getChildPosition(view) < 1)
{ {
return; return;
} }
if (getOrientation(parent) == LinearLayoutManager.VERTICAL) if (getOrientation(parent) == LinearLayoutManager.VERTICAL)
{ {
outRect.top = mDivider.getIntrinsicHeight(); outRect.top = mDivider.getIntrinsicHeight();
} }
else else
{ {
outRect.left = mDivider.getIntrinsicWidth(); outRect.left = mDivider.getIntrinsicWidth();
} }
} }
@Override @Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)
{ {
if (mDivider == null) if (mDivider == null)
{ {
super.onDrawOver(c, parent, state); super.onDrawOver(c, parent, state);
return; return;
} }
// Initialization needed to avoid compiler warning // Initialization needed to avoid compiler warning
int left = 0, right = 0, top = 0, bottom = 0, size; int left = 0, right = 0, top = 0, bottom = 0, size;
int orientation = getOrientation(parent); int orientation = getOrientation(parent);
int childCount = parent.getChildCount(); int childCount = parent.getChildCount();
if (orientation == LinearLayoutManager.VERTICAL) if (orientation == LinearLayoutManager.VERTICAL)
{ {
size = mDivider.getIntrinsicHeight(); size = mDivider.getIntrinsicHeight();
left = parent.getPaddingLeft(); left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight(); right = parent.getWidth() - parent.getPaddingRight();
} }
else else
{ //horizontal { //horizontal
size = mDivider.getIntrinsicWidth(); size = mDivider.getIntrinsicWidth();
top = parent.getPaddingTop(); top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom(); bottom = parent.getHeight() - parent.getPaddingBottom();
} }
for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++)
{ {
View child = parent.getChildAt(i); View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
if (orientation == LinearLayoutManager.VERTICAL) if (orientation == LinearLayoutManager.VERTICAL)
{ {
top = child.getTop() - params.topMargin; top = child.getTop() - params.topMargin;
bottom = top + size; bottom = top + size;
} }
else else
{ //horizontal { //horizontal
left = child.getLeft() - params.leftMargin; left = child.getLeft() - params.leftMargin;
right = left + size; right = left + size;
} }
mDivider.setBounds(left, top, right, bottom); mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c); mDivider.draw(c);
} }
// show last divider // show last divider
if (mShowLastDivider && childCount > 0) if (mShowLastDivider && childCount > 0)
{ {
View child = parent.getChildAt(childCount - 1); View child = parent.getChildAt(childCount - 1);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
if (orientation == LinearLayoutManager.VERTICAL) if (orientation == LinearLayoutManager.VERTICAL)
{ {
top = child.getBottom() + params.bottomMargin; top = child.getBottom() + params.bottomMargin;
bottom = top + size; bottom = top + size;
} }
else else
{ // horizontal { // horizontal
left = child.getRight() + params.rightMargin; left = child.getRight() + params.rightMargin;
right = left + size; right = left + size;
} }
mDivider.setBounds(left, top, right, bottom); mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c); mDivider.draw(c);
} }
} }
private int getOrientation(RecyclerView parent) private int getOrientation(RecyclerView parent)
{ {
if (parent.getLayoutManager() instanceof LinearLayoutManager) if (parent.getLayoutManager() instanceof LinearLayoutManager)
{ {
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager(); LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
return layoutManager.getOrientation(); return layoutManager.getOrientation();
} }
else else
{ {
throw new IllegalStateException( throw new IllegalStateException(
"DividerItemDecoration can only be used with a LinearLayoutManager."); "DividerItemDecoration can only be used with a LinearLayoutManager.");
} }
} }
} }

View File

@ -15,11 +15,11 @@ import android.view.View;
*/ */
public final class NVidiaShieldWorkaroundView extends View public final class NVidiaShieldWorkaroundView extends View
{ {
public NVidiaShieldWorkaroundView(Context context, AttributeSet attrs) public NVidiaShieldWorkaroundView(Context context, AttributeSet attrs)
{ {
super(context, attrs); super(context, attrs);
// Setting this seems to workaround the bug // Setting this seems to workaround the bug
setWillNotDraw(false); setWillNotDraw(false);
} }
} }

View File

@ -11,70 +11,72 @@ import android.widget.TextView;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
public class CustomTitleView extends LinearLayout implements TitleViewAdapter.Provider { public class CustomTitleView extends LinearLayout implements TitleViewAdapter.Provider
private final TextView mTitleView; {
private final View mBadgeView; private final TextView mTitleView;
private final View mBadgeView;
private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter() { private final TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter()
@Override {
public View getSearchAffordanceView() @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); return 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 = (TextView) 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 @Override
public TitleViewAdapter getTitleViewAdapter() public void setTitle(CharSequence titleText)
{ {
return mTitleViewAdapter; 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 = (TextView) 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;
}
} }

View File

@ -17,12 +17,12 @@ import android.widget.Toast;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter; import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView; import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler; import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler;
@ -33,206 +33,211 @@ import org.dolphinemu.dolphinemu.utils.StartupHandler;
*/ */
public final class MainActivity extends AppCompatActivity implements MainView public final class MainActivity extends AppCompatActivity implements MainView
{ {
private ViewPager mViewPager; private ViewPager mViewPager;
private Toolbar mToolbar; private Toolbar mToolbar;
private TabLayout mTabLayout; private TabLayout mTabLayout;
private FloatingActionButton mFab; private FloatingActionButton mFab;
private MainPresenter mPresenter = new MainPresenter(this, this); private MainPresenter mPresenter = new MainPresenter(this, this);
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
findViews(); findViews();
setSupportActionBar(mToolbar); setSupportActionBar(mToolbar);
mTabLayout.setupWithViewPager(mViewPager); mTabLayout.setupWithViewPager(mViewPager);
// Set up the FAB. // Set up the FAB.
mFab.setOnClickListener(view -> mPresenter.onFabClick()); 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) // Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
if (savedInstanceState == null) if (savedInstanceState == null)
StartupHandler.HandleInit(this); StartupHandler.HandleInit(this);
if (PermissionsHandler.hasWriteAccess(this)) if (PermissionsHandler.hasWriteAccess(this))
{ {
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter( PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
getSupportFragmentManager(), this); getSupportFragmentManager(), this);
mViewPager.setAdapter(platformPagerAdapter); mViewPager.setAdapter(platformPagerAdapter);
mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount()); mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount());
showGames(); showGames();
GameFileCacheService.startLoad(this); GameFileCacheService.startLoad(this);
} }
else else
{ {
mViewPager.setVisibility(View.INVISIBLE); mViewPager.setVisibility(View.INVISIBLE);
} }
} }
@Override @Override
protected void onResume() protected void onResume()
{ {
super.onResume(); super.onResume();
mPresenter.addDirIfNeeded(this); mPresenter.addDirIfNeeded(this);
} }
@Override @Override
protected void onDestroy() protected void onDestroy()
{ {
super.onDestroy(); super.onDestroy();
mPresenter.onDestroy(); mPresenter.onDestroy();
} }
@Override @Override
protected void onStart() protected void onStart()
{ {
super.onStart(); super.onStart();
StartupHandler.checkSessionReset(this); StartupHandler.checkSessionReset(this);
} }
@Override @Override
protected void onStop() protected void onStop()
{ {
super.onStop(); super.onStop();
StartupHandler.setSessionTime(this); StartupHandler.setSessionTime(this);
} }
// TODO: Replace with a ButterKnife injection. // TODO: Replace with a ButterKnife injection.
private void findViews() private void findViews()
{ {
mToolbar = (Toolbar) findViewById(R.id.toolbar_main); mToolbar = (Toolbar) findViewById(R.id.toolbar_main);
mViewPager = (ViewPager) findViewById(R.id.pager_platforms); mViewPager = (ViewPager) findViewById(R.id.pager_platforms);
mTabLayout = (TabLayout) findViewById(R.id.tabs_platforms); mTabLayout = (TabLayout) findViewById(R.id.tabs_platforms);
mFab = (FloatingActionButton) findViewById(R.id.button_add_directory); mFab = (FloatingActionButton) findViewById(R.id.button_add_directory);
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) public boolean onCreateOptionsMenu(Menu menu)
{ {
MenuInflater inflater = getMenuInflater(); MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_game_grid, menu); inflater.inflate(R.menu.menu_game_grid, menu);
return true; return true;
} }
/** /**
* MainView * MainView
*/ */
@Override @Override
public void setVersionString(String version) public void setVersionString(String version)
{ {
mToolbar.setSubtitle(version); mToolbar.setSubtitle(version);
} }
@Override @Override
public void refreshFragmentScreenshot(int fragmentPosition) public void refreshFragmentScreenshot(int fragmentPosition)
{ {
// Invalidate Picasso image so that the new screenshot is animated in. // Invalidate Picasso image so that the new screenshot is animated in.
Platform platform = Platform.fromPosition(mViewPager.getCurrentItem()); Platform platform = Platform.fromPosition(mViewPager.getCurrentItem());
PlatformGamesView fragment = getPlatformGamesView(platform); PlatformGamesView fragment = getPlatformGamesView(platform);
if (fragment != null) if (fragment != null)
{ {
fragment.refreshScreenshotAtPosition(fragmentPosition); fragment.refreshScreenshotAtPosition(fragmentPosition);
} }
} }
@Override @Override
public void launchSettingsActivity(MenuTag menuTag) public void launchSettingsActivity(MenuTag menuTag)
{ {
SettingsActivity.launch(this, menuTag, ""); SettingsActivity.launch(this, menuTag, "");
} }
@Override @Override
public void launchFileListActivity() public void launchFileListActivity()
{ {
FileBrowserHelper.openDirectoryPicker(this); FileBrowserHelper.openDirectoryPicker(this);
} }
/** /**
* @param requestCode An int describing whether the Activity that is returning did so successfully. * @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 resultCode An int describing what Activity is giving us this callback.
* @param result The information the returning Activity is providing us. * @param result The information the returning Activity is providing us.
*/ */
@Override @Override
protected void onActivityResult(int requestCode, int resultCode, Intent result) protected void onActivityResult(int requestCode, int resultCode, Intent result)
{ {
switch (requestCode) switch (requestCode)
{ {
case MainPresenter.REQUEST_ADD_DIRECTORY: case MainPresenter.REQUEST_ADD_DIRECTORY:
// If the user picked a file, as opposed to just backing out. // If the user picked a file, as opposed to just backing out.
if (resultCode == MainActivity.RESULT_OK) if (resultCode == MainActivity.RESULT_OK)
{ {
mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result));
} }
break; break;
case MainPresenter.REQUEST_EMULATE_GAME: case MainPresenter.REQUEST_EMULATE_GAME:
mPresenter.refreshFragmentScreenshot(resultCode); mPresenter.refreshFragmentScreenshot(resultCode);
break; break;
} }
} }
@Override @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
switch (requestCode) { {
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: switch (requestCode)
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { {
DirectoryInitializationService.startService(this); case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter( if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
getSupportFragmentManager(), this); {
mViewPager.setAdapter(platformPagerAdapter); DirectoryInitializationService.startService(this);
mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount()); PlatformPagerAdapter platformPagerAdapter = new PlatformPagerAdapter(
mTabLayout.setupWithViewPager(mViewPager); getSupportFragmentManager(), this);
mViewPager.setVisibility(View.VISIBLE); mViewPager.setAdapter(platformPagerAdapter);
GameFileCacheService.startLoad(this); mViewPager.setOffscreenPageLimit(platformPagerAdapter.getCount());
} else { mTabLayout.setupWithViewPager(mViewPager);
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT) mViewPager.setVisibility(View.VISIBLE);
.show(); GameFileCacheService.startLoad(this);
} }
break; else
default: {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
break; .show();
} }
} break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
/** /**
* Called by the framework whenever any actionbar/toolbar icon is clicked. * Called by the framework whenever any actionbar/toolbar icon is clicked.
* *
* @param item The icon that was clicked on. * @param item The icon that was clicked on.
* @return True if the event was handled, false to bubble it up to the OS. * @return True if the event was handled, false to bubble it up to the OS.
*/ */
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) public boolean onOptionsItemSelected(MenuItem item)
{ {
return mPresenter.handleOptionSelection(item.getItemId(), this); return mPresenter.handleOptionSelection(item.getItemId(), this);
} }
public void showGames() public void showGames()
{ {
for (Platform platform : Platform.values()) for (Platform platform : Platform.values())
{ {
PlatformGamesView fragment = getPlatformGamesView(platform); PlatformGamesView fragment = getPlatformGamesView(platform);
if (fragment != null) if (fragment != null)
{ {
fragment.showGames(); fragment.showGames();
} }
} }
} }
@Nullable @Nullable
private PlatformGamesView getPlatformGamesView(Platform platform) private PlatformGamesView getPlatformGamesView(Platform platform)
{ {
String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform.toInt(); String fragmentTag = "android:switcher:" + mViewPager.getId() + ":" + platform.toInt();
return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag); return (PlatformGamesView) getSupportFragmentManager().findFragmentByTag(fragmentTag);
} }
} }

View File

@ -8,106 +8,106 @@ import android.support.v4.content.LocalBroadcastManager;
import org.dolphinemu.dolphinemu.BuildConfig; import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.model.GameFileCache; import org.dolphinemu.dolphinemu.model.GameFileCache;
import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
public final class MainPresenter public final class MainPresenter
{ {
public static final int REQUEST_ADD_DIRECTORY = 1; public static final int REQUEST_ADD_DIRECTORY = 1;
public static final int REQUEST_EMULATE_GAME = 2; public static final int REQUEST_EMULATE_GAME = 2;
private final MainView mView; private final MainView mView;
private final Context mContext; private final Context mContext;
private BroadcastReceiver mBroadcastReceiver = null; private BroadcastReceiver mBroadcastReceiver = null;
private String mDirToAdd; private String mDirToAdd;
public MainPresenter(MainView view, Context context) public MainPresenter(MainView view, Context context)
{ {
mView = view; mView = view;
mContext = context; mContext = context;
} }
public void onCreate() public void onCreate()
{ {
String versionName = BuildConfig.VERSION_NAME; String versionName = BuildConfig.VERSION_NAME;
mView.setVersionString(versionName); mView.setVersionString(versionName);
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
filter.addAction(GameFileCacheService.BROADCAST_ACTION); filter.addAction(GameFileCacheService.BROADCAST_ACTION);
mBroadcastReceiver = new BroadcastReceiver() mBroadcastReceiver = new BroadcastReceiver()
{ {
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(Context context, Intent intent)
{ {
mView.showGames(); mView.showGames();
} }
}; };
LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter); LocalBroadcastManager.getInstance(mContext).registerReceiver(mBroadcastReceiver, filter);
} }
public void onDestroy() public void onDestroy()
{ {
if (mBroadcastReceiver != null) if (mBroadcastReceiver != null)
{ {
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mBroadcastReceiver); LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mBroadcastReceiver);
} }
} }
public void onFabClick() public void onFabClick()
{ {
mView.launchFileListActivity(); mView.launchFileListActivity();
} }
public boolean handleOptionSelection(int itemId, Context context) public boolean handleOptionSelection(int itemId, Context context)
{ {
switch (itemId) switch (itemId)
{ {
case R.id.menu_settings_core: case R.id.menu_settings_core:
mView.launchSettingsActivity(MenuTag.CONFIG); mView.launchSettingsActivity(MenuTag.CONFIG);
return true; return true;
case R.id.menu_settings_graphics: case R.id.menu_settings_graphics:
mView.launchSettingsActivity(MenuTag.GRAPHICS); mView.launchSettingsActivity(MenuTag.GRAPHICS);
return true; return true;
case R.id.menu_settings_gcpad: case R.id.menu_settings_gcpad:
mView.launchSettingsActivity(MenuTag.GCPAD_TYPE); mView.launchSettingsActivity(MenuTag.GCPAD_TYPE);
return true; return true;
case R.id.menu_settings_wiimote: case R.id.menu_settings_wiimote:
mView.launchSettingsActivity(MenuTag.WIIMOTE); mView.launchSettingsActivity(MenuTag.WIIMOTE);
return true; return true;
case R.id.menu_refresh: case R.id.menu_refresh:
GameFileCacheService.startRescan(context); GameFileCacheService.startRescan(context);
return true; return true;
case R.id.button_add_directory: case R.id.button_add_directory:
mView.launchFileListActivity(); mView.launchFileListActivity();
return true; return true;
} }
return false; return false;
} }
public void addDirIfNeeded(Context context) public void addDirIfNeeded(Context context)
{ {
if (mDirToAdd != null) if (mDirToAdd != null)
{ {
GameFileCache.addGameFolder(mDirToAdd, context); GameFileCache.addGameFolder(mDirToAdd, context);
mDirToAdd = null; mDirToAdd = null;
GameFileCacheService.startRescan(context); GameFileCacheService.startRescan(context);
} }
} }
public void onDirectorySelected(String dir) public void onDirectorySelected(String dir)
{ {
mDirToAdd = dir; mDirToAdd = dir;
} }
public void refreshFragmentScreenshot(int resultCode) public void refreshFragmentScreenshot(int resultCode)
{ {
mView.refreshFragmentScreenshot(resultCode); mView.refreshFragmentScreenshot(resultCode);
} }
} }

View File

@ -9,29 +9,29 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
*/ */
public interface MainView public interface MainView
{ {
/** /**
* Pass the view the native library's version string. Displaying * Pass the view the native library's version string. Displaying
* it is optional. * it is optional.
* *
* @param version A string pulled from native code. * @param version A string pulled from native code.
*/ */
void setVersionString(String version); void setVersionString(String version);
/** /**
* Tell the view to tell the currently displayed {@link android.support.v4.app.Fragment} * 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. * 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. * @param fragmentPosition An index corresponding to the list or grid of games.
*/ */
void refreshFragmentScreenshot(int fragmentPosition); void refreshFragmentScreenshot(int fragmentPosition);
void launchSettingsActivity(MenuTag menuTag); void launchSettingsActivity(MenuTag menuTag);
void launchFileListActivity(); void launchFileListActivity();
/** /**
* To be called when the game file cache is updated. * To be called when the game file cache is updated.
*/ */
void showGames(); void showGames();
} }

View File

@ -18,13 +18,13 @@ import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.EmulationActivity; import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter; import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter; import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.model.TvSettingsItem; import org.dolphinemu.dolphinemu.model.TvSettingsItem;
import org.dolphinemu.dolphinemu.services.DirectoryInitializationService; import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper; import org.dolphinemu.dolphinemu.utils.FileBrowserHelper;
import org.dolphinemu.dolphinemu.utils.PermissionsHandler; import org.dolphinemu.dolphinemu.utils.PermissionsHandler;
import org.dolphinemu.dolphinemu.utils.StartupHandler; import org.dolphinemu.dolphinemu.utils.StartupHandler;
@ -35,249 +35,257 @@ import java.util.Collection;
public final class TvMainActivity extends FragmentActivity implements MainView public final class TvMainActivity extends FragmentActivity implements MainView
{ {
private MainPresenter mPresenter = new MainPresenter(this, this); private MainPresenter mPresenter = new MainPresenter(this, this);
private BrowseSupportFragment mBrowseFragment; private BrowseSupportFragment mBrowseFragment;
private ArrayObjectAdapter mRowsAdapter; private ArrayObjectAdapter mRowsAdapter;
@Override @Override
protected void onCreate(Bundle savedInstanceState) protected void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tv_main); 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) // Stuff in this block only happens when this activity is newly created (i.e. not a rotation)
if (savedInstanceState == null) if (savedInstanceState == null)
{ {
StartupHandler.HandleInit(this); StartupHandler.HandleInit(this);
} }
// Setup and/or sync channels // Setup and/or sync channels
TvUtil.scheduleSyncingChannel(getApplicationContext()); TvUtil.scheduleSyncingChannel(getApplicationContext());
} }
@Override @Override
protected void onResume() protected void onResume()
{ {
super.onResume(); super.onResume();
mPresenter.addDirIfNeeded(this); mPresenter.addDirIfNeeded(this);
} }
@Override @Override
protected void onDestroy() protected void onDestroy()
{ {
super.onDestroy(); super.onDestroy();
mPresenter.onDestroy(); mPresenter.onDestroy();
} }
@Override @Override
protected void onStart() protected void onStart()
{ {
super.onStart(); super.onStart();
StartupHandler.checkSessionReset(this); StartupHandler.checkSessionReset(this);
} }
@Override @Override
protected void onStop() protected void onStop()
{ {
super.onStop(); super.onStop();
StartupHandler.setSessionTime(this); StartupHandler.setSessionTime(this);
} }
void setupUI() { void setupUI()
final FragmentManager fragmentManager = getSupportFragmentManager(); {
mBrowseFragment = new BrowseSupportFragment(); final FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager mBrowseFragment = new BrowseSupportFragment();
.beginTransaction() fragmentManager
.add(R.id.content, mBrowseFragment, "BrowseFragment") .beginTransaction()
.commit(); .add(R.id.content, mBrowseFragment, "BrowseFragment")
.commit();
// Set display parameters for the BrowseFragment // Set display parameters for the BrowseFragment
mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED); mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED);
mBrowseFragment.setBrandColor(ContextCompat.getColor(this, R.color.dolphin_blue_dark)); mBrowseFragment.setBrandColor(ContextCompat.getColor(this, R.color.dolphin_blue_dark));
buildRowsAdapter(); buildRowsAdapter();
mBrowseFragment.setOnItemViewClickedListener( mBrowseFragment.setOnItemViewClickedListener(
(itemViewHolder, item, rowViewHolder, row) -> (itemViewHolder, item, rowViewHolder, row) ->
{ {
// Special case: user clicked on a settings row item. // Special case: user clicked on a settings row item.
if (item instanceof TvSettingsItem) if (item instanceof TvSettingsItem)
{ {
TvSettingsItem settingsItem = (TvSettingsItem) item; TvSettingsItem settingsItem = (TvSettingsItem) item;
mPresenter.handleOptionSelection(settingsItem.getItemId(), this); mPresenter.handleOptionSelection(settingsItem.getItemId(), this);
} }
else else
{ {
TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder; TvGameViewHolder holder = (TvGameViewHolder) itemViewHolder;
// Start the emulation activity and send the path of the clicked ISO to it. // Start the emulation activity and send the path of the clicked ISO to it.
EmulationActivity.launch(TvMainActivity.this, EmulationActivity.launch(TvMainActivity.this,
holder.gameFile, holder.gameFile,
-1, -1,
holder.imageScreenshot); holder.imageScreenshot);
} }
}); });
} }
/**
* MainView
*/
@Override /**
public void setVersionString(String version) * MainView
{ */
mBrowseFragment.setTitle(version);
}
@Override @Override
public void refreshFragmentScreenshot(int fragmentPosition) public void setVersionString(String version)
{ {
mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size()); mBrowseFragment.setTitle(version);
} }
@Override @Override
public void launchSettingsActivity(MenuTag menuTag) public void refreshFragmentScreenshot(int fragmentPosition)
{ {
SettingsActivity.launch(this, menuTag, ""); mRowsAdapter.notifyArrayItemRangeChanged(0, mRowsAdapter.size());
} }
@Override @Override
public void launchFileListActivity() public void launchSettingsActivity(MenuTag menuTag)
{ {
FileBrowserHelper.openDirectoryPicker(this); SettingsActivity.launch(this, menuTag, "");
} }
@Override @Override
public void showGames() public void launchFileListActivity()
{ {
// Kicks off the program services to update all channels FileBrowserHelper.openDirectoryPicker(this);
TvUtil.updateAllChannels(getApplicationContext()); }
recreate(); @Override
} public void showGames()
{
// Kicks off the program services to update all channels
TvUtil.updateAllChannels(getApplicationContext());
/** recreate();
* 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); * Callback from AddDirectoryActivity. Applies any changes necessary to the GameGridActivity.
break; *
} * @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;
@Override case MainPresenter.REQUEST_EMULATE_GAME:
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { mPresenter.refreshFragmentScreenshot(resultCode);
switch (requestCode) { break;
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION: }
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { }
DirectoryInitializationService.startService(this);
GameFileCacheService.startLoad(this);
} else {
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
private void buildRowsAdapter() @Override
{ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter()); {
switch (requestCode)
{
case PermissionsHandler.REQUEST_CODE_WRITE_PERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED)
{
DirectoryInitializationService.startService(this);
GameFileCacheService.startLoad(this);
}
else
{
Toast.makeText(this, R.string.write_permission_needed, Toast.LENGTH_SHORT)
.show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
if (PermissionsHandler.hasWriteAccess(this)) private void buildRowsAdapter()
{ {
GameFileCacheService.startLoad(this); mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
}
for (Platform platform : Platform.values()) if (PermissionsHandler.hasWriteAccess(this))
{ {
ListRow row = buildGamesRow(platform, GameFileCacheService.getGameFilesForPlatform(platform)); GameFileCacheService.startLoad(this);
}
// Add row to the adapter only if it is not empty. for (Platform platform : Platform.values())
if (row != null) {
{ ListRow row = buildGamesRow(platform, GameFileCacheService.getGameFilesForPlatform(platform));
mRowsAdapter.add(row);
}
}
mRowsAdapter.add(buildSettingsRow()); // Add row to the adapter only if it is not empty.
if (row != null)
{
mRowsAdapter.add(row);
}
}
mBrowseFragment.setAdapter(mRowsAdapter); mRowsAdapter.add(buildSettingsRow());
}
private ListRow buildGamesRow(Platform platform, Collection<GameFile> gameFiles) mBrowseFragment.setAdapter(mRowsAdapter);
{ }
// If there are no games, don't return a Row.
if (gameFiles.size() == 0)
{
return null;
}
// Create an adapter for this row. private ListRow buildGamesRow(Platform platform, Collection<GameFile> gameFiles)
ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter()); {
row.addAll(0, gameFiles); // If there are no games, don't return a Row.
if (gameFiles.size() == 0)
{
return null;
}
// Create a header for this row. // Create an adapter for this row.
HeaderItem header = new HeaderItem(platform.toInt(), platform.getHeaderName()); ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter());
row.addAll(0, gameFiles);
// Create the row, passing it the filled adapter and the header, and give it to the master adapter. // Create a header for this row.
return new ListRow(header, row); HeaderItem header = new HeaderItem(platform.toInt(), platform.getHeaderName());
}
private ListRow buildSettingsRow() // Create the row, passing it the filled adapter and the header, and give it to the master adapter.
{ return new ListRow(header, row);
ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter()); }
rowItems.add(new TvSettingsItem(R.id.menu_settings_core, private ListRow buildSettingsRow()
R.drawable.ic_settings_core_tv, {
R.string.grid_menu_config)); ArrayObjectAdapter rowItems = new ArrayObjectAdapter(new SettingsRowPresenter());
rowItems.add(new TvSettingsItem(R.id.menu_settings_graphics, rowItems.add(new TvSettingsItem(R.id.menu_settings_core,
R.drawable.ic_settings_graphics_tv, R.drawable.ic_settings_core_tv,
R.string.grid_menu_graphics_settings)); R.string.grid_menu_config));
rowItems.add(new TvSettingsItem(R.id.menu_settings_gcpad, rowItems.add(new TvSettingsItem(R.id.menu_settings_graphics,
R.drawable.ic_settings_gcpad, R.drawable.ic_settings_graphics_tv,
R.string.grid_menu_gcpad_settings)); R.string.grid_menu_graphics_settings));
rowItems.add(new TvSettingsItem(R.id.menu_settings_wiimote, rowItems.add(new TvSettingsItem(R.id.menu_settings_gcpad,
R.drawable.ic_settings_wiimote, R.drawable.ic_settings_gcpad,
R.string.grid_menu_wiimote_settings)); R.string.grid_menu_gcpad_settings));
rowItems.add(new TvSettingsItem(R.id.button_add_directory, rowItems.add(new TvSettingsItem(R.id.menu_settings_wiimote,
R.drawable.ic_add_tv, R.drawable.ic_settings_wiimote,
R.string.add_directory_title)); R.string.grid_menu_wiimote_settings));
rowItems.add(new TvSettingsItem(R.id.menu_refresh, rowItems.add(new TvSettingsItem(R.id.button_add_directory,
R.drawable.ic_refresh_tv, R.drawable.ic_add_tv,
R.string.grid_menu_refresh)); R.string.add_directory_title));
// Create a header for this row. rowItems.add(new TvSettingsItem(R.id.menu_refresh,
HeaderItem header = new HeaderItem(R.string.preferences_settings, getString(R.string.preferences_settings)); R.drawable.ic_refresh_tv,
R.string.grid_menu_refresh));
return new ListRow(header, rowItems); // Create a header for this row.
} HeaderItem header =
new HeaderItem(R.string.preferences_settings, getString(R.string.preferences_settings));
return new ListRow(header, rowItems);
}
} }

View File

@ -1,45 +1,47 @@
package org.dolphinemu.dolphinemu.ui.platform; package org.dolphinemu.dolphinemu.ui.platform;
/** Enum to represent platform (eg GameCube, Wii). */ /**
* Enum to represent platform (eg GameCube, Wii).
*/
public enum Platform public enum Platform
{ {
GAMECUBE(0, "GameCube Games"), GAMECUBE(0, "GameCube Games"),
WII(1, "Wii Games"), WII(1, "Wii Games"),
WIIWARE(2, "WiiWare Games"); WIIWARE(2, "WiiWare Games");
private final int value; private final int value;
private final String headerName; private final String headerName;
Platform(int value, String headerName) Platform(int value, String headerName)
{ {
this.value = value; this.value = value;
this.headerName = headerName; this.headerName = headerName;
} }
public static Platform fromInt(int i) public static Platform fromInt(int i)
{ {
return values()[i]; return values()[i];
} }
public static Platform fromNativeInt(int i) public static Platform fromNativeInt(int i)
{ {
// TODO: Proper support for DOL and ELF files // TODO: Proper support for DOL and ELF files
boolean in_range = i >= 0 && i < values().length; boolean in_range = i >= 0 && i < values().length;
return values()[in_range ? i : WIIWARE.value]; return values()[in_range ? i : WIIWARE.value];
} }
public static Platform fromPosition(int position) public static Platform fromPosition(int position)
{ {
return values()[position]; return values()[position];
} }
public int toInt() public int toInt()
{ {
return value; return value;
} }
public String getHeaderName() public String getHeaderName()
{ {
return headerName; return headerName;
} }
} }

View File

@ -11,85 +11,82 @@ import android.view.ViewGroup;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.adapters.GameAdapter; import org.dolphinemu.dolphinemu.adapters.GameAdapter;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.GameFileCacheService; import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import java.util.List;
public final class PlatformGamesFragment extends Fragment implements PlatformGamesView public final class PlatformGamesFragment extends Fragment implements PlatformGamesView
{ {
private static final String ARG_PLATFORM = "platform"; private static final String ARG_PLATFORM = "platform";
private GameAdapter mAdapter; private GameAdapter mAdapter;
private RecyclerView mRecyclerView; private RecyclerView mRecyclerView;
public static PlatformGamesFragment newInstance(Platform platform) public static PlatformGamesFragment newInstance(Platform platform)
{ {
PlatformGamesFragment fragment = new PlatformGamesFragment(); PlatformGamesFragment fragment = new PlatformGamesFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putSerializable(ARG_PLATFORM, platform); args.putSerializable(ARG_PLATFORM, platform);
fragment.setArguments(args); fragment.setArguments(args);
return fragment; return fragment;
} }
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
{ {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{ {
View rootView = inflater.inflate(R.layout.fragment_grid, container, false); View rootView = inflater.inflate(R.layout.fragment_grid, container, false);
findViews(rootView); findViews(rootView);
return rootView; return rootView;
} }
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState) public void onViewCreated(View view, Bundle savedInstanceState)
{ {
int columns = getResources().getInteger(R.integer.game_grid_columns); int columns = getResources().getInteger(R.integer.game_grid_columns);
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns); RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), columns);
mAdapter = new GameAdapter(); mAdapter = new GameAdapter();
mRecyclerView.setLayoutManager(layoutManager); mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setAdapter(mAdapter); mRecyclerView.setAdapter(mAdapter);
mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8)); mRecyclerView.addItemDecoration(new GameAdapter.SpacesItemDecoration(8));
showGames(); showGames();
} }
@Override @Override
public void refreshScreenshotAtPosition(int position) public void refreshScreenshotAtPosition(int position)
{ {
mAdapter.notifyItemChanged(position); mAdapter.notifyItemChanged(position);
} }
@Override @Override
public void onItemClick(String gameId) public void onItemClick(String gameId)
{ {
// No-op for now // No-op for now
} }
@Override @Override
public void showGames() public void showGames()
{ {
if (mAdapter != null) if (mAdapter != null)
{ {
Platform platform = (Platform) getArguments().getSerializable(ARG_PLATFORM); Platform platform = (Platform) getArguments().getSerializable(ARG_PLATFORM);
mAdapter.swapDataSet(GameFileCacheService.getGameFilesForPlatform(platform)); mAdapter.swapDataSet(GameFileCacheService.getGameFilesForPlatform(platform));
} }
} }
private void findViews(View root) private void findViews(View root)
{ {
mRecyclerView = (RecyclerView) root.findViewById(R.id.grid_games); mRecyclerView = (RecyclerView) root.findViewById(R.id.grid_games);
} }
} }

View File

@ -1,32 +1,28 @@
package org.dolphinemu.dolphinemu.ui.platform; package org.dolphinemu.dolphinemu.ui.platform;
import org.dolphinemu.dolphinemu.model.GameFile;
import java.util.List;
/** /**
* Abstraction for a screen representing a single platform's games. * Abstraction for a screen representing a single platform's games.
*/ */
public interface PlatformGamesView public interface PlatformGamesView
{ {
/** /**
* Tell the view that a certain game's screenshot has been updated, * Tell the view that a certain game's screenshot has been updated,
* and should be redrawn on-screen. * and should be redrawn on-screen.
* *
* @param position The index of the game that should be redrawn. * @param position The index of the game that should be redrawn.
*/ */
void refreshScreenshotAtPosition(int position); void refreshScreenshotAtPosition(int position);
/** /**
* Pass a click event to the view's Presenter. Typically called from the * Pass a click event to the view's Presenter. Typically called from the
* view's list adapter. * view's list adapter.
* *
* @param gameId The ID of the game that was clicked. * @param gameId The ID of the game that was clicked.
*/ */
void onItemClick(String gameId); void onItemClick(String gameId);
/** /**
* To be called when the game file cache is updated. * To be called when the game file cache is updated.
*/ */
void showGames(); void showGames();
} }

View File

@ -18,108 +18,111 @@ import org.dolphinemu.dolphinemu.services.DirectoryInitializationService;
public class Analytics public class Analytics
{ {
private static DirectoryStateReceiver directoryStateReceiver; private static DirectoryStateReceiver directoryStateReceiver;
private static final String analyticsAsked = Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_PERMISSION_ASKED; private static final String analyticsAsked =
private static final String analyticsEnabled = Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_ENABLED; Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_PERMISSION_ASKED;
private static final String analyticsEnabled =
Settings.SECTION_ANALYTICS + "_" + SettingsFile.KEY_ANALYTICS_ENABLED;
private static final String DEVICE_MANUFACTURER = "DEVICE_MANUFACTURER"; private static final String DEVICE_MANUFACTURER = "DEVICE_MANUFACTURER";
private static final String DEVICE_OS = "DEVICE_OS"; private static final String DEVICE_OS = "DEVICE_OS";
private static final String DEVICE_MODEL = "DEVICE_MODEL"; private static final String DEVICE_MODEL = "DEVICE_MODEL";
private static final String DEVICE_TYPE = "DEVICE_TYPE"; private static final String DEVICE_TYPE = "DEVICE_TYPE";
private static String deviceType; private static String deviceType;
public static void checkAnalyticsInit(Context context) public static void checkAnalyticsInit(Context context)
{ {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (!preferences.getBoolean(analyticsAsked, false)) if (!preferences.getBoolean(analyticsAsked, false))
{ {
if (!DirectoryInitializationService.areDolphinDirectoriesReady()) if (!DirectoryInitializationService.areDolphinDirectoriesReady())
{ {
// Wait for directories to get initialized // Wait for directories to get initialized
IntentFilter statusIntentFilter = new IntentFilter( IntentFilter statusIntentFilter = new IntentFilter(
DirectoryInitializationService.BROADCAST_ACTION); DirectoryInitializationService.BROADCAST_ACTION);
directoryStateReceiver = new DirectoryStateReceiver(directoryInitializationState -> directoryStateReceiver = new DirectoryStateReceiver(directoryInitializationState ->
{ {
if (directoryInitializationState == DirectoryInitializationService.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED) if (directoryInitializationState ==
{ DirectoryInitializationService.DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED)
LocalBroadcastManager.getInstance(context).unregisterReceiver(directoryStateReceiver); {
directoryStateReceiver = null; LocalBroadcastManager.getInstance(context).unregisterReceiver(directoryStateReceiver);
showMessage(context, preferences); directoryStateReceiver = null;
} showMessage(context, preferences);
}); }
// Registers the DirectoryStateReceiver and its intent filters });
LocalBroadcastManager.getInstance(context).registerReceiver( // Registers the DirectoryStateReceiver and its intent filters
directoryStateReceiver, LocalBroadcastManager.getInstance(context).registerReceiver(
statusIntentFilter); directoryStateReceiver,
} statusIntentFilter);
else }
{ else
showMessage(context, preferences); {
} showMessage(context, preferences);
} }
// Get device type now since we have a context }
deviceType = TvUtil.isLeanback(context) ? "android-tv" : "android-mobile"; // Get device type now since we have a context
} deviceType = TvUtil.isLeanback(context) ? "android-tv" : "android-mobile";
}
private static void showMessage(Context context, SharedPreferences preferences) private static void showMessage(Context context, SharedPreferences preferences)
{ {
// We asked, set to true regardless of answer // We asked, set to true regardless of answer
SharedPreferences.Editor sPrefsEditor = preferences.edit(); SharedPreferences.Editor sPrefsEditor = preferences.edit();
sPrefsEditor.putBoolean(analyticsAsked, true); sPrefsEditor.putBoolean(analyticsAsked, true);
sPrefsEditor.apply(); sPrefsEditor.apply();
new AlertDialog.Builder(context) new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.analytics)) .setTitle(context.getString(R.string.analytics))
.setMessage(context.getString(R.string.analytics_desc)) .setMessage(context.getString(R.string.analytics_desc))
.setPositiveButton(R.string.yes, (dialogInterface, i) -> .setPositiveButton(R.string.yes, (dialogInterface, i) ->
{ {
sPrefsEditor.putBoolean(analyticsEnabled, true); sPrefsEditor.putBoolean(analyticsEnabled, true);
sPrefsEditor.apply(); sPrefsEditor.apply();
SettingsFile.firstAnalyticsAdd(true); SettingsFile.firstAnalyticsAdd(true);
}) })
.setNegativeButton(R.string.no, (dialogInterface, i) -> .setNegativeButton(R.string.no, (dialogInterface, i) ->
{ {
sPrefsEditor.putBoolean(analyticsEnabled, false); sPrefsEditor.putBoolean(analyticsEnabled, false);
sPrefsEditor.apply(); sPrefsEditor.apply();
SettingsFile.firstAnalyticsAdd(false); SettingsFile.firstAnalyticsAdd(false);
}) })
.create() .create()
.show(); .show();
} }
public static void sendReport(String endpoint, byte[] data) public static void sendReport(String endpoint, byte[] data)
{ {
StringRequest request = new StringRequest(Request.Method.POST, endpoint, StringRequest request = new StringRequest(Request.Method.POST, endpoint,
null, error -> Log.debug("Send Report Failure code: " null, error -> Log.debug("Send Report Failure code: "
+ error.networkResponse.statusCode)) + error.networkResponse.statusCode))
{ {
@Override @Override
public byte[] getBody() public byte[] getBody()
{ {
return data; return data;
} }
}; };
VolleyUtil.getQueue().add(request); VolleyUtil.getQueue().add(request);
} }
public static String getValue(String key) public static String getValue(String key)
{ {
switch (key) switch (key)
{ {
case DEVICE_MODEL: case DEVICE_MODEL:
return Build.MODEL; return Build.MODEL;
case DEVICE_MANUFACTURER: case DEVICE_MANUFACTURER:
return Build.MANUFACTURER; return Build.MANUFACTURER;
case DEVICE_OS: case DEVICE_OS:
return String.valueOf(Build.VERSION.SDK_INT); return String.valueOf(Build.VERSION.SDK_INT);
case DEVICE_TYPE: case DEVICE_TYPE:
return deviceType; return deviceType;
default: default:
return ""; return "";
} }
} }
} }

View File

@ -5,25 +5,25 @@ import android.view.ViewPropertyAnimator;
public final class Animations public final class Animations
{ {
private Animations() private Animations()
{ {
} }
public static ViewPropertyAnimator fadeViewIn(View view) public static ViewPropertyAnimator fadeViewIn(View view)
{ {
view.setVisibility(View.VISIBLE); view.setVisibility(View.VISIBLE);
return view.animate() return view.animate()
.withLayer() .withLayer()
.setDuration(100) .setDuration(100)
.alpha(1.0f); .alpha(1.0f);
} }
public static ViewPropertyAnimator fadeViewOut(View view) public static ViewPropertyAnimator fadeViewOut(View view)
{ {
return view.animate() return view.animate()
.withLayer() .withLayer()
.setDuration(300) .setDuration(300)
.alpha(0.0f); .alpha(0.0f);
} }
} }

View File

@ -10,150 +10,150 @@ import java.util.List;
*/ */
public class AppLinkHelper public class AppLinkHelper
{ {
public static final String PLAY = "play"; public static final String PLAY = "play";
public static final String BROWSE = "browse"; public static final String BROWSE = "browse";
private static final String SCHEMA_URI_PREFIX = "dolphinemu://app/"; private static final String SCHEMA_URI_PREFIX = "dolphinemu://app/";
private static final String URI_PLAY = SCHEMA_URI_PREFIX + PLAY; private static final String URI_PLAY = SCHEMA_URI_PREFIX + PLAY;
private static final String URI_VIEW = SCHEMA_URI_PREFIX + BROWSE; private static final String URI_VIEW = SCHEMA_URI_PREFIX + BROWSE;
private static final int URI_INDEX_OPTION = 0; private static final int URI_INDEX_OPTION = 0;
private static final int URI_INDEX_CHANNEL = 1; private static final int URI_INDEX_CHANNEL = 1;
private static final int URI_INDEX_GAME = 2; private static final int URI_INDEX_GAME = 2;
public static Uri buildGameUri(long channelId, String gameId) public static Uri buildGameUri(long channelId, String gameId)
{ {
return Uri.parse(URI_PLAY) return Uri.parse(URI_PLAY)
.buildUpon() .buildUpon()
.appendPath(String.valueOf(channelId)) .appendPath(String.valueOf(channelId))
.appendPath(String.valueOf(gameId)) .appendPath(String.valueOf(gameId))
.build(); .build();
} }
public static Uri buildBrowseUri(String subscriptionName) public static Uri buildBrowseUri(String subscriptionName)
{ {
return Uri.parse(URI_VIEW).buildUpon().appendPath(subscriptionName).build(); return Uri.parse(URI_VIEW).buildUpon().appendPath(subscriptionName).build();
} }
public static AppLinkAction extractAction(Uri uri) public static AppLinkAction extractAction(Uri uri)
{ {
if (isGameUri(uri)) if (isGameUri(uri))
return new PlayAction(extractChannelId(uri), extractGameId(uri)); return new PlayAction(extractChannelId(uri), extractGameId(uri));
else if (isBrowseUri(uri)) else if (isBrowseUri(uri))
return new BrowseAction(extractSubscriptionName(uri)); return new BrowseAction(extractSubscriptionName(uri));
throw new IllegalArgumentException("No action found for uri " + uri); throw new IllegalArgumentException("No action found for uri " + uri);
} }
private static boolean isGameUri(Uri uri) private static boolean isGameUri(Uri uri)
{ {
if (uri.getPathSegments().isEmpty()) if (uri.getPathSegments().isEmpty())
{ {
return false; return false;
} }
String option = uri.getPathSegments().get(URI_INDEX_OPTION); String option = uri.getPathSegments().get(URI_INDEX_OPTION);
return PLAY.equals(option); return PLAY.equals(option);
} }
private static boolean isBrowseUri(Uri uri) private static boolean isBrowseUri(Uri uri)
{ {
if (uri.getPathSegments().isEmpty()) if (uri.getPathSegments().isEmpty())
return false; return false;
String option = uri.getPathSegments().get(URI_INDEX_OPTION); String option = uri.getPathSegments().get(URI_INDEX_OPTION);
return BROWSE.equals(option); return BROWSE.equals(option);
} }
private static String extractSubscriptionName(Uri uri) private static String extractSubscriptionName(Uri uri)
{ {
return extract(uri, URI_INDEX_CHANNEL); return extract(uri, URI_INDEX_CHANNEL);
} }
private static long extractChannelId(Uri uri) private static long extractChannelId(Uri uri)
{ {
return extractLong(uri, URI_INDEX_CHANNEL); return extractLong(uri, URI_INDEX_CHANNEL);
} }
private static String extractGameId(Uri uri) private static String extractGameId(Uri uri)
{ {
return extract(uri, URI_INDEX_GAME); return extract(uri, URI_INDEX_GAME);
} }
private static long extractLong(Uri uri, int index) private static long extractLong(Uri uri, int index)
{ {
return Long.valueOf(extract(uri, index)); return Long.valueOf(extract(uri, index));
} }
private static String extract(Uri uri, int index) private static String extract(Uri uri, int index)
{ {
List<String> pathSegments = uri.getPathSegments(); List<String> pathSegments = uri.getPathSegments();
if (pathSegments.isEmpty() || pathSegments.size() < index) if (pathSegments.isEmpty() || pathSegments.size() < index)
return null; return null;
return pathSegments.get(index); return pathSegments.get(index);
} }
@StringDef({BROWSE, PLAY}) @StringDef({BROWSE, PLAY})
public @interface ActionFlags public @interface ActionFlags
{ {
} }
/** /**
* Action for deep linking. * Action for deep linking.
*/ */
public interface AppLinkAction public interface AppLinkAction
{ {
/** /**
* Returns an string representation of the action. * Returns an string representation of the action.
*/ */
@ActionFlags @ActionFlags
String getAction(); String getAction();
} }
/** /**
* Action when clicking the channel icon * Action when clicking the channel icon
*/ */
public static class BrowseAction implements AppLinkAction public static class BrowseAction implements AppLinkAction
{ {
private final String mSubscriptionName; private final String mSubscriptionName;
private BrowseAction(String subscriptionName) private BrowseAction(String subscriptionName)
{ {
this.mSubscriptionName = subscriptionName; this.mSubscriptionName = subscriptionName;
} }
@Override @Override
public String getAction() public String getAction()
{ {
return BROWSE; return BROWSE;
} }
} }
/** /**
* Action when clicking a program(game) * Action when clicking a program(game)
*/ */
public static class PlayAction implements AppLinkAction public static class PlayAction implements AppLinkAction
{ {
private final long channelId; private final long channelId;
private final String gameId; private final String gameId;
private PlayAction(long channelId, String gameId) private PlayAction(long channelId, String gameId)
{ {
this.channelId = channelId; this.channelId = channelId;
this.gameId = gameId; this.gameId = gameId;
} }
public long getChannelId() public long getChannelId()
{ {
return channelId; return channelId;
} }
public String getGameId() public String getGameId()
{ {
return gameId; return gameId;
} }
@Override @Override
public String getAction() public String getAction()
{ {
return PLAY; return PLAY;
} }
} }
} }

View File

@ -6,22 +6,22 @@ import java.util.Map;
public class BiMap<K, V> public class BiMap<K, V>
{ {
private Map<K, V> forward = new HashMap<K, V>(); private Map<K, V> forward = new HashMap<K, V>();
private Map<V, K> backward = new HashMap<V, K>(); private Map<V, K> backward = new HashMap<V, K>();
public synchronized void add(K key, V value) public synchronized void add(K key, V value)
{ {
forward.put(key, value); forward.put(key, value);
backward.put(value, key); backward.put(value, key);
} }
public synchronized V getForward(K key) public synchronized V getForward(K key)
{ {
return forward.get(key); return forward.get(key);
} }
public synchronized K getBackward(V key) public synchronized K getBackward(V key)
{ {
return backward.get(key); return backward.get(key);
} }
} }

View File

@ -4,54 +4,61 @@ import android.view.InputDevice;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
/** Some controllers have incorrect mappings. This class has special-case fixes for them. */ /**
* Some controllers have incorrect mappings. This class has special-case fixes for them.
*/
public class ControllerMappingHelper public class ControllerMappingHelper
{ {
/** Some controllers report extra button presses that can be ignored. */ /**
public static boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode) * Some controllers report extra button presses that can be ignored.
{ */
if (isDualShock4(inputDevice)) { public static boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode)
// 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 if (isDualShock4(inputDevice))
// 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; // 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
return false; // 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;
}
/** Scale an axis to be zero-centered with a proper range. */ /**
public static float scaleAxis(InputDevice inputDevice, int axis, float value) * Scale an axis to be zero-centered with a proper range.
{ */
if (isDualShock4(inputDevice)) public static float scaleAxis(InputDevice inputDevice, int axis, float value)
{ {
// Android doesn't have correct mappings for this controller's triggers. It reports them if (isDualShock4(inputDevice))
// 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]. // Android doesn't have correct mappings for this controller's triggers. It reports them
if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY) // 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].
return (value + 1) / 2.0f; 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. else if (isXboxOneWireless(inputDevice))
if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ) {
{ // Same as the DualShock 4, the mappings are missing.
return (value + 1) / 2.0f; if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ)
} {
} return (value + 1) / 2.0f;
return value; }
} }
return value;
}
private static boolean isDualShock4(InputDevice inputDevice) private static boolean isDualShock4(InputDevice inputDevice)
{ {
// Sony DualShock 4 controller // Sony DualShock 4 controller
return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc; return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc;
} }
private static boolean isXboxOneWireless(InputDevice inputDevice) private static boolean isXboxOneWireless(InputDevice inputDevice)
{ {
// Microsoft Xbox One controller // Microsoft Xbox One controller
return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0; return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0;
} }
} }

View File

@ -3,79 +3,78 @@ package org.dolphinemu.dolphinemu.utils;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
import java.io.FileOutputStream; import java.io.FileOutputStream;
public final class CoverHelper public final class CoverHelper
{ {
private static String baseUrl = "https://art.gametdb.com/wii/cover/%s/%s.png"; private static String baseUrl = "https://art.gametdb.com/wii/cover/%s/%s.png";
public static String buildGameTDBUrl(GameFile game, String region) public static String buildGameTDBUrl(GameFile game, String region)
{ {
String gameId = game.getGameId(); String gameId = game.getGameId();
if(game.getPlatform() == 2) // WiiWare if (game.getPlatform() == 2) // WiiWare
gameId = gameId.substring(0,4); gameId = gameId.substring(0, 4);
return String.format(baseUrl, region, gameId); return String.format(baseUrl, region, gameId);
} }
public static String getRegion(GameFile game) public static String getRegion(GameFile game)
{ {
String region; String region;
switch(game.getRegion()) switch (game.getRegion())
{ {
case 0: // NTSC_J case 0: // NTSC_J
region = "JA"; region = "JA";
break; break;
case 1: // NTSC_U case 1: // NTSC_U
region = "US"; region = "US";
break; break;
case 4: // NTSC_K case 4: // NTSC_K
region = "KO"; region = "KO";
break; break;
case 2: // PAL case 2: // PAL
switch (game.getCountry()) switch (game.getCountry())
{ {
case 2: // German case 2: // German
region = "DE"; region = "DE";
break; break;
case 3: // French case 3: // French
region = "FR"; region = "FR";
break; break;
case 4: // Spanish case 4: // Spanish
region = "ES"; region = "ES";
break; break;
case 5: // Italian case 5: // Italian
region = "IT"; region = "IT";
break; break;
case 6: // Dutch case 6: // Dutch
region = "NL"; region = "NL";
break; break;
case 1: // English case 1: // English
default: default:
region = "EN"; region = "EN";
break; break;
} }
break; break;
case 3: // Unknown case 3: // Unknown
default: default:
region = "EN"; region = "EN";
break; break;
} }
return region; return region;
} }
public static void saveCover(Bitmap cover, String path) public static void saveCover(Bitmap cover, String path)
{ {
try try
{ {
FileOutputStream out = new FileOutputStream(path); FileOutputStream out = new FileOutputStream(path);
cover.compress(Bitmap.CompressFormat.PNG, 100, out); cover.compress(Bitmap.CompressFormat.PNG, 100, out);
out.close(); out.close();
} }
catch (Exception e) catch (Exception e)
{ {
// Do nothing // Do nothing
} }
} }
} }

View File

@ -9,16 +9,20 @@ import org.dolphinemu.dolphinemu.services.DirectoryInitializationService.Directo
import rx.functions.Action1; import rx.functions.Action1;
public class DirectoryStateReceiver extends BroadcastReceiver { public class DirectoryStateReceiver extends BroadcastReceiver
Action1<DirectoryInitializationState> callback; {
public DirectoryStateReceiver(Action1<DirectoryInitializationState> callback) { Action1<DirectoryInitializationState> callback;
this.callback = callback;
}
@Override public DirectoryStateReceiver(Action1<DirectoryInitializationState> callback)
public void onReceive(Context context, Intent intent) {
{ this.callback = callback;
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);
}
} }

View File

@ -24,367 +24,366 @@ import javax.microedition.khronos.opengles.GL10;
*/ */
public final class EGLHelper public final class EGLHelper
{ {
private final EGL10 mEGL; private final EGL10 mEGL;
private final EGLDisplay mDisplay; private final EGLDisplay mDisplay;
private EGLConfig[] mEGLConfigs; private EGLConfig[] mEGLConfigs;
private EGLContext mEGLContext; private EGLContext mEGLContext;
private EGLSurface mEGLSurface; private EGLSurface mEGLSurface;
private GL10 mGL; private GL10 mGL;
// GL support flags // GL support flags
private boolean supportGL; private boolean supportGL;
private boolean supportGLES2; private boolean supportGLES2;
private boolean supportGLES3; private boolean supportGLES3;
// Renderable type bitmasks // Renderable type bitmasks
public static final int EGL_OPENGL_ES_BIT = 0x0001; public static final int EGL_OPENGL_ES_BIT = 0x0001;
public static final int EGL_OPENGL_ES2_BIT = 0x0004; public static final int EGL_OPENGL_ES2_BIT = 0x0004;
public static final int EGL_OPENGL_BIT = 0x0008; public static final int EGL_OPENGL_BIT = 0x0008;
public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040; public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040;
// API types // API types
public static final int EGL_OPENGL_ES_API = 0x30A0; public static final int EGL_OPENGL_ES_API = 0x30A0;
public static final int EGL_OPENGL_API = 0x30A2; public static final int EGL_OPENGL_API = 0x30A2;
/** /**
* Constructor * Constructor
* <p> * <p>
* Initializes the underlying {@link EGLSurface} with a width and height of 1. * 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 * This is useful if all you need to use this class for is to query information
* from specific API contexts. * from specific API contexts.
* *
* @param renderableType Bitmask indicating which types of client API contexts * @param renderableType Bitmask indicating which types of client API contexts
* the framebuffer config must support. * the framebuffer config must support.
*/ */
public EGLHelper(int renderableType) public EGLHelper(int renderableType)
{ {
this(1, 1, renderableType); this(1, 1, renderableType);
} }
/** /**
* Constructor * Constructor
* *
* @param width Width of the underlying {@link EGLSurface}. * @param width Width of the underlying {@link EGLSurface}.
* @param height Height of the underlying {@link EGLSurface}. * @param height Height of the underlying {@link EGLSurface}.
* @param renderableType Bitmask indicating which types of client API contexts * @param renderableType Bitmask indicating which types of client API contexts
* the framebuffer config must support. * the framebuffer config must support.
*/ */
public EGLHelper(int width, int height, int renderableType) public EGLHelper(int width, int height, int renderableType)
{ {
// Initialize handle to an EGL display. // Initialize handle to an EGL display.
mEGL = (EGL10) EGLContext.getEGL(); mEGL = (EGL10) EGLContext.getEGL();
mDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); mDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// If a display is present, initialize EGL. // If a display is present, initialize EGL.
if (mDisplay != EGL10.EGL_NO_DISPLAY) if (mDisplay != EGL10.EGL_NO_DISPLAY)
{ {
int[] version = new int[2]; int[] version = new int[2];
if (mEGL.eglInitialize(mDisplay, version)) if (mEGL.eglInitialize(mDisplay, version))
{ {
// Detect supported GL APIs, initialize configs, etc. // Detect supported GL APIs, initialize configs, etc.
detect(); detect();
// Create context and surface // Create context and surface
create(width, height, renderableType); create(width, height, renderableType);
} }
else else
{ {
Log.error("[EGLHelper] Error initializing EGL."); Log.error("[EGLHelper] Error initializing EGL.");
} }
} }
else else
{ {
Log.error("[EGLHelper] Error initializing EGL display."); Log.error("[EGLHelper] Error initializing EGL display.");
} }
} }
/** /**
* Releases all resources associated with this helper. * Releases all resources associated with this helper.
* <p> * <p>
* This should be called whenever this helper is no longer needed. * This should be called whenever this helper is no longer needed.
*/ */
public void closeHelper() public void closeHelper()
{ {
mEGL.eglTerminate(mDisplay); mEGL.eglTerminate(mDisplay);
} }
/** /**
* Gets information through EGL.<br/> * Gets information through EGL.<br/>
* <p> * <p>
* Index 0: Vendor <br/> * Index 0: Vendor <br/>
* Index 1: Version <br/> * Index 1: Version <br/>
* Index 2: Renderer <br/> * Index 2: Renderer <br/>
* Index 3: Extensions <br/> * Index 3: Extensions <br/>
* *
* @return information retrieved through EGL. * @return information retrieved through EGL.
*/ */
public String[] getEGLInfo() public String[] getEGLInfo()
{ {
return new String[] { return new String[]{
mGL.glGetString(GL10.GL_VENDOR), mGL.glGetString(GL10.GL_VENDOR),
mGL.glGetString(GL10.GL_VERSION), mGL.glGetString(GL10.GL_VERSION),
mGL.glGetString(GL10.GL_RENDERER), mGL.glGetString(GL10.GL_RENDERER),
mGL.glGetString(GL10.GL_EXTENSIONS), mGL.glGetString(GL10.GL_EXTENSIONS),
}; };
} }
/** /**
* Whether or not this device supports OpenGL. * Whether or not this device supports OpenGL.
* *
* @return true if this device supports OpenGL; false otherwise. * @return true if this device supports OpenGL; false otherwise.
*/ */
public boolean supportsOpenGL() public boolean supportsOpenGL()
{ {
return supportGL; return supportGL;
} }
/** /**
* Whether or not this device supports OpenGL ES 2. * Whether or not this device supports OpenGL ES 2.
* <br/> * <br/>
* Note that if this returns true, then OpenGL ES 1 is also supported. * Note that if this returns true, then OpenGL ES 1 is also supported.
* *
* @return true if this device supports OpenGL ES 2; false otherwise. * @return true if this device supports OpenGL ES 2; false otherwise.
*/ */
public boolean supportsGLES2() public boolean supportsGLES2()
{ {
return supportGLES2; return supportGLES2;
} }
/** /**
* Whether or not this device supports OpenGL ES 3. * Whether or not this device supports OpenGL ES 3.
* <br/> * <br/>
* Note that if this returns true, then OpenGL ES 1 and 2 are also supported. * 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. * @return true if this device supports OpenGL ES 3; false otherwise.
*/ */
public boolean supportsGLES3() public boolean supportsGLES3()
{ {
return supportGLES3; return supportGLES3;
} }
/** /**
* Gets the underlying {@link EGL10} instance. * Gets the underlying {@link EGL10} instance.
* *
* @return the underlying {@link EGL10} instance. * @return the underlying {@link EGL10} instance.
*/ */
public EGL10 getEGL() public EGL10 getEGL()
{ {
return mEGL; return mEGL;
} }
/** /**
* Gets the underlying {@link GL10} instance. * Gets the underlying {@link GL10} instance.
* *
* @return the underlying {@link GL10} instance. * @return the underlying {@link GL10} instance.
*/ */
public GL10 getGL() public GL10 getGL()
{ {
return mGL; return mGL;
} }
/** /**
* Gets the underlying {@link EGLDisplay}. * Gets the underlying {@link EGLDisplay}.
* *
* @return the underlying {@link EGLDisplay} * @return the underlying {@link EGLDisplay}
*/ */
public EGLDisplay getDisplay() public EGLDisplay getDisplay()
{ {
return mDisplay; return mDisplay;
} }
/** /**
* Gets all supported framebuffer configurations for this device. * Gets all supported framebuffer configurations for this device.
* *
* @return all supported framebuffer configurations for this device. * @return all supported framebuffer configurations for this device.
*/ */
public EGLConfig[] getConfigs() public EGLConfig[] getConfigs()
{ {
return mEGLConfigs; return mEGLConfigs;
} }
/** /**
* Gets the underlying {@link EGLContext}. * Gets the underlying {@link EGLContext}.
* *
* @return the underlying {@link EGLContext}. * @return the underlying {@link EGLContext}.
*/ */
public EGLContext getContext() public EGLContext getContext()
{ {
return mEGLContext; return mEGLContext;
} }
/** /**
* Gets the underlying {@link EGLSurface}. * Gets the underlying {@link EGLSurface}.
* *
* @return the underlying {@link EGLSurface}. * @return the underlying {@link EGLSurface}.
*/ */
public EGLSurface getSurface() public EGLSurface getSurface()
{ {
return mEGLSurface; return mEGLSurface;
} }
// Detects the specific kind of GL modes that are supported // Detects the specific kind of GL modes that are supported
private boolean detect() private boolean detect()
{ {
// Get total number of configs available. // Get total number of configs available.
int[] numConfigs = new int[1]; int[] numConfigs = new int[1];
if (!mEGL.eglGetConfigs(mDisplay, null, 0, numConfigs)) if (!mEGL.eglGetConfigs(mDisplay, null, 0, numConfigs))
{ {
Log.error("[EGLHelper] Error retrieving number of EGL configs available."); Log.error("[EGLHelper] Error retrieving number of EGL configs available.");
return false; return false;
} }
// Now get all the configurations // Now get all the configurations
mEGLConfigs = new EGLConfig[numConfigs[0]]; mEGLConfigs = new EGLConfig[numConfigs[0]];
if (!mEGL.eglGetConfigs(mDisplay, mEGLConfigs, mEGLConfigs.length, numConfigs)) if (!mEGL.eglGetConfigs(mDisplay, mEGLConfigs, mEGLConfigs.length, numConfigs))
{ {
Log.error("[EGLHelper] Error retrieving all EGL configs."); Log.error("[EGLHelper] Error retrieving all EGL configs.");
return false; return false;
} }
for (EGLConfig mEGLConfig : mEGLConfigs) for (EGLConfig mEGLConfig : mEGLConfigs)
{ {
int[] attribVal = new int[1]; int[] attribVal = new int[1];
boolean ret = mEGL.eglGetConfigAttrib(mDisplay, mEGLConfig, EGL10.EGL_RENDERABLE_TYPE, attribVal); boolean ret =
if (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_BIT) != 0)
supportGL = true;
if ((attribVal[0] & EGL_OPENGL_ES2_BIT) != 0) if ((attribVal[0] & EGL_OPENGL_ES2_BIT) != 0)
supportGLES2 = true; supportGLES2 = true;
if ((attribVal[0] & EGL_OPENGL_ES3_BIT_KHR) != 0) if ((attribVal[0] & EGL_OPENGL_ES3_BIT_KHR) != 0)
supportGLES3 = true; supportGLES3 = true;
} }
} }
return true; return true;
} }
// Creates the context and surface. // Creates the context and surface.
private void create(int width, int height, int renderableType) private void create(int width, int height, int renderableType)
{ {
int[] attribs = { int[] attribs = {
EGL10.EGL_WIDTH, width, EGL10.EGL_WIDTH, width,
EGL10.EGL_HEIGHT, height, EGL10.EGL_HEIGHT, height,
EGL10.EGL_NONE EGL10.EGL_NONE
}; };
// Initially we just assume GLES2 will be the default context. // Initially we just assume GLES2 will be the default context.
int EGL_CONTEXT_CLIENT_VERSION = 0x3098; int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] ctx_attribs = { int[] ctx_attribs = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE EGL10.EGL_NONE
}; };
// Determine the type of context that will be created // Determine the type of context that will be created
// and change the attribute arrays accordingly. // and change the attribute arrays accordingly.
switch (renderableType) switch (renderableType)
{ {
case EGL_OPENGL_ES_BIT: case EGL_OPENGL_ES_BIT:
ctx_attribs[1] = 1; ctx_attribs[1] = 1;
break; break;
case EGL_OPENGL_BIT: case EGL_OPENGL_BIT:
ctx_attribs[0] = EGL10.EGL_NONE; ctx_attribs[0] = EGL10.EGL_NONE;
break; break;
case EGL_OPENGL_ES3_BIT_KHR: case EGL_OPENGL_ES3_BIT_KHR:
ctx_attribs[1] = 3; ctx_attribs[1] = 3;
break; break;
case EGL_OPENGL_ES2_BIT: case EGL_OPENGL_ES2_BIT:
default: // Fall-back to GLES 2. default: // Fall-back to GLES 2.
ctx_attribs[1] = 2; ctx_attribs[1] = 2;
break; break;
} }
if (renderableType == EGL_OPENGL_BIT) if (renderableType == EGL_OPENGL_BIT)
NativeLibrary.eglBindAPI(EGL_OPENGL_API); NativeLibrary.eglBindAPI(EGL_OPENGL_API);
else else
NativeLibrary.eglBindAPI(EGL_OPENGL_ES_API); NativeLibrary.eglBindAPI(EGL_OPENGL_ES_API);
mEGLContext = mEGL.eglCreateContext(mDisplay, mEGLConfigs[0], EGL10.EGL_NO_CONTEXT, ctx_attribs); mEGLContext =
mEGLSurface = mEGL.eglCreatePbufferSurface(mDisplay, mEGLConfigs[0], attribs); mEGL.eglCreateContext(mDisplay, mEGLConfigs[0], EGL10.EGL_NO_CONTEXT, ctx_attribs);
mEGL.eglMakeCurrent(mDisplay, mEGLSurface, mEGLSurface, mEGLContext); mEGLSurface = mEGL.eglCreatePbufferSurface(mDisplay, mEGLConfigs[0], attribs);
mGL = (GL10) mEGLContext.getGL(); mEGL.eglMakeCurrent(mDisplay, mEGLSurface, mEGLSurface, mEGLContext);
} mGL = (GL10) mEGLContext.getGL();
}
/** /**
* Simplified call to {@link GL10#glGetString(int)} * Simplified call to {@link GL10#glGetString(int)}
* <p> * <p>
* Accepts the following constants: * Accepts the following constants:
* <ul> * <ul>
* <li>GL_VENDOR - Company responsible for the GL implementation.</li> * <li>GL_VENDOR - Company responsible for the GL implementation.</li>
* <li>GL_VERSION - Version or release number.</li> * <li>GL_VERSION - Version or release number.</li>
* <li>GL_RENDERER - Name of the renderer</li> * <li>GL_RENDERER - Name of the renderer</li>
* <li>GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language </li> * <li>GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language </li>
* </ul> * </ul>
* *
* @param glEnum A symbolic constant within {@link GL10}. * @param glEnum A symbolic constant within {@link GL10}.
* * @return the string information represented by {@code glEnum}.
* @return the string information represented by {@code glEnum}. */
*/ public String glGetString(int glEnum)
public String glGetString(int glEnum) {
{ return mGL.glGetString(glEnum);
return mGL.glGetString(glEnum); }
}
/** /**
* Simplified call to {@link GLES30#glGetStringi(int, int)} * Simplified call to {@link GLES30#glGetStringi(int, int)}
* <p> * <p>
* Accepts the following constants: * Accepts the following constants:
* <ul> * <ul>
* <li>GL_VENDOR - Company responsible for the GL implementation.</li> * <li>GL_VENDOR - Company responsible for the GL implementation.</li>
* <li>GL_VERSION - Version or release number.</li> * <li>GL_VERSION - Version or release number.</li>
* <li>GL_RENDERER - Name of the renderer</li> * <li>GL_RENDERER - Name of the renderer</li>
* <li>GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language </li> * <li>GL_SHADING_LANGUAGE_VERSION - Version or release number of the shading language </li>
* <li>GL_EXTENSIONS - Extension string supported by the implementation at {@code index}.</li> * <li>GL_EXTENSIONS - Extension string supported by the implementation at {@code index}.</li>
* </ul> * </ul>
* *
* @param glEnum A symbolic GL constant * @param glEnum A symbolic GL constant
* @param index The index of the string to return. * @param index The index of the string to return.
* * @return the string information represented by {@code glEnum} and {@code index}.
* @return the string information represented by {@code glEnum} and {@code index}. */
*/ public String glGetStringi(int glEnum, int index)
public String glGetStringi(int glEnum, int index) {
{ return GLES30.glGetStringi(glEnum, index);
return GLES30.glGetStringi(glEnum, index); }
}
public boolean SupportsExtension(String extension) public boolean SupportsExtension(String extension)
{ {
int[] num_ext = new int[1]; int[] num_ext = new int[1];
GLES30.glGetIntegerv(GLES30.GL_NUM_EXTENSIONS, num_ext, 0); GLES30.glGetIntegerv(GLES30.GL_NUM_EXTENSIONS, num_ext, 0);
for (int i = 0; i < num_ext[0]; ++i) for (int i = 0; i < num_ext[0]; ++i)
{ {
String ext = GLES30.glGetStringi(GLES30.GL_EXTENSIONS, i); String ext = GLES30.glGetStringi(GLES30.GL_EXTENSIONS, i);
if (ext.equals(extension)) if (ext.equals(extension))
return true; return true;
} }
return false; return false;
} }
public int GetVersion() public int GetVersion()
{ {
int[] major = new int[1]; int[] major = new int[1];
int[] minor = new int[1]; int[] minor = new int[1];
GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, major, 0); GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, major, 0);
GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, minor, 0); GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, minor, 0);
return major[0] * 100 + minor[0] * 10; return major[0] * 100 + minor[0] * 10;
} }
/** /**
* Simplified call to {@link GL10#glGetIntegerv(int, int[], int) * Simplified call to {@link GL10#glGetIntegerv(int, int[], int)
* *
* @param glEnum A symbolic GL constant. * @param glEnum A symbolic GL constant.
* * @return the integer information represented by {@code glEnum}.
* @return the integer information represented by {@code glEnum}. */
*/ public int glGetInteger(int glEnum)
public int glGetInteger(int glEnum) {
{ int[] val = new int[1];
int[] val = new int[1]; mGL.glGetIntegerv(glEnum, val, 0);
mGL.glGetIntegerv(glEnum, val, 0); return val[0];
return val[0]; }
}
} }

View File

@ -17,41 +17,43 @@ import java.util.List;
public final class FileBrowserHelper public final class FileBrowserHelper
{ {
public static void openDirectoryPicker(FragmentActivity activity) 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());
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<Uri> files = Utils.getSelectedFilesFromResult(result);
if (!files.isEmpty())
{ {
Intent i = new Intent(activity, CustomFilePickerActivity.class); File file = Utils.getFileForUri(files.get(0));
return file.getAbsolutePath();
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) return null;
{ }
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<Uri> files = Utils.getSelectedFilesFromResult(result);
if (!files.isEmpty())
{
File file = Utils.getFileForUri(files.get(0));
return file.getAbsolutePath();
}
return null;
}
} }

View File

@ -19,134 +19,143 @@ import org.dolphinemu.dolphinemu.services.USBPermService;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class Java_GCAdapter { public class Java_GCAdapter
public static UsbManager manager; {
static byte[] controller_payload = new byte[37]; public static UsbManager manager;
static byte[] controller_payload = new byte[37];
static UsbDeviceConnection usb_con; static UsbDeviceConnection usb_con;
static UsbInterface usb_intf; static UsbInterface usb_intf;
static UsbEndpoint usb_in; static UsbEndpoint usb_in;
static UsbEndpoint usb_out; static UsbEndpoint usb_out;
private static void RequestPermission() private static void RequestPermission()
{ {
Context context = NativeLibrary.sEmulationActivity.get(); Context context = NativeLibrary.sEmulationActivity.get();
if (context != null) if (context != null)
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
{ {
UsbDevice dev = pair.getValue(); UsbDevice dev = pair.getValue();
if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
{ {
if (!manager.hasPermission(dev)) if (!manager.hasPermission(dev))
{ {
Intent intent = new Intent(); Intent intent = new Intent();
PendingIntent pend_intent; PendingIntent pend_intent;
intent.setClass(context, USBPermService.class); intent.setClass(context, USBPermService.class);
pend_intent = PendingIntent.getService(context, 0, intent, 0); pend_intent = PendingIntent.getService(context, 0, intent, 0);
manager.requestPermission(dev, pend_intent); manager.requestPermission(dev, pend_intent);
} }
} }
} }
} }
else else
{ {
Log.warning("Cannot request GameCube Adapter permission as EmulationActivity is null."); Log.warning("Cannot request GameCube Adapter permission as EmulationActivity is null.");
} }
} }
public static void Shutdown() public static void Shutdown()
{ {
usb_con.close(); usb_con.close();
} }
public static int GetFD() { return usb_con.getFileDescriptor(); }
public static boolean QueryAdapter() public static int GetFD()
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); return usb_con.getFileDescriptor();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) }
{
UsbDevice dev = pair.getValue();
if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
{
if (manager.hasPermission(dev))
return true;
else
RequestPermission();
}
}
return false;
}
public static void InitAdapter() public static boolean QueryAdapter()
{ {
byte[] init = { 0x13 }; HashMap<String, UsbDevice> devices = manager.getDeviceList();
usb_con.bulkTransfer(usb_in, init, init.length, 0); for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
} {
UsbDevice dev = pair.getValue();
if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
{
if (manager.hasPermission(dev))
return true;
else
RequestPermission();
}
}
return false;
}
public static int Input() { public static void InitAdapter()
return usb_con.bulkTransfer(usb_in, controller_payload, controller_payload.length, 16); {
} byte[] init = {0x13};
usb_con.bulkTransfer(usb_in, init, init.length, 0);
}
public static int Output(byte[] rumble) { public static int Input()
return usb_con.bulkTransfer(usb_out, rumble, 5, 16); {
} return usb_con.bulkTransfer(usb_in, controller_payload, controller_payload.length, 16);
}
public static boolean OpenAdapter() public static int Output(byte[] rumble)
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); return usb_con.bulkTransfer(usb_out, rumble, 5, 16);
for (Map.Entry<String, UsbDevice> 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()); public static boolean OpenAdapter()
Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); {
HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
{
UsbDevice dev = pair.getValue();
if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e)
{
if (manager.hasPermission(dev))
{
usb_con = manager.openDevice(dev);
if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount());
{ Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount());
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 (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0)
{
UsbConfiguration conf = dev.getConfiguration(0);
usb_intf = conf.getInterface(0);
usb_con.claimInterface(usb_intf, true);
if (usb_intf.getEndpointCount() == 2) Log.info("GCAdapter: Number of endpoints: " + usb_intf.getEndpointCount());
{
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(); if (usb_intf.getEndpointCount() == 2)
return true; {
} for (int i = 0; i < usb_intf.getEndpointCount(); ++i)
else if (usb_intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
{ usb_in = usb_intf.getEndpoint(i);
usb_con.releaseInterface(usb_intf); else
} usb_out = usb_intf.getEndpoint(i);
}
final Activity emulationActivity = NativeLibrary.sEmulationActivity.get(); InitAdapter();
if (emulationActivity != null) return true;
{ }
emulationActivity.runOnUiThread(() -> Toast.makeText(emulationActivity, "GameCube Adapter couldn't be opened. Please re-plug the device.", Toast.LENGTH_LONG).show()); else
} {
else usb_con.releaseInterface(usb_intf);
{ }
Log.warning("Cannot show toast for GameCube Adapter failure."); }
}
usb_con.close(); final Activity emulationActivity = NativeLibrary.sEmulationActivity.get();
} if (emulationActivity != null)
} {
} emulationActivity.runOnUiThread(() -> Toast.makeText(emulationActivity,
return false; "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;
}
} }

View File

@ -19,145 +19,148 @@ import java.util.Map;
public class Java_WiimoteAdapter public class Java_WiimoteAdapter
{ {
final static int MAX_PAYLOAD = 23; final static int MAX_PAYLOAD = 23;
final static int MAX_WIIMOTES = 4; final static int MAX_WIIMOTES = 4;
final static int TIMEOUT = 200; final static int TIMEOUT = 200;
final static short NINTENDO_VENDOR_ID = 0x057e; final static short NINTENDO_VENDOR_ID = 0x057e;
final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306; final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306;
public static UsbManager manager; public static UsbManager manager;
static UsbDeviceConnection usb_con; static UsbDeviceConnection usb_con;
static UsbInterface[] usb_intf = new UsbInterface[MAX_WIIMOTES]; static UsbInterface[] usb_intf = new UsbInterface[MAX_WIIMOTES];
static UsbEndpoint[] usb_in = new UsbEndpoint[MAX_WIIMOTES]; static UsbEndpoint[] usb_in = new UsbEndpoint[MAX_WIIMOTES];
public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD]; public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD];
private static void RequestPermission() private static void RequestPermission()
{ {
Context context = NativeLibrary.sEmulationActivity.get(); Context context = NativeLibrary.sEmulationActivity.get();
if (context != null) if (context != null)
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
{ {
UsbDevice dev = pair.getValue(); UsbDevice dev = pair.getValue();
if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID) if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID &&
{ dev.getVendorId() == NINTENDO_VENDOR_ID)
if (!manager.hasPermission(dev)) {
{ if (!manager.hasPermission(dev))
Log.warning("Requesting permission for Wii Remote adapter"); {
Intent intent = new Intent(); Log.warning("Requesting permission for Wii Remote adapter");
PendingIntent pend_intent; Intent intent = new Intent();
intent.setClass(context, USBPermService.class); PendingIntent pend_intent;
pend_intent = PendingIntent.getService(context, 0, intent, 0); intent.setClass(context, USBPermService.class);
manager.requestPermission(dev, pend_intent); pend_intent = PendingIntent.getService(context, 0, intent, 0);
} manager.requestPermission(dev, pend_intent);
} }
} }
} }
else }
{ else
Log.warning("Cannot request Wiimote adapter permission as EmulationActivity is null."); {
} Log.warning("Cannot request Wiimote adapter permission as EmulationActivity is null.");
} }
}
public static boolean QueryAdapter() public static boolean QueryAdapter()
{ {
HashMap<String, UsbDevice> devices = manager.getDeviceList(); HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
{ {
UsbDevice dev = pair.getValue(); UsbDevice dev = pair.getValue();
if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID) if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID &&
{ dev.getVendorId() == NINTENDO_VENDOR_ID)
if (manager.hasPermission(dev)) {
return true; if (manager.hasPermission(dev))
else return true;
RequestPermission(); else
} RequestPermission();
} }
return false; }
} return false;
}
public static int Input(int index) public static int Input(int index)
{ {
return usb_con.bulkTransfer(usb_in[index], wiimote_payload[index], MAX_PAYLOAD, TIMEOUT); return usb_con.bulkTransfer(usb_in[index], wiimote_payload[index], MAX_PAYLOAD, TIMEOUT);
} }
public static int Output(int index, byte[] buf, int size) public static int Output(int index, byte[] buf, int size)
{ {
byte report_number = buf[0]; byte report_number = buf[0];
// Remove the report number from the buffer // Remove the report number from the buffer
buf = Arrays.copyOfRange(buf, 1, buf.length); buf = Arrays.copyOfRange(buf, 1, buf.length);
size--; size--;
final int LIBUSB_REQUEST_TYPE_CLASS = (1 << 5); final int LIBUSB_REQUEST_TYPE_CLASS = (1 << 5);
final int LIBUSB_RECIPIENT_INTERFACE = 0x1; final int LIBUSB_RECIPIENT_INTERFACE = 0x1;
final int LIBUSB_ENDPOINT_OUT = 0; final int LIBUSB_ENDPOINT_OUT = 0;
final int HID_SET_REPORT = 0x9; final int HID_SET_REPORT = 0x9;
final int HID_OUTPUT = (2 << 8); final int HID_OUTPUT = (2 << 8);
int write = usb_con.controlTransfer( int write = usb_con.controlTransfer(
LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT,
HID_SET_REPORT, HID_SET_REPORT,
HID_OUTPUT | report_number, HID_OUTPUT | report_number,
index, index,
buf, size, buf, size,
1000); 1000);
if (write < 0) if (write < 0)
return 0; return 0;
return write + 1; return write + 1;
} }
public static boolean OpenAdapter() public static boolean OpenAdapter()
{ {
// If the adapter is already open. Don't attempt to do it again // If the adapter is already open. Don't attempt to do it again
if (usb_con != null && usb_con.getFileDescriptor() != -1) if (usb_con != null && usb_con.getFileDescriptor() != -1)
return true; return true;
HashMap<String, UsbDevice> devices = manager.getDeviceList(); HashMap<String, UsbDevice> devices = manager.getDeviceList();
for (Map.Entry<String, UsbDevice> pair : devices.entrySet()) for (Map.Entry<String, UsbDevice> pair : devices.entrySet())
{ {
UsbDevice dev = pair.getValue(); UsbDevice dev = pair.getValue();
if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && dev.getVendorId() == NINTENDO_VENDOR_ID) if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID &&
{ dev.getVendorId() == NINTENDO_VENDOR_ID)
if (manager.hasPermission(dev)) {
{ if (manager.hasPermission(dev))
usb_con = manager.openDevice(dev); {
UsbConfiguration conf = dev.getConfiguration(0); usb_con = manager.openDevice(dev);
UsbConfiguration conf = dev.getConfiguration(0);
Log.info("Number of configurations: " + dev.getConfigurationCount()); Log.info("Number of configurations: " + dev.getConfigurationCount());
Log.info("Number of Interfaces: " + dev.getInterfaceCount()); Log.info("Number of Interfaces: " + dev.getInterfaceCount());
Log.info("Number of Interfaces from conf: " + conf.getInterfaceCount()); Log.info("Number of Interfaces from conf: " + conf.getInterfaceCount());
// Sometimes the interface count is returned as zero. // Sometimes the interface count is returned as zero.
// Means the device needs to be unplugged and plugged back in again // Means the device needs to be unplugged and plugged back in again
if (dev.getInterfaceCount() > 0) if (dev.getInterfaceCount() > 0)
{ {
for (int i = 0; i < MAX_WIIMOTES; ++i) for (int i = 0; i < MAX_WIIMOTES; ++i)
{ {
// One interface per Wii Remote // One interface per Wii Remote
usb_intf[i] = dev.getInterface(i); usb_intf[i] = dev.getInterface(i);
usb_con.claimInterface(usb_intf[i], true); usb_con.claimInterface(usb_intf[i], true);
// One endpoint per Wii Remote. Input only // One endpoint per Wii Remote. Input only
// Output reports go through the control channel. // Output reports go through the control channel.
usb_in[i] = usb_intf[i].getEndpoint(0); usb_in[i] = usb_intf[i].getEndpoint(0);
Log.info("Interface " + i + " endpoint count:" + usb_intf[i].getEndpointCount()); Log.info("Interface " + i + " endpoint count:" + usb_intf[i].getEndpointCount());
} }
return true; return true;
} }
else else
{ {
// XXX: Message that the device was found, but it needs to be unplugged and plugged back in? // XXX: Message that the device was found, but it needs to be unplugged and plugged back in?
usb_con.close(); usb_con.close();
} }
} }
} }
} }
return false; return false;
} }
} }

View File

@ -9,45 +9,45 @@ import org.dolphinemu.dolphinemu.BuildConfig;
*/ */
public final class Log public final class Log
{ {
private static final String TAG = "Dolphin"; private static final String TAG = "Dolphin";
private Log() private Log()
{ {
} }
public static void verbose(String message) public static void verbose(String message)
{ {
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
{ {
android.util.Log.v(TAG, message); android.util.Log.v(TAG, message);
} }
} }
public static void debug(String message) public static void debug(String message)
{ {
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
{ {
android.util.Log.d(TAG, message); android.util.Log.d(TAG, message);
} }
} }
public static void info(String message) public static void info(String message)
{ {
android.util.Log.i(TAG, message); android.util.Log.i(TAG, message);
} }
public static void warning(String message) public static void warning(String message)
{ {
android.util.Log.w(TAG, message); android.util.Log.w(TAG, message);
} }
public static void error(String message) public static void error(String message)
{ {
android.util.Log.e(TAG, message); android.util.Log.e(TAG, message);
} }
public static void wtf(String message) public static void wtf(String message)
{ {
android.util.Log.wtf(TAG, message); android.util.Log.wtf(TAG, message);
} }
} }

View File

@ -14,49 +14,59 @@ import org.dolphinemu.dolphinemu.R;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
public class PermissionsHandler { public class PermissionsHandler
public static final int REQUEST_CODE_WRITE_PERMISSION = 500; {
public static final int REQUEST_CODE_WRITE_PERMISSION = 500;
@TargetApi(Build.VERSION_CODES.M) @TargetApi(Build.VERSION_CODES.M)
public static boolean checkWritePermission(final FragmentActivity activity) { public static boolean checkWritePermission(final FragmentActivity activity)
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { {
return true; if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
} {
return true;
}
int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE); int hasWritePermission = ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE);
if (hasWritePermission != PackageManager.PERMISSION_GRANTED) { if (hasWritePermission != PackageManager.PERMISSION_GRANTED)
if (activity.shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) { {
showMessageOKCancel(activity, activity.getString(R.string.write_permission_needed), if (activity.shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE))
(dialog, which) -> activity.requestPermissions(new String[] {WRITE_EXTERNAL_STORAGE}, {
REQUEST_CODE_WRITE_PERMISSION)); showMessageOKCancel(activity, activity.getString(R.string.write_permission_needed),
return false; (dialog, which) -> activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
} REQUEST_CODE_WRITE_PERMISSION));
return false;
}
activity.requestPermissions(new String[] {WRITE_EXTERNAL_STORAGE}, activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_WRITE_PERMISSION); REQUEST_CODE_WRITE_PERMISSION);
return false; return false;
} }
return true; return true;
} }
public static boolean hasWriteAccess(Context context) { 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); if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
return hasWritePermission == PackageManager.PERMISSION_GRANTED; {
} int hasWritePermission = ContextCompat.checkSelfPermission(context, WRITE_EXTERNAL_STORAGE);
return hasWritePermission == PackageManager.PERMISSION_GRANTED;
}
return true; return true;
} }
private static void showMessageOKCancel(final FragmentActivity activity, String message, DialogInterface.OnClickListener okListener) { private static void showMessageOKCancel(final FragmentActivity activity, String message,
new AlertDialog.Builder(activity) DialogInterface.OnClickListener okListener)
.setMessage(message) {
.setPositiveButton(android.R.string.ok, okListener) new AlertDialog.Builder(activity)
.setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> .setMessage(message)
Toast.makeText(activity, R.string.write_permission_needed, Toast.LENGTH_SHORT).show()) .setPositiveButton(android.R.string.ok, okListener)
.create() .setNegativeButton(android.R.string.cancel, (dialogInterface, i) ->
.show(); Toast.makeText(activity, R.string.write_permission_needed, Toast.LENGTH_SHORT)
} .show())
.create()
.show();
}
} }

View File

@ -12,98 +12,105 @@ import org.dolphinemu.dolphinemu.model.GameFile;
import java.io.File; import java.io.File;
public class PicassoUtils { public class PicassoUtils
public static void loadGameBanner(ImageView imageView, GameFile gameFile) {
{ public static void loadGameBanner(ImageView imageView, GameFile gameFile)
File cover = new File(gameFile.getCustomCoverPath()); {
if (cover.exists()) File cover = new File(gameFile.getCustomCoverPath());
{ if (cover.exists())
Picasso.with(imageView.getContext()) {
.load(cover) Picasso.with(imageView.getContext())
.fit() .load(cover)
.noFade() .fit()
.noPlaceholder() .noFade()
.config(Bitmap.Config.ARGB_8888) .noPlaceholder()
.error(R.drawable.no_banner) .config(Bitmap.Config.ARGB_8888)
.into(imageView); .error(R.drawable.no_banner)
} .into(imageView);
else if ((cover = new File(gameFile.getCoverPath())).exists()) }
{ else if ((cover = new File(gameFile.getCoverPath())).exists())
Picasso.with(imageView.getContext()) {
.load(cover) Picasso.with(imageView.getContext())
.fit() .load(cover)
.noFade() .fit()
.noPlaceholder() .noFade()
.config(Bitmap.Config.ARGB_8888) .noPlaceholder()
.error(R.drawable.no_banner) .config(Bitmap.Config.ARGB_8888)
.into(imageView); .error(R.drawable.no_banner)
} .into(imageView);
/** }
* GameTDB has a pretty close to complete collection for US/EN covers. First pass at getting /**
* the cover will be by the disk's region, second will be the US cover, and third EN. * GameTDB has a pretty close to complete collection for US/EN covers. First pass at getting
*/ * the cover will be by the disk's region, second will be the US cover, and third EN.
else */
{ else
Picasso.with(imageView.getContext()) {
.load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile))) Picasso.with(imageView.getContext())
.fit() .load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile)))
.noFade() .fit()
.noPlaceholder() .noFade()
.config(Bitmap.Config.ARGB_8888) .noPlaceholder()
.error(R.drawable.no_banner) .config(Bitmap.Config.ARGB_8888)
.into(imageView, new Callback() .error(R.drawable.no_banner)
{ .into(imageView, new Callback()
@Override {
public void onSuccess() @Override
{ public void onSuccess()
CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(), {
gameFile.getCoverPath()); CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(),
} gameFile.getCoverPath());
@Override }
public void onError() // Second pass using US region
{ @Override
Picasso.with(imageView.getContext()) public void onError() // Second pass using US region
.load(CoverHelper.buildGameTDBUrl(gameFile, "US")) {
.fit() Picasso.with(imageView.getContext())
.noFade() .load(CoverHelper.buildGameTDBUrl(gameFile, "US"))
.noPlaceholder() .fit()
.config(Bitmap.Config.ARGB_8888) .noFade()
.error(R.drawable.no_banner) .noPlaceholder()
.into(imageView, new Callback() .config(Bitmap.Config.ARGB_8888)
{ .error(R.drawable.no_banner)
@Override .into(imageView, new Callback()
public void onSuccess() {
{ @Override
CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(), public void onSuccess()
gameFile.getCoverPath()); {
} CoverHelper.saveCover(
@Override ((BitmapDrawable) imageView.getDrawable()).getBitmap(),
public void onError() // Third and last pass using EN region gameFile.getCoverPath());
{ }
Picasso.with(imageView.getContext())
.load(CoverHelper.buildGameTDBUrl(gameFile, "EN")) @Override
.fit() public void onError() // Third and last pass using EN region
.noFade() {
.noPlaceholder() Picasso.with(imageView.getContext())
.config(Bitmap.Config.ARGB_8888) .load(CoverHelper.buildGameTDBUrl(gameFile, "EN"))
.error(R.drawable.no_banner) .fit()
.into(imageView, new Callback() .noFade()
{ .noPlaceholder()
@Override .config(Bitmap.Config.ARGB_8888)
public void onSuccess() .error(R.drawable.no_banner)
{ .into(imageView, new Callback()
CoverHelper.saveCover(((BitmapDrawable) imageView.getDrawable()).getBitmap(), {
gameFile.getCoverPath()); @Override
} public void onSuccess()
@Override {
public void onError() CoverHelper.saveCover(
{ ((BitmapDrawable) imageView.getDrawable())
} .getBitmap(),
}); gameFile.getCoverPath());
} }
});
} @Override
}); public void onError()
} {
} }
});
}
});
}
});
}
}
} }

View File

@ -14,61 +14,61 @@ import java.util.Date;
public final class StartupHandler public final class StartupHandler
{ {
public static final String NEW_SESSION = "NEW_SESSION"; public static final String NEW_SESSION = "NEW_SESSION";
public static final String LAST_CLOSED = "LAST_CLOSED"; public static final String LAST_CLOSED = "LAST_CLOSED";
public static final Long SESSION_TIMEOUT = 21600000L; // 6 hours in milliseconds public static final Long SESSION_TIMEOUT = 21600000L; // 6 hours in milliseconds
public static void HandleInit(FragmentActivity parent) public static void HandleInit(FragmentActivity parent)
{ {
// Ask the user to grant write permission if it's not already granted // Ask the user to grant write permission if it's not already granted
PermissionsHandler.checkWritePermission(parent); PermissionsHandler.checkWritePermission(parent);
// Ask the user if he wants to enable analytics if we haven't yet. // Ask the user if he wants to enable analytics if we haven't yet.
Analytics.checkAnalyticsInit(parent); Analytics.checkAnalyticsInit(parent);
String start_file = ""; String start_file = "";
Bundle extras = parent.getIntent().getExtras(); Bundle extras = parent.getIntent().getExtras();
if (extras != null) if (extras != null)
{ {
start_file = extras.getString("AutoStartFile"); start_file = extras.getString("AutoStartFile");
} }
if (!TextUtils.isEmpty(start_file)) if (!TextUtils.isEmpty(start_file))
{ {
// Start the emulation activity, send the ISO passed in and finish the main activity // Start the emulation activity, send the ISO passed in and finish the main activity
Intent emulation_intent = new Intent(parent, EmulationActivity.class); Intent emulation_intent = new Intent(parent, EmulationActivity.class);
emulation_intent.putExtra("SelectedGame", start_file); emulation_intent.putExtra("SelectedGame", start_file);
parent.startActivity(emulation_intent); parent.startActivity(emulation_intent);
parent.finish(); parent.finish();
} }
} }
/** /**
* There isn't a good way to determine a new session. setSessionTime is called if the main * There isn't a good way to determine a new session. setSessionTime is called if the main
* activity goes into the background. * activity goes into the background.
*/ */
public static void setSessionTime(Context context) public static void setSessionTime(Context context)
{ {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor sPrefsEditor = preferences.edit(); SharedPreferences.Editor sPrefsEditor = preferences.edit();
sPrefsEditor.putLong(LAST_CLOSED, new Date(System.currentTimeMillis()).getTime()); sPrefsEditor.putLong(LAST_CLOSED, new Date(System.currentTimeMillis()).getTime());
sPrefsEditor.apply(); sPrefsEditor.apply();
} }
/** /**
* Called to determine if we treat this activity start as a new session. * Called to determine if we treat this activity start as a new session.
*/ */
public static void checkSessionReset(Context context) public static void checkSessionReset(Context context)
{ {
Long currentTime = new Date(System.currentTimeMillis()).getTime(); Long currentTime = new Date(System.currentTimeMillis()).getTime();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Long lastOpen = preferences.getLong(LAST_CLOSED, 0); Long lastOpen = preferences.getLong(LAST_CLOSED, 0);
if (currentTime > (lastOpen + SESSION_TIMEOUT)) if (currentTime > (lastOpen + SESSION_TIMEOUT))
{ {
// Passed at emulation start to trigger first open event. // Passed at emulation start to trigger first open event.
SharedPreferences.Editor sPrefsEditor = preferences.edit(); SharedPreferences.Editor sPrefsEditor = preferences.edit();
sPrefsEditor.putBoolean(NEW_SESSION, true); sPrefsEditor.putBoolean(NEW_SESSION, true);
sPrefsEditor.apply(); sPrefsEditor.apply();
} }
} }
} }

View File

@ -17,7 +17,6 @@ import android.graphics.drawable.VectorDrawable;
import android.media.tv.TvContract; import android.media.tv.TvContract;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Environment;
import android.os.PersistableBundle; import android.os.PersistableBundle;
import android.support.annotation.AnyRes; import android.support.annotation.AnyRes;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
@ -25,7 +24,6 @@ import android.support.media.tv.Channel;
import android.support.media.tv.TvContractCompat; import android.support.media.tv.TvContractCompat;
import android.util.Log; import android.util.Log;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.GameFile; import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.model.HomeScreenChannel; import org.dolphinemu.dolphinemu.model.HomeScreenChannel;
import org.dolphinemu.dolphinemu.services.SyncChannelJobService; import org.dolphinemu.dolphinemu.services.SyncChannelJobService;
@ -33,7 +31,6 @@ import org.dolphinemu.dolphinemu.services.SyncProgramsJobService;
import org.dolphinemu.dolphinemu.ui.platform.Platform; import org.dolphinemu.dolphinemu.ui.platform.Platform;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -45,232 +42,234 @@ import static android.support.v4.content.FileProvider.getUriForFile;
*/ */
public class TvUtil public class TvUtil
{ {
private static final String TAG = "TvUtil"; private static final String TAG = "TvUtil";
private static final long CHANNEL_JOB_ID_OFFSET = 1000; private static final long CHANNEL_JOB_ID_OFFSET = 1000;
private static final String[] CHANNELS_PROJECTION = { private static final String[] CHANNELS_PROJECTION = {
TvContractCompat.Channels._ID, TvContractCompat.Channels._ID,
TvContract.Channels.COLUMN_DISPLAY_NAME, TvContract.Channels.COLUMN_DISPLAY_NAME,
TvContractCompat.Channels.COLUMN_BROWSABLE TvContractCompat.Channels.COLUMN_BROWSABLE
}; };
private static final String LEANBACK_PACKAGE = "com.google.android.tvlauncher"; private static final String LEANBACK_PACKAGE = "com.google.android.tvlauncher";
public static int getNumberOfChannels(Context context) public static int getNumberOfChannels(Context context)
{ {
Cursor cursor = Cursor cursor =
context.getContentResolver() context.getContentResolver()
.query( .query(
TvContractCompat.Channels.CONTENT_URI, TvContractCompat.Channels.CONTENT_URI,
CHANNELS_PROJECTION, CHANNELS_PROJECTION,
null, null,
null, null,
null); null);
return cursor != null ? cursor.getCount() : 0; return cursor != null ? cursor.getCount() : 0;
} }
public static List<Channel> getAllChannels(Context context) public static List<Channel> getAllChannels(Context context)
{ {
List<Channel> channels = new ArrayList<>(); List<Channel> channels = new ArrayList<>();
Cursor cursor = Cursor cursor =
context.getContentResolver() context.getContentResolver()
.query( .query(
TvContractCompat.Channels.CONTENT_URI, TvContractCompat.Channels.CONTENT_URI,
CHANNELS_PROJECTION, CHANNELS_PROJECTION,
null, null,
null, null,
null); null);
if (cursor != null && cursor.moveToFirst()) if (cursor != null && cursor.moveToFirst())
{ {
do do
{ {
channels.add(Channel.fromCursor(cursor)); channels.add(Channel.fromCursor(cursor));
} while (cursor.moveToNext()); }
} while (cursor.moveToNext());
return channels; }
} return channels;
}
public static Channel getChannelById(Context context, long channelId) public static Channel getChannelById(Context context, long channelId)
{ {
for (Channel channel : getAllChannels(context)) for (Channel channel : getAllChannels(context))
{ {
if (channel.getId() == channelId) if (channel.getId() == channelId)
{ {
return channel; return channel;
} }
} }
return null; return null;
} }
/** /**
* Updates all Leanback homescreen channels * Updates all Leanback homescreen channels
*/ */
public static void updateAllChannels(Context context) public static void updateAllChannels(Context context)
{ {
if (Build.VERSION.SDK_INT < 26) if (Build.VERSION.SDK_INT < 26)
return; return;
for (Channel channel : getAllChannels(context)) for (Channel channel : getAllChannels(context))
{ {
context.getContentResolver() context.getContentResolver()
.update( .update(
TvContractCompat.buildChannelUri(channel.getId()), TvContractCompat.buildChannelUri(channel.getId()),
channel.toContentValues(), channel.toContentValues(),
null, null,
null); null);
} }
} }
public static Uri getUriToResource(Context context, @AnyRes int resId) public static Uri getUriToResource(Context context, @AnyRes int resId)
throws Resources.NotFoundException throws Resources.NotFoundException
{ {
Resources res = context.getResources(); Resources res = context.getResources();
Uri resUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + Uri resUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE +
"://" + res.getResourcePackageName(resId) "://" + res.getResourcePackageName(resId)
+ '/' + res.getResourceTypeName(resId) + '/' + res.getResourceTypeName(resId)
+ '/' + res.getResourceEntryName(resId)); + '/' + res.getResourceEntryName(resId));
return resUri; return resUri;
} }
/** /**
* Converts a resource into a {@link Bitmap}. If the resource is a vector drawable, it will be * Converts a resource into a {@link Bitmap}. If the resource is a vector drawable, it will be
* drawn into a new Bitmap. Otherwise the {@link BitmapFactory} will decode the resource. * drawn into a new Bitmap. Otherwise the {@link BitmapFactory} will decode the resource.
*/ */
@NonNull @NonNull
public static Bitmap convertToBitmap(Context context, int resourceId) public static Bitmap convertToBitmap(Context context, int resourceId)
{ {
Drawable drawable = context.getDrawable(resourceId); Drawable drawable = context.getDrawable(resourceId);
if (drawable instanceof VectorDrawable) if (drawable instanceof VectorDrawable)
{ {
Bitmap bitmap = Bitmap bitmap =
Bitmap.createBitmap( Bitmap.createBitmap(
drawable.getIntrinsicWidth(), drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight(), drawable.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888); Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap); Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas); drawable.draw(canvas);
return bitmap; return bitmap;
} }
return BitmapFactory.decodeResource(context.getResources(), resourceId); return BitmapFactory.decodeResource(context.getResources(), resourceId);
} }
/** /**
* Leanback lanucher requires a uri for poster art so we create a contentUri and * Leanback lanucher requires a uri for poster art so we create a contentUri and
* pass that to LEANBACK_PACKAGE * pass that to LEANBACK_PACKAGE
*/ */
public static Uri buildBanner(GameFile game, Context context) public static Uri buildBanner(GameFile game, Context context)
{ {
Uri contentUri = null; Uri contentUri = null;
try try
{ {
File cover = new File(game.getCustomCoverPath()); File cover = new File(game.getCustomCoverPath());
if(cover.exists()) if (cover.exists())
{ {
contentUri = getUriForFile(context, getFileProvider(context), cover); contentUri = getUriForFile(context, getFileProvider(context), cover);
} }
else if ((cover = new File(game.getCoverPath())).exists()) else if ((cover = new File(game.getCoverPath())).exists())
{ {
contentUri = getUriForFile(context, getFileProvider(context), cover); contentUri = getUriForFile(context, getFileProvider(context), cover);
} }
context.grantUriPermission(LEANBACK_PACKAGE, contentUri, context.grantUriPermission(LEANBACK_PACKAGE, contentUri,
FLAG_GRANT_READ_URI_PERMISSION); FLAG_GRANT_READ_URI_PERMISSION);
} }
catch (Exception e) catch (Exception e)
{ {
Log.e(TAG, "Failed to create banner"); Log.e(TAG, "Failed to create banner");
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());
} }
return contentUri; return contentUri;
} }
/** /**
* Needed since debug builds append '.debug' to the end of the package * Needed since debug builds append '.debug' to the end of the package
*/ */
private static String getFileProvider(Context context) private static String getFileProvider(Context context)
{ {
return context.getPackageName() + ".filesprovider"; return context.getPackageName() + ".filesprovider";
} }
/** /**
* Schedules syncing channels via a {@link JobScheduler}. * Schedules syncing channels via a {@link JobScheduler}.
* *
* @param context for accessing the {@link JobScheduler}. * @param context for accessing the {@link JobScheduler}.
*/ */
public static void scheduleSyncingChannel(Context context) public static void scheduleSyncingChannel(Context context)
{ {
ComponentName componentName = new ComponentName(context, SyncChannelJobService.class); ComponentName componentName = new ComponentName(context, SyncChannelJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(1, componentName); JobInfo.Builder builder = new JobInfo.Builder(1, componentName);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
JobScheduler scheduler = JobScheduler scheduler =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
Log.d(TAG, "Scheduled channel creation."); Log.d(TAG, "Scheduled channel creation.");
scheduler.schedule(builder.build()); scheduler.schedule(builder.build());
} }
/** /**
* Schedulers syncing programs for a channel. The scheduler will listen to a {@link Uri} for a * Schedulers syncing programs for a channel. The scheduler will listen to a {@link Uri} for a
* particular channel. * particular channel.
* *
* @param context for accessing the {@link JobScheduler}. * @param context for accessing the {@link JobScheduler}.
* @param channelId for the channel to listen for changes. * @param channelId for the channel to listen for changes.
*/ */
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
public static void scheduleSyncingProgramsForChannel(Context context, long channelId) public static void scheduleSyncingProgramsForChannel(Context context, long channelId)
{ {
Log.d(TAG, "ProgramsRefresh job"); Log.d(TAG, "ProgramsRefresh job");
ComponentName componentName = new ComponentName(context, SyncProgramsJobService.class); ComponentName componentName = new ComponentName(context, SyncProgramsJobService.class);
JobInfo.Builder builder = JobInfo.Builder builder =
new JobInfo.Builder(getJobIdForChannelId(channelId), componentName); new JobInfo.Builder(getJobIdForChannelId(channelId), componentName);
JobInfo.TriggerContentUri triggerContentUri = JobInfo.TriggerContentUri triggerContentUri =
new JobInfo.TriggerContentUri( new JobInfo.TriggerContentUri(
TvContractCompat.buildChannelUri(channelId), TvContractCompat.buildChannelUri(channelId),
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS);
builder.addTriggerContentUri(triggerContentUri); builder.addTriggerContentUri(triggerContentUri);
builder.setTriggerContentMaxDelay(0L); builder.setTriggerContentMaxDelay(0L);
builder.setTriggerContentUpdateDelay(0L); builder.setTriggerContentUpdateDelay(0L);
PersistableBundle bundle = new PersistableBundle(); PersistableBundle bundle = new PersistableBundle();
bundle.putLong(TvContractCompat.EXTRA_CHANNEL_ID, channelId); bundle.putLong(TvContractCompat.EXTRA_CHANNEL_ID, channelId);
builder.setExtras(bundle); builder.setExtras(bundle);
JobScheduler scheduler = JobScheduler scheduler =
(JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.cancel(getJobIdForChannelId(channelId)); scheduler.cancel(getJobIdForChannelId(channelId));
scheduler.schedule(builder.build()); scheduler.schedule(builder.build());
} }
private static int getJobIdForChannelId(long channelId) private static int getJobIdForChannelId(long channelId)
{ {
return (int) (CHANNEL_JOB_ID_OFFSET + channelId); return (int) (CHANNEL_JOB_ID_OFFSET + channelId);
} }
/** /**
* Generates all subscriptions for homescreen channels. * Generates all subscriptions for homescreen channels.
*/ */
public static List<HomeScreenChannel> createUniversalSubscriptions() public static List<HomeScreenChannel> createUniversalSubscriptions()
{ {
//Leaving the subs local variable in case more channels are created other than platforms. //Leaving the subs local variable in case more channels are created other than platforms.
List<HomeScreenChannel> subs = new ArrayList<>(createPlatformSubscriptions()); List<HomeScreenChannel> subs = new ArrayList<>(createPlatformSubscriptions());
return subs; return subs;
} }
private static List<HomeScreenChannel> createPlatformSubscriptions() private static List<HomeScreenChannel> createPlatformSubscriptions()
{ {
List<HomeScreenChannel> subs = new ArrayList<>(); List<HomeScreenChannel> subs = new ArrayList<>();
for (Platform platform : Platform.values()) for (Platform platform : Platform.values())
{ {
subs.add(new HomeScreenChannel( subs.add(new HomeScreenChannel(
platform.getHeaderName(), platform.getHeaderName(),
platform.getHeaderName(), platform.getHeaderName(),
AppLinkHelper.buildBrowseUri(platform.getHeaderName()).toString())); AppLinkHelper.buildBrowseUri(platform.getHeaderName()).toString()));
} }
return subs; return subs;
} }
public static Boolean isLeanback(Context context)
{ public static Boolean isLeanback(Context context)
return(context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)); {
} return (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK));
}
} }

View File

@ -7,16 +7,16 @@ import com.android.volley.toolbox.Volley;
public class VolleyUtil public class VolleyUtil
{ {
private static RequestQueue queue; private static RequestQueue queue;
public static void init(Context context) public static void init(Context context)
{ {
if (queue == null) if (queue == null)
queue = Volley.newRequestQueue(context); queue = Volley.newRequestQueue(context);
} }
public static RequestQueue getQueue() public static RequestQueue getQueue()
{ {
return queue; return queue;
} }
} }

View File

@ -13,18 +13,18 @@ import org.dolphinemu.dolphinemu.R;
*/ */
public class FileViewHolder extends RecyclerView.ViewHolder public class FileViewHolder extends RecyclerView.ViewHolder
{ {
public View itemView; public View itemView;
public TextView textFileName; public TextView textFileName;
public ImageView imageType; public ImageView imageType;
public FileViewHolder(View itemView) public FileViewHolder(View itemView)
{ {
super(itemView); super(itemView);
this.itemView = itemView; this.itemView = itemView;
textFileName = (TextView) itemView.findViewById(R.id.text_file_name); textFileName = (TextView) itemView.findViewById(R.id.text_file_name);
imageType = (ImageView) itemView.findViewById(R.id.image_type); imageType = (ImageView) itemView.findViewById(R.id.image_type);
} }
} }

View File

@ -14,20 +14,20 @@ import org.dolphinemu.dolphinemu.model.GameFile;
*/ */
public class GameViewHolder extends RecyclerView.ViewHolder public class GameViewHolder extends RecyclerView.ViewHolder
{ {
public ImageView imageScreenshot; public ImageView imageScreenshot;
public TextView textGameTitle; public TextView textGameTitle;
public TextView textCompany; public TextView textCompany;
public GameFile gameFile; public GameFile gameFile;
public GameViewHolder(View itemView) public GameViewHolder(View itemView)
{ {
super(itemView); super(itemView);
itemView.setTag(this); itemView.setTag(this);
imageScreenshot = itemView.findViewById(R.id.image_game_screen); imageScreenshot = itemView.findViewById(R.id.image_game_screen);
textGameTitle = itemView.findViewById(R.id.text_game_title); textGameTitle = itemView.findViewById(R.id.text_game_title);
textCompany = itemView.findViewById(R.id.text_company); textCompany = itemView.findViewById(R.id.text_company);
} }
} }

View File

@ -13,19 +13,19 @@ import org.dolphinemu.dolphinemu.model.GameFile;
*/ */
public final class TvGameViewHolder extends Presenter.ViewHolder public final class TvGameViewHolder extends Presenter.ViewHolder
{ {
public ImageCardView cardParent; public ImageCardView cardParent;
public ImageView imageScreenshot; public ImageView imageScreenshot;
public GameFile gameFile; public GameFile gameFile;
public TvGameViewHolder(View itemView) public TvGameViewHolder(View itemView)
{ {
super(itemView); super(itemView);
itemView.setTag(this); itemView.setTag(this);
cardParent = (ImageCardView) itemView; cardParent = (ImageCardView) itemView;
imageScreenshot = cardParent.getMainImageView(); imageScreenshot = cardParent.getMainImageView();
} }
} }

View File

@ -6,17 +6,17 @@ import android.view.View;
public final class TvSettingsViewHolder extends Presenter.ViewHolder public final class TvSettingsViewHolder extends Presenter.ViewHolder
{ {
public ImageCardView cardParent; public ImageCardView cardParent;
// Determines what action to take when this item is clicked. // Determines what action to take when this item is clicked.
public int itemId; public int itemId;
public TvSettingsViewHolder(View itemView) public TvSettingsViewHolder(View itemView)
{ {
super(itemView); super(itemView);
itemView.setTag(this); itemView.setTag(this);
cardParent = (ImageCardView) itemView; cardParent = (ImageCardView) itemView;
} }
} }

View File

@ -3,19 +3,19 @@
<!-- This animation is used ONLY when a submenu is replaced. --> <!-- This animation is used ONLY when a submenu is replaced. -->
<objectAnimator <objectAnimator
android:propertyName="translationX" android:propertyName="translationX"
android:valueType="floatType" android:valueType="floatType"
android:valueFrom="0" android:valueFrom="0"
android:valueTo="-1280dp" android:valueTo="-1280dp"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="200"/> android:duration="200"/>
<objectAnimator <objectAnimator
android:propertyName="alpha" android:propertyName="alpha"
android:valueType="floatType" android:valueType="floatType"
android:valueFrom="1" android:valueFrom="1"
android:valueTo="0" android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="200"/> android:duration="200"/>
</set> </set>

View File

@ -3,19 +3,19 @@
<!-- This animation is used ONLY when a submenu is replaced. --> <!-- This animation is used ONLY when a submenu is replaced. -->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="translationX" android:propertyName="translationX"
android:valueType="floatType" android:valueType="floatType"
android:valueFrom="0" android:valueFrom="0"
android:valueTo="1280dp" android:valueTo="1280dp"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="200"/> android:duration="200"/>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="alpha" android:propertyName="alpha"
android:valueType="floatType" android:valueType="floatType"
android:valueFrom="1" android:valueFrom="1"
android:valueTo="0" android:valueTo="0"
android:interpolator="@android:interpolator/decelerate_quad" android:interpolator="@android:interpolator/decelerate_quad"
android:duration="200"/> android:duration="200"/>
</set> </set>

View File

@ -2,7 +2,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:state_selected="true" android:state_selected="true"
android:drawable="@color/dolphin_accent_gamecube" /> android:drawable="@color/dolphin_accent_gamecube"/>
<item <item
android:drawable="@color/tv_card_unselected" /> android:drawable="@color/tv_card_unselected"/>
</selector> </selector>

View File

@ -2,7 +2,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:state_selected="true" android:state_selected="true"
android:drawable="@color/dolphin_accent_wii" /> android:drawable="@color/dolphin_accent_wii"/>
<item <item
android:drawable="@color/tv_card_unselected" /> android:drawable="@color/tv_card_unselected"/>
</selector> </selector>

View File

@ -2,7 +2,7 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:state_selected="true" android:state_selected="true"
android:drawable="@color/dolphin_accent_wiiware" /> android:drawable="@color/dolphin_accent_wiiware"/>
<item <item
android:drawable="@color/tv_card_unselected" /> android:drawable="@color/tv_card_unselected"/>
</selector> </selector>

View File

@ -1,7 +1,7 @@
<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android" <SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/surface_emulation" android:id="@+id/surface_emulation"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:focusable="false" android:focusable="false"
android:focusableInTouchMode="false" android:focusableInTouchMode="false"
/> />

View File

@ -1,11 +1,11 @@
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android" <GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/grid_state_slots" android:id="@+id/grid_state_slots"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:columnCount="3" android:columnCount="3"
android:rowCount="2" android:rowCount="2"
android:layout_gravity="center" android:layout_gravity="center"
android:background="#af000000"> android:background="#af000000">
<Button <Button
android:id="@+id/loadsave_state_button_1" android:id="@+id/loadsave_state_button_1"

Some files were not shown because too many files have changed in this diff Show More