Merge pull request #4040 from Ironthighs/master

Android: Configurable Control Placement
This commit is contained in:
Markus Wick 2016-07-24 23:18:45 +02:00 committed by GitHub
commit 0e7b10064d
9 changed files with 285 additions and 14 deletions

View File

@ -9,6 +9,10 @@ android {
// This is important as it will run lint but not abort on error // This is important as it will run lint but not abort on error
// Lint has some overly obnoxious "errors" that should really be warnings // Lint has some overly obnoxious "errors" that should really be warnings
abortOnError false abortOnError false
//Uncomment disable lines for test builds...
//disable 'MissingTranslation'
//disable 'ExtraTranslation'
} }
defaultConfig { defaultConfig {

View File

@ -394,6 +394,18 @@ public final class EmulationActivity extends AppCompatActivity
return; return;
} }
case R.id.menu_emulation_configure_controls:
EmulationFragment emulationFragment = (EmulationFragment) getFragmentManager().findFragmentById(R.id.frame_emulation_fragment);
if (emulationFragment.isConfiguringControls())
{
emulationFragment.stopConfiguringControls();
}
else
{
emulationFragment.startConfiguringControls();
}
break;
case R.id.menu_refresh_wiimotes: case R.id.menu_refresh_wiimotes:
NativeLibrary.RefreshWiimotes(); NativeLibrary.RefreshWiimotes();
return; return;

View File

@ -10,6 +10,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import org.dolphinemu.dolphinemu.BuildConfig; import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
@ -86,7 +87,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
} }
} }
if (savedInstanceState == null) if (savedInstanceState == null)
{ {
mEmulationThread = new Thread(mEmulationRunner); mEmulationThread = new Thread(mEmulationRunner);
@ -101,6 +101,20 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
return contents; return contents;
} }
@Override
public void onViewCreated(View view, Bundle savedInstanceState)
{
Button doneButton = (Button) view.findViewById(R.id.done_control_config);
doneButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
stopConfiguringControls();
}
});
}
@Override @Override
public void onStart() public void onStart()
{ {
@ -224,4 +238,21 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
NativeLibrary.Run(); NativeLibrary.Run();
} }
}; };
public void startConfiguringControls()
{
getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE);
mInputOverlay.setIsInEditMode(true);
}
public void stopConfiguringControls()
{
getView().findViewById(R.id.done_control_config).setVisibility(View.GONE);
mInputOverlay.setIsInEditMode(false);
}
public boolean isConfiguringControls()
{
return mInputOverlay.isInEditMode();
}
} }

View File

