Android App starten

This commit is contained in:
2023-01-08 18:30:55 +01:00
parent 1f66795bb1
commit 4d800c36fe
88 changed files with 3021 additions and 0 deletions
@@ -0,0 +1 @@
/build
@@ -0,0 +1,47 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 32
versionCode 20
versionName "1.10.1"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.12'
}
ext {
// Where you will see your artifact in Bintray's web interface
// The "bintrayName" should match the name of the Bintray repro.
bintrayRepo = 'maven'
bintrayName = 'virtual-joystick-android'
// Maven metadata
publishedGroupId = 'io.github.controlwear'
libraryName = 'virtual-joystick-android'
// Save yourself a head ache, and set this equal to the name of the Android Studio library
// module. The artifact name needs to match the name of the library.
artifact = 'virtualjoystick'
libraryDescription = 'This library provides a very simple and ready-to-use custom view which emulates a joystick for Android.'
libraryVersion = '1.10.1'
developerId = 'makowildcat'
developerName = 'Damien Brun'
developerEmail = 'makowildcat@gmail.com'
}
//apply from: 'https://raw.githubusercontent.com/attwellbrian/JCenter/master/installv1.gradle'
//apply from: 'https://raw.githubusercontent.com/attwellbrian/JCenter/master/bintrayv1.gradle'
@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/damienbrun/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="io.github.controlwear.virtual.joystick.android">
</manifest>
@@ -0,0 +1,871 @@
package io.github.controlwear.virtual.joystick.android;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
public class JoystickView extends View
implements
Runnable {
/*
INTERFACES
*/
/**
* Interface definition for a callback to be invoked when a
* JoystickView's button is moved
*/
public interface OnMoveListener {
/**
* Called when a JoystickView's button has been moved
* @param angle current angle
* @param strength current strength
*/
void onMove(int angle, int strength);
}
/**
* Interface definition for a callback to be invoked when a JoystickView
* is touched and held by multiple pointers.
*/
public interface OnMultipleLongPressListener {
/**
* Called when a JoystickView has been touch and held enough time by multiple pointers.
*/
void onMultipleLongPress();
}
/*
CONSTANTS
*/
/**
* Default refresh rate as a time in milliseconds to send move values through callback
*/
private static final int DEFAULT_LOOP_INTERVAL = 50; // in milliseconds
/**
* Used to allow a slight move without cancelling MultipleLongPress
*/
private static final int MOVE_TOLERANCE = 10;
/**
* Default color for button
*/
private static final int DEFAULT_COLOR_BUTTON = Color.BLACK;
/**
* Default color for border
*/
private static final int DEFAULT_COLOR_BORDER = Color.TRANSPARENT;
/**
* Default alpha for border
*/
private static final int DEFAULT_ALPHA_BORDER = 255;
/**
* Default background color
*/
private static final int DEFAULT_BACKGROUND_COLOR = Color.TRANSPARENT;
/**
* Default View's size
*/
private static final int DEFAULT_SIZE = 200;
/**
* Default border's width
*/
private static final int DEFAULT_WIDTH_BORDER = 3;
/**
* Default behavior to fixed center (not auto-defined)
*/
private static final boolean DEFAULT_FIXED_CENTER = true;
/**
* Default behavior to auto re-center button (automatically recenter the button)
*/
private static final boolean DEFAULT_AUTO_RECENTER_BUTTON = true;
/**
* Default behavior to button stickToBorder (button stay on the border)
*/
private static final boolean DEFAULT_BUTTON_STICK_TO_BORDER = false;
// DRAWING
private Paint mPaintCircleButton;
private Paint mPaintCircleBorder;
private Paint mPaintBackground;
private Paint mPaintBitmapButton;
private Bitmap mButtonBitmap;
/**
* Ratio use to define the size of the button
*/
private float mButtonSizeRatio;
/**
* Ratio use to define the size of the background
*
*/
private float mBackgroundSizeRatio;
// COORDINATE
private int mPosX = 0;
private int mPosY = 0;
private int mCenterX = 0;
private int mCenterY = 0;
private int mFixedCenterX = 0;
private int mFixedCenterY = 0;
/**
* Used to adapt behavior whether it is auto-defined center (false) or fixed center (true)
*/
private boolean mFixedCenter;
/**
* Used to adapt behavior whether the button is automatically re-centered (true)
* when released or not (false)
*/
private boolean mAutoReCenterButton;
/**
* Used to adapt behavior whether the button is stick to border (true) or
* could be anywhere (when false - similar to regular behavior)
*/
private boolean mButtonStickToBorder;
/**
* Used to enabled/disabled the Joystick. When disabled (enabled to false) the joystick button
* can't move and onMove is not called.
*/
private boolean mEnabled;
// SIZE
private int mButtonRadius;
private int mBorderRadius;
/**
* Alpha of the border (to use when changing color dynamically)
*/
private int mBorderAlpha;
/**
* Based on mBorderRadius but a bit smaller (minus half the stroke size of the border)
*/
private float mBackgroundRadius;
/**
* Listener used to dispatch OnMove event
*/
private OnMoveListener mCallback;
private long mLoopInterval = DEFAULT_LOOP_INTERVAL;
private Thread mThread = new Thread(this);
/**
* Listener used to dispatch MultipleLongPress event
*/
private OnMultipleLongPressListener mOnMultipleLongPressListener;
private final Handler mHandlerMultipleLongPress = new Handler();
private Runnable mRunnableMultipleLongPress;
private int mMoveTolerance;
/**
* Default value.
* Both direction correspond to horizontal and vertical movement
*/
public static int BUTTON_DIRECTION_BOTH = 0;
/**
* The allowed direction of the button is define by the value of this parameter:
* - a negative value for horizontal axe
* - a positive value for vertical axe
* - zero for both axes
*/
private int mButtonDirection = 0;
/*
CONSTRUCTORS
*/
/**
* Simple constructor to use when creating a JoystickView from code.
* Call another constructor passing null to Attribute.
* @param context The Context the JoystickView is running in, through which it can
* access the current theme, resources, etc.
*/
public JoystickView(Context context) {
this(context, null);
}
public JoystickView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs);
}
/**
* Constructor that is called when inflating a JoystickView from XML. This is called
* when a JoystickView is being constructed from an XML file, supplying attributes
* that were specified in the XML file.
* @param context The Context the JoystickView is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the JoystickView.
*/
public JoystickView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.JoystickView,
0, 0
);
int buttonColor;
int borderColor;
int backgroundColor;
int borderWidth;
Drawable buttonDrawable;
try {
buttonColor = styledAttributes.getColor(R.styleable.JoystickView_JV_buttonColor, DEFAULT_COLOR_BUTTON);
borderColor = styledAttributes.getColor(R.styleable.JoystickView_JV_borderColor, DEFAULT_COLOR_BORDER);
mBorderAlpha = styledAttributes.getInt(R.styleable.JoystickView_JV_borderAlpha, DEFAULT_ALPHA_BORDER);
backgroundColor = styledAttributes.getColor(R.styleable.JoystickView_JV_backgroundColor, DEFAULT_BACKGROUND_COLOR);
borderWidth = styledAttributes.getDimensionPixelSize(R.styleable.JoystickView_JV_borderWidth, DEFAULT_WIDTH_BORDER);
mFixedCenter = styledAttributes.getBoolean(R.styleable.JoystickView_JV_fixedCenter, DEFAULT_FIXED_CENTER);
mAutoReCenterButton = styledAttributes.getBoolean(R.styleable.JoystickView_JV_autoReCenterButton, DEFAULT_AUTO_RECENTER_BUTTON);
mButtonStickToBorder = styledAttributes.getBoolean(R.styleable.JoystickView_JV_buttonStickToBorder, DEFAULT_BUTTON_STICK_TO_BORDER);
buttonDrawable = styledAttributes.getDrawable(R.styleable.JoystickView_JV_buttonImage);
mEnabled = styledAttributes.getBoolean(R.styleable.JoystickView_JV_enabled, true);
mButtonSizeRatio = styledAttributes.getFraction(R.styleable.JoystickView_JV_buttonSizeRatio, 1, 1, 0.25f);
mBackgroundSizeRatio = styledAttributes.getFraction(R.styleable.JoystickView_JV_backgroundSizeRatio, 1, 1, 0.75f);
mButtonDirection = styledAttributes.getInteger(R.styleable.JoystickView_JV_buttonDirection, BUTTON_DIRECTION_BOTH);
} finally {
styledAttributes.recycle();
}
// Initialize the drawing according to attributes
mPaintCircleButton = new Paint();
mPaintCircleButton.setAntiAlias(true);
mPaintCircleButton.setColor(buttonColor);
mPaintCircleButton.setStyle(Paint.Style.FILL);
if (buttonDrawable != null) {
if (buttonDrawable instanceof BitmapDrawable) {
mButtonBitmap = ((BitmapDrawable) buttonDrawable).getBitmap();
mPaintBitmapButton = new Paint();
}
}
mPaintCircleBorder = new Paint();
mPaintCircleBorder.setAntiAlias(true);
mPaintCircleBorder.setColor(borderColor);
mPaintCircleBorder.setStyle(Paint.Style.STROKE);
mPaintCircleBorder.setStrokeWidth(borderWidth);
if (borderColor != Color.TRANSPARENT) {
mPaintCircleBorder.setAlpha(mBorderAlpha);
}
mPaintBackground = new Paint();
mPaintBackground.setAntiAlias(true);
mPaintBackground.setColor(backgroundColor);
mPaintBackground.setStyle(Paint.Style.FILL);
// Init Runnable for MultiLongPress
mRunnableMultipleLongPress = new Runnable() {
@Override
public void run() {
if (mOnMultipleLongPressListener != null)
mOnMultipleLongPressListener.onMultipleLongPress();
}
};
}
private void initPosition() {
// get the center of view to position circle
mFixedCenterX = mCenterX = mPosX = getWidth() / 2;
mFixedCenterY = mCenterY = mPosY = getWidth() / 2;
}
/**
* Draw the background, the border and the button
* @param canvas the canvas on which the shapes will be drawn
*/
@Override
protected void onDraw(Canvas canvas) {
// Draw the background
canvas.drawCircle(mFixedCenterX, mFixedCenterY, mBackgroundRadius, mPaintBackground);
// Draw the circle border
canvas.drawCircle(mFixedCenterX, mFixedCenterY, mBorderRadius, mPaintCircleBorder);
// Draw the button from image
if (mButtonBitmap != null) {
canvas.drawBitmap(
mButtonBitmap,
mPosX + mFixedCenterX - mCenterX - mButtonRadius,
mPosY + mFixedCenterY - mCenterY - mButtonRadius,
mPaintBitmapButton
);
}
// Draw the button as simple circle
else {
canvas.drawCircle(
mPosX + mFixedCenterX - mCenterX,
mPosY + mFixedCenterY - mCenterY,
mButtonRadius,
mPaintCircleButton
);
}
}
/**
* This is called during layout when the size of this view has changed.
* Here we get the center of the view and the radius to draw all the shapes.
*
* @param w Current width of this view.
* @param h Current height of this view.
* @param oldW Old width of this view.
* @param oldH Old height of this view.
*/
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
initPosition();
// radius based on smallest size : height OR width
int d = Math.min(w, h);
mButtonRadius = (int) (d / 2 * mButtonSizeRatio);
mBorderRadius = (int) (d / 2 * mBackgroundSizeRatio);
mBackgroundRadius = mBorderRadius - (mPaintCircleBorder.getStrokeWidth() / 2);
if (mButtonBitmap != null)
mButtonBitmap = Bitmap.createScaledBitmap(mButtonBitmap, mButtonRadius * 2, mButtonRadius * 2, true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// setting the measured values to resize the view to a certain width and height
int d = Math.min(measure(widthMeasureSpec), measure(heightMeasureSpec));
setMeasuredDimension(d, d);
}
private int measure(int measureSpec) {
if (MeasureSpec.getMode(measureSpec) == MeasureSpec.UNSPECIFIED) {
// if no bounds are specified return a default size (200)
return DEFAULT_SIZE;
} else {
// As you want to fill the available space
// always return the full available bounds.
return MeasureSpec.getSize(measureSpec);
}
}
/*
USER EVENT
*/
/**
* Handle touch screen motion event. Move the button according to the
* finger coordinate and detect longPress by multiple pointers only.
*
* @param event The motion event.
* @return True if the event was handled, false otherwise.
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
// if disabled we don't move the
if (!mEnabled) {
return true;
}
// to move the button according to the finger coordinate
// (or limited to one axe according to direction option
mPosY = mButtonDirection < 0 ? mCenterY : (int) event.getY(); // direction negative is horizontal axe
mPosX = mButtonDirection > 0 ? mCenterX : (int) event.getX(); // direction positive is vertical axe
if (event.getAction() == MotionEvent.ACTION_UP) {
// stop listener because the finger left the touch screen
mThread.interrupt();
// re-center the button or not (depending on settings)
if (mAutoReCenterButton) {
resetButtonPosition();
// update now the last strength and angle which should be zero after resetButton
if (mCallback != null)
mCallback.onMove(getAngle(), getStrength());
}
// if mAutoReCenterButton is false we will send the last strength and angle a bit
// later only after processing new position X and Y otherwise it could be above the border limit
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
if (mThread != null && mThread.isAlive()) {
mThread.interrupt();
}
mThread = new Thread(this);
mThread.start();
if (mCallback != null)
mCallback.onMove(getAngle(), getStrength());
}
// handle first touch and long press with multiple touch only
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
// when the first touch occurs we update the center (if set to auto-defined center)
if (!mFixedCenter) {
mCenterX = mPosX;
mCenterY = mPosY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
// when the second finger touch
if (event.getPointerCount() == 2) {
mHandlerMultipleLongPress.postDelayed(mRunnableMultipleLongPress, ViewConfiguration.getLongPressTimeout()*2);
mMoveTolerance = MOVE_TOLERANCE;
}
break;
}
case MotionEvent.ACTION_MOVE:
mMoveTolerance--;
if (mMoveTolerance == 0) {
mHandlerMultipleLongPress.removeCallbacks(mRunnableMultipleLongPress);
}
break;
case MotionEvent.ACTION_POINTER_UP: {
// when the last multiple touch is released
if (event.getPointerCount() == 2) {
mHandlerMultipleLongPress.removeCallbacks(mRunnableMultipleLongPress);
}
break;
}
}
double abs = Math.sqrt((mPosX - mCenterX) * (mPosX - mCenterX)
+ (mPosY - mCenterY) * (mPosY - mCenterY));
// (abs > mBorderRadius) means button is too far therefore we limit to border
// (buttonStickBorder && abs != 0) means wherever is the button we stick it to the border except when abs == 0
if (abs > mBorderRadius || (mButtonStickToBorder && abs != 0)) {
mPosX = (int) ((mPosX - mCenterX) * mBorderRadius / abs + mCenterX);
mPosY = (int) ((mPosY - mCenterY) * mBorderRadius / abs + mCenterY);
}
if (!mAutoReCenterButton) {
// Now update the last strength and angle if not reset to center
if (mCallback != null)
mCallback.onMove(getAngle(), getStrength());
}
// to force a new draw
invalidate();
return true;
}
/*
GETTERS
*/
/**
* Process the angle following the 360° counter-clock protractor rules.
* @return the angle of the button
*/
private int getAngle() {
int angle = (int) Math.toDegrees(Math.atan2(mCenterY - mPosY, mPosX - mCenterX));
return angle < 0 ? angle + 360 : angle; // make it as a regular counter-clock protractor
}
/**
* Process the strength as a percentage of the distance between the center and the border.
* @return the strength of the button
*/
private int getStrength() {
return (int) (100 * Math.sqrt((mPosX - mCenterX)
* (mPosX - mCenterX) + (mPosY - mCenterY)
* (mPosY - mCenterY)) / mBorderRadius);
}
/**
* Reset the button position to the center.
*/
public void resetButtonPosition() {
mPosX = mCenterX;
mPosY = mCenterY;
}
/**
* Return the current direction allowed for the button to move
* @return Actually return an integer corresponding to the direction:
* - A negative value is horizontal axe,
* - A positive value is vertical axe,
* - Zero means both axes
*/
public int getButtonDirection() {
return mButtonDirection;
}
/**
* Return the state of the joystick. False when the button don't move.
* @return the state of the joystick
*/
public boolean isEnabled() {
return mEnabled;
}
/**
* Return the size of the button (as a ratio of the total width/height)
* Default is 0.25 (25%).
* @return button size (value between 0.0 and 1.0)
*/
public float getButtonSizeRatio() {
return mButtonSizeRatio;
}
/**
* Return the size of the background (as a ratio of the total width/height)
* Default is 0.75 (75%).
* @return background size (value between 0.0 and 1.0)
*/
public float getmBackgroundSizeRatio() {
return mBackgroundSizeRatio;
}
/**
* Return the current behavior of the auto re-center button
* @return True if automatically re-centered or False if not
*/
public boolean isAutoReCenterButton() {
return mAutoReCenterButton;
}
/**
* Return the current behavior of the button stick to border
* @return True if the button stick to the border otherwise False
*/
public boolean isButtonStickToBorder() {
return mButtonStickToBorder;
}
/**
* Return the relative X coordinate of button center related
* to top-left virtual corner of the border
* @return coordinate of X (normalized between 0 and 100)
*/
public int getNormalizedX() {
if (getWidth() == 0) {
return 50;
}
return Math.round((mPosX-mButtonRadius)*100.0f/(getWidth()-mButtonRadius*2));
}
/**
* Return the relative Y coordinate of the button center related
* to top-left virtual corner of the border
* @return coordinate of Y (normalized between 0 and 100)
*/
public int getNormalizedY() {
if (getHeight() == 0) {
return 50;
}
return Math.round((mPosY-mButtonRadius)*100.0f/(getHeight()-mButtonRadius*2));
}
/**
* Return the alpha of the border
* @return it should be an integer between 0 and 255 previously set
*/
public int getBorderAlpha() {
return mBorderAlpha;
}
/*
SETTERS
*/
/**
* Set an image to the button with a drawable
* @param d drawable to pick the image
*/
public void setButtonDrawable(Drawable d) {
if (d != null) {
if (d instanceof BitmapDrawable) {
mButtonBitmap = ((BitmapDrawable) d).getBitmap();
if (mButtonRadius != 0) {
mButtonBitmap = Bitmap.createScaledBitmap(
mButtonBitmap,
mButtonRadius * 2,
mButtonRadius * 2,
true);
}
if (mPaintBitmapButton != null)
mPaintBitmapButton = new Paint();
}
}
}
/**
* Set the button color for this JoystickView.
* @param color the color of the button
*/
public void setButtonColor(int color) {
mPaintCircleButton.setColor(color);
invalidate();
}
/**
* Set the border color for this JoystickView.
* @param color the color of the border
*/
public void setBorderColor(int color) {
mPaintCircleBorder.setColor(color);
if (color != Color.TRANSPARENT) {
mPaintCircleBorder.setAlpha(mBorderAlpha);
}
invalidate();
}
/**
* Set the border alpha for this JoystickView.
* @param alpha the transparency of the border between 0 and 255
*/
public void setBorderAlpha(int alpha) {
mBorderAlpha = alpha;
mPaintCircleBorder.setAlpha(alpha);
invalidate();
}
/**
* Set the background color for this JoystickView.
* @param color the color of the background
*/
@Override
public void setBackgroundColor(int color) {
mPaintBackground.setColor(color);
invalidate();
}
/**
* Set the border width for this JoystickView.
* @param width the width of the border
*/
public void setBorderWidth(int width) {
mPaintCircleBorder.setStrokeWidth(width);
mBackgroundRadius = mBorderRadius - (width / 2.0f);
invalidate();
}
/**
* Register a callback to be invoked when this JoystickView's button is moved
* @param l The callback that will run
*/
public void setOnMoveListener(OnMoveListener l) {
setOnMoveListener(l, DEFAULT_LOOP_INTERVAL);
}
/**
* Register a callback to be invoked when this JoystickView's button is moved
* @param l The callback that will run
* @param loopInterval Refresh rate to be invoked in milliseconds
*/
public void setOnMoveListener(OnMoveListener l, int loopInterval) {
mCallback = l;
mLoopInterval = loopInterval;
}
/**
* Register a callback to be invoked when this JoystickView is touch and held by multiple pointers
* @param l The callback that will run
*/
public void setOnMultiLongPressListener(OnMultipleLongPressListener l) {
mOnMultipleLongPressListener = l;
}
/**
* Set the joystick center's behavior (fixed or auto-defined)
* @param fixedCenter True for fixed center, False for auto-defined center based on touch down
*/
public void setFixedCenter(boolean fixedCenter) {
// if we set to "fixed" we make sure to re-init position related to the width of the joystick
if (fixedCenter) {
initPosition();
}
mFixedCenter = fixedCenter;
invalidate();
}
/**
* Enable or disable the joystick
* @param enabled False mean the button won't move and onMove won't be called
*/
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
/**
* Set the joystick button size (as a fraction of the real width/height)
* By default it is 25% (0.25).
* @param newRatio between 0.0 and 1.0
*/
public void setButtonSizeRatio(float newRatio) {
if (newRatio > 0.0f & newRatio <= 1.0f) {
mButtonSizeRatio = newRatio;
}
}
/**
* Set the joystick button size (as a fraction of the real width/height)
* By default it is 75% (0.75).
* Not working if the background is an image.
* @param newRatio between 0.0 and 1.0
*/
public void setBackgroundSizeRatio(float newRatio) {
if (newRatio > 0.0f & newRatio <= 1.0f) {
mBackgroundSizeRatio = newRatio;
}
}
/**
* Set the current behavior of the auto re-center button
* @param b True if automatically re-centered or False if not
*/
public void setAutoReCenterButton(boolean b) {
mAutoReCenterButton = b;
}
/**
* Set the current behavior of the button stick to border
* @param b True if the button stick to the border or False (default) if not
*/
public void setButtonStickToBorder(boolean b) {
mButtonStickToBorder = b;
}
/**
* Set the current authorized direction for the button to move
* @param direction the value will define the authorized direction:
* - any negative value (such as -1) for horizontal axe
* - any positive value (such as 1) for vertical axe
* - zero (0) for the full direction (both axes)
*/
public void setButtonDirection(int direction) {
mButtonDirection = direction;
}
/*
IMPLEMENTS
*/
@Override // Runnable
public void run() {
while (!Thread.interrupted()) {
post(new Runnable() {
public void run() {
if (mCallback != null)
mCallback.onMove(getAngle(), getStrength());
}
});
try {
Thread.sleep(mLoopInterval);
} catch (InterruptedException e) {
break;
}
}
}
}
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="JoystickView">
<attr name="JV_buttonImage" format="reference" />
<attr name="JV_buttonColor" format="color"/>
<attr name="JV_borderColor" format="color"/>
<attr name="JV_borderAlpha" format="integer"/>
<attr name="JV_backgroundColor" format="color"/>
<attr name="JV_borderWidth" format="dimension"/>
<attr name="JV_fixedCenter" format="boolean"/>
<attr name="JV_autoReCenterButton" format="boolean"/>
<attr name="JV_buttonStickToBorder" format="boolean"/>
<attr name="JV_enabled" format="boolean"/>
<attr name="JV_buttonSizeRatio" format="fraction"/>
<attr name="JV_backgroundSizeRatio" format="fraction"/>
<attr name="JV_buttonDirection">
<flag name="horizontal" value="-1"/>
<flag name="both" value="0"/>
<flag name="vertical" value="1"/>
</attr>
</declare-styleable>
</resources>
@@ -0,0 +1,15 @@
package io.github.controlwear.virtual.joystick.android;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}