diff --git a/Android/app/src/main/AndroidManifest.xml b/Android/app/src/main/AndroidManifest.xml index 96e24cb5a..b47792f2c 100644 --- a/Android/app/src/main/AndroidManifest.xml +++ b/Android/app/src/main/AndroidManifest.xml @@ -44,6 +44,11 @@ android:label="@string/ScanRomsActivity_title" android:theme="@style/Theme.Project64.Apearance" > + + getUnmappableKeyCodes () + { + List unmappables = new ArrayList(); + unmappables.add( KeyEvent.KEYCODE_MENU ); + if( IS_HONEYCOMB ) + { + // Back key is needed to show/hide the action bar in HC+ + unmappables.add( KeyEvent.KEYCODE_BACK ); + } + if( !MapVolumeKeys ) + { + unmappables.add( KeyEvent.KEYCODE_VOLUME_UP ); + unmappables.add( KeyEvent.KEYCODE_VOLUME_DOWN ); + unmappables.add( KeyEvent.KEYCODE_VOLUME_MUTE ); + } + return unmappables; + } + public static ArrayList getStorageDirectories() { BufferedReader bufReader = null; diff --git a/Android/app/src/main/java/emu/project64/game/GameLifecycleHandler.java b/Android/app/src/main/java/emu/project64/game/GameLifecycleHandler.java index 8583b0f60..04486414a 100644 --- a/Android/app/src/main/java/emu/project64/game/GameLifecycleHandler.java +++ b/Android/app/src/main/java/emu/project64/game/GameLifecycleHandler.java @@ -11,9 +11,7 @@ import java.util.ArrayList; import java.util.Set; import emu.project64.AndroidDevice; -import emu.project64.Project64Application; import emu.project64.R; -import emu.project64.hack.MogaHack; import emu.project64.input.AbstractController; import emu.project64.input.PeripheralController; import emu.project64.input.TouchController; @@ -23,12 +21,8 @@ import emu.project64.input.provider.AbstractProvider; import emu.project64.input.provider.AxisProvider; import emu.project64.input.provider.KeyProvider; import emu.project64.input.provider.KeyProvider.ImeFormula; -import emu.project64.input.provider.MogaProvider; import emu.project64.jni.NativeExports; import emu.project64.jni.NativeVideo; -import emu.project64.jni.NativeXperiaTouchpad; -import emu.project64.jni.SettingsID; -import emu.project64.jni.SystemEvent; import emu.project64.jni.UISettingID; import emu.project64.jni.VideoSettingID; import emu.project64.persistent.ConfigFile; @@ -39,19 +33,16 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.content.Context; -import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.Vibrator; import android.util.Log; -import android.view.Display; import android.view.Gravity; import android.view.KeyEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.Window; -import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.FrameLayout; @@ -69,7 +60,6 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C private ArrayList mControllers; private VisibleTouchMap mTouchscreenMap; private KeyProvider mKeyProvider; - private Controller mMogaController; // Internal flags private boolean mStarted = false; @@ -87,20 +77,14 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C { mActivity = activity; mControllers = new ArrayList(); - mMogaController = Controller.getInstance(mActivity); } - @TargetApi(11) public void onCreateBegin(Bundle savedInstanceState) { if (LOG_GAMELIFECYCLEHANDLER) { Log.i("GameLifecycleHandler", "onCreateBegin - Start"); } - // Initialize MOGA controller API - // TODO: Remove hack after MOGA SDK is fixed - // mMogaController.init(); - MogaHack.init(mMogaController, mActivity); // For Honeycomb, let the action bar overlay the rendered view (rather // than squeezing it) @@ -122,7 +106,6 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C } } - @TargetApi(11) public void onCreateEnd(Bundle savedInstanceState) { if (LOG_GAMELIFECYCLEHANDLER) @@ -157,13 +140,22 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C mSurface.getHolder().addCallback(this); mSurface.createGLContext((ActivityManager) mActivity.getSystemService(Context.ACTIVITY_SERVICE)); - //CreateTouchScreenControls(); - //View inputSource = mOverlay; - //initControllers(inputSource); + // Configure the action bar introduced in higher Android versions + /*if (AndroidDevice.IS_ACTION_BAR_AVAILABLE) + { + mActivity.getActionBar().hide(); + ColorDrawable color = new ColorDrawable(Color.parseColor("#303030")); + color.setAlpha(50 /*mGlobalPrefs.displayActionBarTransparency*/ /*); + mActivity.getActionBar().setBackgroundDrawable(color); + } - // Override the peripheral controllers' key provider, to add some extra - // functionality - //inputSource.setOnKeyListener(this); + CreateTouchScreenControls();*/ + + // Initialize user interface devices + initControllers(mOverlay); + + // Override the peripheral controllers' key provider, to add some extra functionality + mOverlay.setOnKeyListener(this); if (LOG_GAMELIFECYCLEHANDLER) { Log.i("GameLifecycleHandler", "onCreateEnd done"); @@ -186,8 +178,6 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C } mIsResumed = true; tryRunning(); - - mMogaController.onResume(); } @Override @@ -325,7 +315,6 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C { Log.i("GameLifecycleHandler", "onDestroy"); } - mMogaController.exit(); } public void onSettingDone() @@ -343,30 +332,27 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C private void CreateTouchScreenControls() { - /*boolean isTouchscreenAnimated = false; // mGlobalPrefs.isTouchscreenAnimated - boolean isTouchscreenHidden = false; // !isTouchscreenEnabled || - // globalPrefs.touchscreenTransparency - // == 0; + boolean isTouchscreenAnimated = false; //mGlobalPrefs.isTouchscreenAnimated + boolean isTouchscreenHidden = false; //!isTouchscreenEnabled || globalPrefs.touchscreenTransparency == 0; String profilesDir = AndroidDevice.PACKAGE_DIRECTORY + "/profiles"; String touchscreenProfiles_cfg = profilesDir + "/touchscreen.cfg"; - ConfigFile touchscreenConfigFile = new ConfigFile(touchscreenProfiles_cfg); + ConfigFile touchscreenConfigFile = new ConfigFile( touchscreenProfiles_cfg ); ConfigSection section = touchscreenConfigFile.get(mlayout); if (section == null) { mlayout = "Analog"; section = touchscreenConfigFile.get(mlayout); } - Profile touchscreenProfile = new Profile(true, section); + Profile touchscreenProfile = new Profile( true, section); int touchscreenTransparency = 100; String touchscreenSkinsDir = AndroidDevice.PACKAGE_DIRECTORY + "/skins/touchscreen"; String touchscreenSkin = touchscreenSkinsDir + "/Outline"; - // The touch map and overlay are needed to display frame rate and/or - // controls - mTouchscreenMap = new VisibleTouchMap(mActivity.getResources()); - mTouchscreenMap.load(touchscreenSkin, touchscreenProfile, isTouchscreenAnimated, mtouchscreenScale, touchscreenTransparency); - mOverlay.initialize(mTouchscreenMap, !isTouchscreenHidden, isTouchscreenAnimated);*/ - } + // The touch map and overlay are needed to display frame rate and/or controls + mTouchscreenMap = new VisibleTouchMap( mActivity.getResources() ); + mTouchscreenMap.load(touchscreenSkin, touchscreenProfile, isTouchscreenAnimated, mtouchscreenScale, touchscreenTransparency ); + mOverlay.initialize( mTouchscreenMap, !isTouchscreenHidden, isTouchscreenAnimated ); + } @Override public boolean onKey(View view, int keyCode, KeyEvent event) @@ -385,33 +371,32 @@ public class GameLifecycleHandler implements View.OnKeyListener, SurfaceHolder.C private void initControllers(View inputSource) { // By default, send Player 1 rumbles through phone vibrator - /*Vibrator vibrator = (Vibrator) mActivity.getSystemService(Context.VIBRATOR_SERVICE); + Vibrator vibrator = (Vibrator) mActivity.getSystemService( Context.VIBRATOR_SERVICE ); int touchscreenAutoHold = 0; boolean isTouchscreenFeedbackEnabled = false; Set autoHoldableButtons = null; // Create the touchscreen controller - TouchController touchscreenController = new TouchController(mTouchscreenMap, inputSource, mOverlay, vibrator, - touchscreenAutoHold, isTouchscreenFeedbackEnabled, autoHoldableButtons); - mControllers.add(touchscreenController); + TouchController touchscreenController = new TouchController( mTouchscreenMap, + inputSource, mOverlay, vibrator, touchscreenAutoHold, + isTouchscreenFeedbackEnabled, autoHoldableButtons ); + mControllers.add( touchscreenController ); // Create the input providers shared among all peripheral controllers - String profile_name = NativeExports.UISettingsLoadString(UISettingID.Controller_CurrentProfile.getValue()); - ConfigFile ControllerConfigFile = new ConfigFile( - NativeExports.UISettingsLoadString(UISettingID.Controller_ConfigFile.getValue())); - ConfigSection section = ControllerConfigFile.get(profile_name); + String profile_name = NativeExports.SettingsLoadString(UISettingID.ControllerCurrentProfile.toString()); + ConfigFile ControllerConfigFile = new ConfigFile(NativeExports.SettingsLoadString(UISettingID.ControllerConfigFile.toString())); + ConfigSection section = ControllerConfigFile.get( profile_name ); if (section != null) { - Profile ControllerProfile = new Profile(false, section); - InputMap map = new InputMap(ControllerProfile.get("map")); + Profile ControllerProfile = new Profile( false, section ); + InputMap map = new InputMap( ControllerProfile.get( "map" ) ); - mKeyProvider = new KeyProvider(inputSource, ImeFormula.DEFAULT, AndroidDevice.getUnmappableKeyCodes()); - MogaProvider mogaProvider = new MogaProvider(mMogaController); - AbstractProvider axisProvider = AndroidDevice.IS_HONEYCOMB_MR1 ? new AxisProvider(inputSource) : null; - int Deadzone = NativeExports.UISettingsLoadDword(UISettingID.Controller_Deadzone.getValue()); - int Sensitivity = NativeExports.UISettingsLoadDword(UISettingID.Controller_Sensitivity.getValue()); - mControllers.add(new PeripheralController(1, map, Deadzone, Sensitivity, mKeyProvider, axisProvider, mogaProvider)); - }*/ + mKeyProvider = new KeyProvider( inputSource, ImeFormula.DEFAULT, AndroidDevice.getUnmappableKeyCodes() ); + AbstractProvider axisProvider = AndroidDevice.IS_HONEYCOMB_MR1 ? new AxisProvider( inputSource ) : null; + int Deadzone = NativeExports.SettingsLoadDword(UISettingID.ControllerDeadzone.toString()); + int Sensitivity = NativeExports.SettingsLoadDword(UISettingID.ControllerSensitivity.toString()); + mControllers.add(new PeripheralController(1, map, Deadzone, Sensitivity, mKeyProvider, axisProvider)); + } } private void tryRunning() diff --git a/Android/app/src/main/java/emu/project64/profile/ControllerProfileActivity.java b/Android/app/src/main/java/emu/project64/profile/ControllerProfileActivity.java new file mode 100644 index 000000000..eb308f74a --- /dev/null +++ b/Android/app/src/main/java/emu/project64/profile/ControllerProfileActivity.java @@ -0,0 +1,471 @@ +package emu.project64.profile; + +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.PorterDuff; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.DisplayMetrics; +import android.view.KeyEvent; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import java.util.ArrayList; + +import emu.project64.AndroidDevice; +import emu.project64.R; +import emu.project64.input.AbstractController; +import emu.project64.input.map.InputMap; +import emu.project64.input.provider.AbstractProvider; +import emu.project64.input.provider.AxisProvider; +import emu.project64.input.provider.KeyProvider; +import emu.project64.jni.NativeExports; +import emu.project64.jni.UISettingID; +import emu.project64.persistent.ConfigFile; + +public class ControllerProfileActivity extends AppCompatActivity implements AbstractProvider.OnInputListener, View.OnClickListener +{ + // Visual settings + private static final float UNMAPPED_BUTTON_ALPHA = 0.2f; + private static final int UNMAPPED_BUTTON_FILTER = 0x66FFFFFF; + private static final int MIN_LAYOUT_WIDTH_DP = 480; + + // Controller profile objects + private ConfigFile mConfigFile; + private Profile mProfile; + + // Input listening + private KeyProvider mKeyProvider; + private AxisProvider mAxisProvider; + + // Widgets + private final Button[] mN64Buttons = new Button[InputMap.NUM_MAPPABLES]; + private TextView mFeedbackText; + + @Override + public void onCreate( Bundle savedInstanceState ) + { + super.onCreate(savedInstanceState); + + // Load the profile; fail fast if there are any programmer usage errors + String name = NativeExports.SettingsLoadString(UISettingID.ControllerCurrentProfile.toString()); + if(TextUtils.isEmpty(name)) + { + throw new Error("Invalid usage: profile name cannot be null or empty"); + } + mConfigFile = new ConfigFile(NativeExports.SettingsLoadString(UISettingID.ControllerConfigFile.toString())); + ConfigFile.ConfigSection Section = mConfigFile.get( name ); + if (Section == null) + { + //profile not found create it + mConfigFile.put(name, "map", "-"); + Section = mConfigFile.get( name ); + if(Section == null) + { + throw new Error( "Invalid usage: profile name not found in config file" ); + } + } + mProfile = new Profile(false, Section); + + // Set up input listeners + mKeyProvider = new KeyProvider( KeyProvider.ImeFormula.DEFAULT, AndroidDevice.getUnmappableKeyCodes() ); + mKeyProvider.registerListener( this ); + if( AndroidDevice.IS_HONEYCOMB_MR1 ) + { + mAxisProvider = new AxisProvider(); + mAxisProvider.registerListener( this ); + } + + // Initialize the layout + initLayoutDefault(); + + // Refresh everything + refreshAllButtons(); + } + + @Override + public void onResume() + { + super.onResume(); + } + + @Override + public void onPause() + { + super.onPause(); + + // Lazily persist the profile data; only need to do it on pause + mProfile.writeTo( mConfigFile ); + mConfigFile.save(); + } + + @Override + public void onDestroy() + { + super.onDestroy(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + if (item.getItemId() == android.R.id.home) + { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() + { + finish(); + } + + private void initLayoutDefault() + { + WindowManager manager = (WindowManager) getSystemService( Context.WINDOW_SERVICE ); + DisplayMetrics metrics = new DisplayMetrics(); + manager.getDefaultDisplay().getMetrics( metrics ); + float ScaleFactor = (float) DisplayMetrics.DENSITY_DEFAULT / (float) metrics.densityDpi; + int widthDp = Math.round( metrics.widthPixels * ScaleFactor ); + + // For narrow screens, use an alternate layout + if( widthDp < MIN_LAYOUT_WIDTH_DP ) + { + setContentView( R.layout.controller_profile_activity_port ); + } + else + { + setContentView( R.layout.controller_profile_activity ); + } + + // Add the tool bar to the activity (which supports the fancy menu/arrow animation) + Toolbar toolbar = findViewById( R.id.toolbar ); + toolbar.setTitle( getString(R.string.gamepad_title) ); + setSupportActionBar( toolbar ); + ActionBar actionbar = getSupportActionBar(); + + if (AndroidDevice.IS_ICE_CREAM_SANDWICH) + { + actionbar.setHomeButtonEnabled(true); + actionbar.setDisplayHomeAsUpEnabled(true); + } + + // Initialize and refresh the widgets + initWidgets(); + } + + private void initWidgets() + { + // Get the text view object + mFeedbackText = findViewById( R.id.textFeedback ); + mFeedbackText.setText( "" ); + + // Create a button list to simplify highlighting and mapping + setupButton( R.id.buttonDR, AbstractController.DPD_R ); + setupButton( R.id.buttonDL, AbstractController.DPD_L ); + setupButton( R.id.buttonDD, AbstractController.DPD_D ); + setupButton( R.id.buttonDU, AbstractController.DPD_U ); + setupButton( R.id.buttonS, AbstractController.START ); + setupButton( R.id.buttonZ, AbstractController.BTN_Z ); + setupButton( R.id.buttonB, AbstractController.BTN_B ); + setupButton( R.id.buttonA, AbstractController.BTN_A ); + setupButton( R.id.buttonCR, AbstractController.CPD_R ); + setupButton( R.id.buttonCL, AbstractController.CPD_L ); + setupButton( R.id.buttonCD, AbstractController.CPD_D ); + setupButton( R.id.buttonCU, AbstractController.CPD_U ); + setupButton( R.id.buttonR, AbstractController.BTN_R ); + setupButton( R.id.buttonL, AbstractController.BTN_L ); + setupButton( R.id.buttonAR, InputMap.AXIS_R ); + setupButton( R.id.buttonAL, InputMap.AXIS_L ); + setupButton( R.id.buttonAD, InputMap.AXIS_D ); + setupButton( R.id.buttonAU, InputMap.AXIS_U ); + } + + private void setupButton( int resId, int index ) + { + mN64Buttons[index] = findViewById( resId ); + if( mN64Buttons[index] != null ) + { + mN64Buttons[index].setOnClickListener( this ); + } + } + + @Override + public void onClick(View view) + { + // Handle button clicks in the mapping screen + for( int i = 0; i < mN64Buttons.length; i++ ) + { + // Find the button that was pressed + if( view.equals( mN64Buttons[i] ) ) + { + // Popup a dialog to listen to input codes from user + Button button = (Button) view; + popupListener( button.getText(), i ); + } + } + } + + private interface PromptInputCodeListener + { + void onDialogClosed( int inputCode, int hardwareId, int which ); + } + + /** + * Open a dialog to prompt the user for an input code. + * + * @param context The activity context. + * @param title The title of the dialog. + * @param message The message to be shown inside the dialog. + * @param neutralButtonText The text to be shown on the neutral button, or null. + * @param listener The listener to process the input code, when provided. + */ + public static void promptInputCode( Context context, CharSequence title, CharSequence message, + CharSequence neutralButtonText, final PromptInputCodeListener listener ) + { + final ArrayList providers = new ArrayList<>(); + + // Create a widget to dispatch key/motion event data + FrameLayout view = new FrameLayout( context ); + EditText dummyImeListener = new EditText( context ); + dummyImeListener.setVisibility( View.INVISIBLE ); + dummyImeListener.setHeight( 0 ); + view.addView( dummyImeListener ); + + // Set the focus parameters of the view so that it will dispatch events + view.setFocusable( true ); + view.setFocusableInTouchMode( true ); + view.requestFocus(); + + // Create the input event providers + providers.add( new KeyProvider(view,KeyProvider.ImeFormula.DEFAULT,AndroidDevice.getUnmappableKeyCodes())); + if( AndroidDevice.IS_HONEYCOMB_MR1 ) + { + providers.add( new AxisProvider(view)); + } + + // Notify the client when the user clicks the dialog's positive button + DialogInterface.OnClickListener clickListener = new DialogInterface.OnClickListener() + { + @Override + public void onClick( DialogInterface dialog, int which ) + { + for( AbstractProvider provider : providers ) + { + provider.unregisterAllListeners(); + } + listener.onDialogClosed( 0, 0, which ); + } + }; + + final AlertDialog dialog = new AlertDialog.Builder( context ).setTitle( title ).setMessage( message ).setCancelable( false ) + .setNegativeButton( context.getString( android.R.string.cancel ), clickListener ) + .setPositiveButton( context.getString( android.R.string.ok ), clickListener ) + .setNeutralButton( neutralButtonText, clickListener ).setPositiveButton( null, null ) + .setView( view ).create(); + + AbstractProvider.OnInputListener inputListener = new AbstractProvider.OnInputListener() + { + @Override + public void onInput( int[] inputCodes, float[] strengths, int hardwareId ) + { + if( inputCodes == null || strengths == null ) + return; + + // Find the strongest input + float maxStrength = 0; + int strongestInputCode = 0; + for( int i = 0; i < inputCodes.length; i++ ) + { + // Identify the strongest input + float strength = strengths[i]; + if( strength > maxStrength ) + { + maxStrength = strength; + strongestInputCode = inputCodes[i]; + } + } + + // Call the overloaded method with the strongest found + onInput( strongestInputCode, maxStrength, hardwareId ); + } + + @Override + public void onInput( int inputCode, float strength, int hardwareId ) + { + if( inputCode != 0 && strength > AbstractProvider.STRENGTH_THRESHOLD ) + { + for( AbstractProvider provider : providers ) + provider.unregisterAllListeners(); + listener.onDialogClosed( inputCode, hardwareId, DialogInterface.BUTTON_POSITIVE ); + dialog.dismiss(); + } + } + }; + + // Connect the upstream event listeners + for( AbstractProvider provider : providers ) + { + provider.registerListener( inputListener ); + } + + // Launch the dialog + dialog.show(); + } + + private void popupListener( CharSequence title, final int index ) + { + final InputMap map = new InputMap( mProfile.get( "map" ) ); + String message = getString( R.string.inputMapActivity_popupMessage,map.getMappedCodeInfo( index ) ); + String btnText = getString( R.string.inputMapActivity_popupUnmap ); + + PromptInputCodeListener listener = new PromptInputCodeListener() + { + @Override + public void onDialogClosed( int inputCode, int hardwareId, int which ) + { + if( which != DialogInterface.BUTTON_NEGATIVE ) + { + if( which == DialogInterface.BUTTON_POSITIVE ) + { + map.map( inputCode, index ); + } + else + { + map.unmapCommand( index ); + } + mProfile.put( "map", map.serialize() ); + refreshAllButtons(); + } + } + }; + promptInputCode( this, title, message, btnText, listener); + } + + @Override + public boolean onKeyDown( int keyCode, KeyEvent event ) + { + return mKeyProvider.onKey( keyCode, event ) || super.onKeyDown( keyCode, event ); + } + + @Override + public boolean onKeyUp( int keyCode, KeyEvent event ) + { + return mKeyProvider.onKey( keyCode, event ) || super.onKeyUp( keyCode, event ); + } + + @Override + public boolean onGenericMotionEvent( MotionEvent event ) + { + if( !AndroidDevice.IS_HONEYCOMB_MR1 ) + { + return false; + } + return mAxisProvider.onGenericMotion( event ) || super.onGenericMotionEvent( event ); + } + + @Override + public void onInput(int inputCode, float strength, int hardwareId) + { + refreshButton( inputCode, strength ); + refreshFeedbackText( inputCode, strength ); + } + + @Override + public void onInput(int[] inputCodes, float[] strengths, int hardwareId) + { + float maxStrength = AbstractProvider.STRENGTH_THRESHOLD; + int strongestInputCode = 0; + for( int i = 0; i < inputCodes.length; i++ ) + { + int inputCode = inputCodes[i]; + float strength = strengths[i]; + + // Cache the strongest input + if( strength > maxStrength ) + { + maxStrength = strength; + strongestInputCode = inputCode; + } + + refreshButton( inputCode, strength ); + } + refreshFeedbackText( strongestInputCode, maxStrength ); + } + + private void refreshFeedbackText( int inputCode, float strength ) + { + // Update the feedback text (not all layouts include this, so check null) + if( mFeedbackText != null ) + { + mFeedbackText.setText( strength > AbstractProvider.STRENGTH_THRESHOLD ? AbstractProvider.getInputName( inputCode ) : "" ); + } + } + + private void refreshButton( int inputCode, float strength ) + { + InputMap map = new InputMap( mProfile.get( "map" ) ); + int command = map.get( inputCode ); + if( command != InputMap.UNMAPPED ) + { + Button button = mN64Buttons[command]; + refreshButton( button, strength, true ); + } + } + + private void refreshButton( Button button, float strength, boolean isMapped ) + { + if( button != null ) + { + button.setPressed( strength > AbstractProvider.STRENGTH_THRESHOLD ); + + // Fade any buttons that aren't mapped + if( AndroidDevice.IS_HONEYCOMB ) + { + if( isMapped ) + { + button.setAlpha( 1 ); + } + else + { + button.setAlpha( UNMAPPED_BUTTON_ALPHA ); + } + } + else + { + // For older APIs try something similar (not quite the same) + if( isMapped ) + { + button.getBackground().clearColorFilter(); + } + else + { + button.getBackground().setColorFilter( UNMAPPED_BUTTON_FILTER, PorterDuff.Mode.MULTIPLY ); + } + button.invalidate(); + } + } + } + + private void refreshAllButtons() + { + final InputMap map = new InputMap( mProfile.get( "map" ) ); + for( int i = 0; i < mN64Buttons.length; i++ ) + { + refreshButton( mN64Buttons[i], 0, map.isMapped( i ) ); + } + } +} diff --git a/Android/app/src/main/java/emu/project64/settings/BaseSettingsFragment.java b/Android/app/src/main/java/emu/project64/settings/BaseSettingsFragment.java index cf55120f2..3e0f6cb59 100644 --- a/Android/app/src/main/java/emu/project64/settings/BaseSettingsFragment.java +++ b/Android/app/src/main/java/emu/project64/settings/BaseSettingsFragment.java @@ -2,6 +2,7 @@ package emu.project64.settings; import emu.project64.R; import emu.project64.SplashActivity; +import emu.project64.profile.ControllerProfileActivity; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; @@ -61,6 +62,9 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat } else if (preference.getKey().equals("settings_gamepad_screen")) { + final AppCompatActivity activity = (AppCompatActivity)getActivity(); + Intent intent = new Intent( activity, ControllerProfileActivity.class ); + activity.startActivity( intent ); } else if (preference.getKey().equals("settings_video")) { diff --git a/Android/app/src/main/res/layout/controller_profile_activity.xml b/Android/app/src/main/res/layout/controller_profile_activity.xml index fb8ab95a7..db1927cee 100644 --- a/Android/app/src/main/res/layout/controller_profile_activity.xml +++ b/Android/app/src/main/res/layout/controller_profile_activity.xml @@ -1,21 +1,20 @@ - - + + android:theme="@style/Theme.Project64.ToolBar" + app:titleTextAppearance="@style/Theme.Project64.ToolBar.TitleText" + />