@ -39,6 +39,10 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>(); private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>();
private final Set<InputOverlayDrawableJoystick> overlayJoysticks = new HashSet<>(); private final Set<InputOverlayDrawableJoystick> overlayJoysticks = new HashSet<>();
private boolean mIsInEditMode = false;
private InputOverlayDrawableButton mButtonBeingConfigured;
private InputOverlayDrawableJoystick mJoystickBeingConfigured;
/** /**
* Resizes a {@link Bitmap} by a given scale factor * Resizes a {@link Bitmap} by a given scale factor
* *
@ -111,25 +115,34 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
@Override @Override
public boolean onTouch(View v, MotionEvent event) public boolean onTouch(View v, MotionEvent event)
{ {
if (isInEditMode())
{
return onTouchWhileEditing(v, event);
}
int pointerIndex = event.getActionIndex(); int pointerIndex = event.getActionIndex();
for (InputOverlayDrawableButton button : overlayButtons) for (InputOverlayDrawableButton button : overlayButtons)
{ {
// Determine the button state to apply based on the MotionEvent action flag. // Determine the button state to apply based on the MotionEvent action flag.
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:
case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_MOVE:
// If a pointer enters the bounds of a button, press that button. // If a pointer enters the bounds of a button, press that button.
if (button.getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex))) if (button.getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex)))
{
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), ButtonState.PRESSED); NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), ButtonState.PRESSED);
}
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_UP:
// If a pointer ends, release the button it was pressing. // If a pointer ends, release the button it was pressing.
if (button.getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex))) if (button.getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex)))
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), ButtonState.RELEASED); {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), ButtonState.RELEASED);
}
break; break;
} }
} }
@ -142,12 +155,103 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
float[] axises = joystick.getAxisValues(); float[] axises = joystick.getAxisValues();
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, axisIDs[i], axises[i]); NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, axisIDs[i], axises[i]);
}
} }
return true; return true;
} }
public boolean onTouchWhileEditing(View v, MotionEvent event)
{
int pointerIndex = event.getActionIndex();
int fingerPositionX = (int)event.getX(pointerIndex);
int fingerPositionY = (int)event.getY(pointerIndex);
//Maybe combine Button and Joystick as subclasses of the same parent?
//Or maybe create an interface like IMoveableHUDControl?
for (InputOverlayDrawableButton button : overlayButtons)
{
// Determine the button state to apply based on the MotionEvent action flag.
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
// If no button is being moved now, remember the currently touched button to move.
if (mButtonBeingConfigured == null && button.getBounds().contains(fingerPositionX, fingerPositionY))
{
mButtonBeingConfigured = button;
mButtonBeingConfigured.onConfigureTouch(v, event);
}
break;
case MotionEvent.ACTION_MOVE:
if (mButtonBeingConfigured != null)
{
mButtonBeingConfigured.onConfigureTouch(v, event);
invalidate();
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (mButtonBeingConfigured == button)
{
//Persist button position by saving new place.
saveControlPosition(mButtonBeingConfigured.getSharedPrefsId(), mButtonBeingConfigured.getBounds().left, mButtonBeingConfigured.getBounds().top);
mButtonBeingConfigured = null;
}
break;
}
}
for (InputOverlayDrawableJoystick joystick : overlayJoysticks)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (mJoystickBeingConfigured == null && joystick.getBounds().contains(fingerPositionX, fingerPositionY))
{
mJoystickBeingConfigured = joystick;
mJoystickBeingConfigured.onConfigureTouch(v, event);
}
break;
case MotionEvent.ACTION_MOVE:
if (mJoystickBeingConfigured != null)
{
mJoystickBeingConfigured.onConfigureTouch(v, event);
invalidate();
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (mJoystickBeingConfigured != null)
{
saveControlPosition(mJoystickBeingConfigured.getSharedPrefsId(), mJoystickBeingConfigured.getBounds().left, mJoystickBeingConfigured.getBounds().right);
mJoystickBeingConfigured = null;
}
break;
}
}
return true;
}
private void saveControlPosition(String sharedPrefsId, int x, int y)
{
final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor sPrefsEditor = sPrefs.edit();
sPrefsEditor.putFloat(sharedPrefsId+"-X", x);
sPrefsEditor.putFloat(sharedPrefsId+"-Y", y);
sPrefsEditor.apply();
}
/** /**
* Initializes an InputOverlayDrawableButton, given by resId, with all of the * Initializes an InputOverlayDrawableButton, given by resId, with all of the
* parameters set for it to be properly shown on the InputOverlay. * parameters set for it to be properly shown on the InputOverlay.
@ -212,11 +316,10 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Initialize the InputOverlayDrawableButton. // Initialize the InputOverlayDrawableButton.
final Bitmap bitmap = resizeBitmap(context, BitmapFactory.decodeResource(res, resId), scale); final Bitmap bitmap = resizeBitmap(context, BitmapFactory.decodeResource(res, resId), scale);
final InputOverlayDrawableButton overlayDrawable = new InputOverlayDrawableButton(res, bitmap, buttonId);
// String ID of the Drawable. This is what is passed into SharedPreferences // String ID of the Drawable. This is what is passed into SharedPreferences
// to check whether or not a value has been set. // to check whether or not a value has been set. Send to button so it can be referenced.
final String drawableId = res.getResourceEntryName(resId); final String drawableId = res.getResourceEntryName(resId);
final InputOverlayDrawableButton overlayDrawable = new InputOverlayDrawableButton(res, bitmap, buttonId, drawableId);
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
@ -233,6 +336,9 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// This will dictate where on the screen (and the what the size) the InputOverlayDrawableButton will be. // This will dictate where on the screen (and the what the size) the InputOverlayDrawableButton will be.
overlayDrawable.setBounds(drawableX, drawableY, drawableX+intrinWidth, drawableY+intrinHeight); overlayDrawable.setBounds(drawableX, drawableY, drawableX+intrinWidth, drawableY+intrinHeight);
// Need to set the image's position
overlayDrawable.setPosition(drawableX, drawableY);
return overlayDrawable; return overlayDrawable;
} }
@ -276,14 +382,27 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
Rect outerRect = new Rect(drawableX, drawableY, drawableX + outerSize, drawableY + outerSize); Rect outerRect = new Rect(drawableX, drawableY, drawableX + outerSize, drawableY + outerSize);
Rect innerRect = new Rect(0, 0, outerSize / 4, outerSize / 4); Rect innerRect = new Rect(0, 0, outerSize / 4, outerSize / 4);
// Send the drawableId to the joystick so it can be referenced when saving control position.
final InputOverlayDrawableJoystick overlayDrawable final InputOverlayDrawableJoystick overlayDrawable
= new InputOverlayDrawableJoystick(res, = new InputOverlayDrawableJoystick(res,
bitmapOuter, bitmapInner, bitmapOuter, bitmapInner,
outerRect, innerRect, outerRect, innerRect,
joystick); joystick, drawableId);
// Need to set the image's position
overlayDrawable.setPosition(drawableX, drawableY);
return overlayDrawable; return overlayDrawable;
} }
public void setIsInEditMode(boolean isInEditMode)
{
mIsInEditMode = isInEditMode;
}
public boolean isInEditMode()
{
return mIsInEditMode;
}
} }

View File

@ -8,7 +8,10 @@ package org.dolphinemu.dolphinemu.overlay;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent;
import android.view.View;
/** /**
* Custom {@link BitmapDrawable} that is capable * Custom {@link BitmapDrawable} that is capable
@ -17,7 +20,10 @@ import android.graphics.drawable.BitmapDrawable;
public final class InputOverlayDrawableButton extends BitmapDrawable public final class InputOverlayDrawableButton extends BitmapDrawable
{ {
// The ID identifying what type of button this Drawable represents. // The ID identifying what type of button this Drawable represents.
private int buttonType; private int mButtonType;
private int mPreviousTouchX, mPreviousTouchY;
private int mControlPositionX, mControlPositionY;
private String mSharedPrefsId;
/** /**
* Constructor * Constructor
@ -25,12 +31,13 @@ public final class InputOverlayDrawableButton extends BitmapDrawable
* @param res {@link Resources} instance. * @param res {@link Resources} instance.
* @param bitmap {@link Bitmap} to use with this Drawable. * @param bitmap {@link Bitmap} to use with this Drawable.
* @param buttonType Identifier for this type of button. * @param buttonType Identifier for this type of button.
* @param sharedPrefsId Identifier for getting X and Y control positions from Shared Preferences.
*/ */
public InputOverlayDrawableButton(Resources res, Bitmap bitmap, int buttonType) public InputOverlayDrawableButton(Resources res, Bitmap bitmap, int buttonType, String sharedPrefsId)
{ {
super(res, bitmap); super(res, bitmap);
mButtonType = buttonType;
this.buttonType = buttonType; mSharedPrefsId = sharedPrefsId;
} }
/** /**
@ -40,6 +47,40 @@ public final class InputOverlayDrawableButton extends BitmapDrawable
*/ */
public int getId() public int getId()
{ {
return buttonType; return mButtonType;
}
public boolean onConfigureTouch(View v, MotionEvent event)
{
int pointerIndex = event.getActionIndex();
int fingerPositionX = (int)event.getX(pointerIndex);
int fingerPositionY = (int)event.getY(pointerIndex);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY;
break;
case MotionEvent.ACTION_MOVE:
mControlPositionX += fingerPositionX - mPreviousTouchX;
mControlPositionY += fingerPositionY - mPreviousTouchY;
setBounds(new Rect(mControlPositionX, mControlPositionY, getBitmap().getWidth() + mControlPositionX, getBitmap().getHeight() + mControlPositionY));
mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY;
break;
}
return true;
}
public String getSharedPrefsId()
{
return mSharedPrefsId;
}
public void setPosition(int x, int y)
{
mControlPositionX = x;
mControlPositionY = y;
} }
} }

View File

@ -12,6 +12,7 @@ import android.graphics.Canvas;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View;
/** /**
* Custom {@link BitmapDrawable} that is capable * Custom {@link BitmapDrawable} that is capable
@ -23,6 +24,9 @@ public final class InputOverlayDrawableJoystick extends BitmapDrawable
private final float[] axises = {0f, 0f}; private final float[] axises = {0f, 0f};
private final BitmapDrawable ringInner; private final BitmapDrawable ringInner;
private int trackId = -1; private int trackId = -1;
private String mSharedPrefsId;
private int mControlPositionX, mControlPositionY;
private int mPreviousTouchX, mPreviousTouchY;
/** /**
* Constructor * Constructor
@ -33,11 +37,12 @@ public final class InputOverlayDrawableJoystick extends BitmapDrawable
* @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.
* @param sharedPrefsId Identifier for getting X and Y control positions from Shared Preferences.
*/ */
public InputOverlayDrawableJoystick(Resources res, public InputOverlayDrawableJoystick(Resources res,
Bitmap bitmapOuter, Bitmap bitmapInner, Bitmap bitmapOuter, Bitmap bitmapInner,
Rect rectOuter, Rect rectInner, Rect rectOuter, Rect rectInner,
int joystick) int joystick, String sharedPrefsId)
{ {
super(res, bitmapOuter); super(res, bitmapOuter);
this.setBounds(rectOuter); this.setBounds(rectOuter);
@ -49,13 +54,13 @@ public final class InputOverlayDrawableJoystick extends BitmapDrawable
this.axisIDs[1] = joystick + 2; this.axisIDs[1] = joystick + 2;
this.axisIDs[2] = joystick + 3; this.axisIDs[2] = joystick + 3;
this.axisIDs[3] = joystick + 4; this.axisIDs[3] = joystick + 4;
mSharedPrefsId = sharedPrefsId;
} }
@Override @Override
public void draw(Canvas canvas) public void draw(Canvas canvas)
{ {
super.draw(canvas); super.draw(canvas);
ringInner.draw(canvas); ringInner.draw(canvas);
} }
@ -106,6 +111,33 @@ public final class InputOverlayDrawableJoystick extends BitmapDrawable
} }
} }
public boolean onConfigureTouch(View v, MotionEvent event)
{
int pointerIndex = event.getActionIndex();
int fingerPositionX = (int)event.getX(pointerIndex);
int fingerPositionY = (int)event.getY(pointerIndex);
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY;
break;
case MotionEvent.ACTION_MOVE:
int deltaX = fingerPositionX - mPreviousTouchX;
int deltaY = fingerPositionY - mPreviousTouchY;
mControlPositionX += deltaX;
mControlPositionY += deltaY;
setBounds(new Rect(mControlPositionX, mControlPositionY, getBitmap().getWidth() + mControlPositionX, getBitmap().getHeight() + mControlPositionY));
SetInnerBounds();
mPreviousTouchX = fingerPositionX;
mPreviousTouchY = fingerPositionY;
break;
}
return true;
}
public float[] getAxisValues() public float[] getAxisValues()
{ {
float[] joyaxises = {0f, 0f, 0f, 0f}; float[] joyaxises = {0f, 0f, 0f, 0f};
@ -133,5 +165,17 @@ public final class InputOverlayDrawableJoystick extends BitmapDrawable
int height = this.ringInner.getBounds().height() / 2; int height = this.ringInner.getBounds().height() / 2;
this.ringInner.setBounds(X - width, Y - height, this.ringInner.setBounds(X - width, Y - height,
X + width, Y + height); X + width, Y + height);
ringInner.invalidateSelf();
}
public String getSharedPrefsId()
{
return mSharedPrefsId;
}
public void setPosition(int x, int y)
{
mControlPositionX = x;
mControlPositionY = y;
} }
} }

View File

@ -20,4 +20,16 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:focusable="true" android:focusable="true"
android:focusableInTouchMode="true"/> android:focusableInTouchMode="true"/>
<Button
android:id="@+id/done_control_config"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="@dimen/spacing_small"
android:background="@color/dolphin_blue"
android:textColor="@color/lb_tv_white"
android:text="@string/emulation_done"
android:visibility="gone"/>
</FrameLayout> </FrameLayout>

View File

@ -31,6 +31,12 @@
android:showAsAction="never" android:showAsAction="never"
android:title="@string/emulation_toggle_input"/> android:title="@string/emulation_toggle_input"/>
<item
android:id="@+id/menu_emulation_configure_controls"
android:showAsAction="never"
android:title="@string/emulation_configure_controls">
</item>
<!-- Save State Slots --> <!-- Save State Slots -->
<item <item
android:id="@+id/menu_emulation_save_root" android:id="@+id/menu_emulation_save_root"

View File

@ -359,6 +359,8 @@
<string name="emulation_quicksave">Quick Save</string> <string name="emulation_quicksave">Quick Save</string>
<string name="emulation_quickload">Quick Load</string> <string name="emulation_quickload">Quick Load</string>
<string name="emulation_refresh_wiimotes">Refresh Wiimotes</string> <string name="emulation_refresh_wiimotes">Refresh Wiimotes</string>
<string name="emulation_configure_controls">Configure Controls</string>
<string name="emulation_done">Done</string>
<!-- GC Adapter Menu--> <!-- GC Adapter Menu-->
<string name="gc_adapter_rumble">Enable Vibration</string> <string name="gc_adapter_rumble">Enable Vibration</string>