├── consumer-rules.pro ├── README.md ├── src └── main │ ├── AndroidManifest.xml │ ├── res │ ├── values │ │ ├── style.xml │ │ ├── colors.xml │ │ ├── styleable.xml │ │ └── strings.xml │ ├── drawable │ │ ├── dpad.xml │ │ ├── hcircle_top.xml │ │ ├── hcircle_bottom.xml │ │ ├── arrow_down.xml │ │ ├── arrow_up.xml │ │ ├── rotate.xml │ │ ├── refresh.xml │ │ ├── keyboard_button.xml │ │ ├── close.xml │ │ ├── fastforward.xml │ │ ├── sound_off.xml │ │ ├── action_back_square.xml │ │ ├── sound_on.xml │ │ ├── action_back_circle.xml │ │ ├── settings.xml │ │ ├── flare.xml │ │ ├── dpad_front.xml │ │ ├── dpad_back.xml │ │ └── keyboard.xml │ ├── anim │ │ ├── button_touch_down.xml │ │ ├── button_touch_up.xml │ │ └── button_touch.xml │ ├── layout │ │ ├── spinner_item.xml │ │ ├── spinner_item_keycode.xml │ │ ├── settings_layout.xml │ │ └── gamepad_layout.xml │ ├── values-zh │ │ └── strings.xml │ ├── values-vi │ │ └── strings.xml │ ├── values-tr │ │ └── strings.xml │ ├── values-ru │ │ └── strings.xml │ └── values-es │ │ └── strings.xml │ └── java │ └── cyou │ └── joiplay │ └── joipad │ ├── animation │ └── ButtonAnimations.java │ ├── drawable │ └── TextDrawable.java │ ├── util │ ├── DirectionUtils.java │ ├── KeyboardUtils.java │ └── ViewUtils.java │ ├── view │ ├── KeyCodeSpinner.java │ ├── GamePadDPad.java │ └── GamePadButton.java │ ├── dialog │ └── SettingsDialog.java │ └── JoiPad.java ├── proguard-rules.pro ├── LICENSE └── .gitignore /consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # joipad 2 | Virtual gamepad for JoiPlay plugins 3 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/res/values/style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /src/main/res/drawable/dpad.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/res/anim/button_touch_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /src/main/res/anim/button_touch_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /src/main/res/layout/spinner_item.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/res/layout/spinner_item_keycode.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/res/drawable/hcircle_top.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 11 | -------------------------------------------------------------------------------- /src/main/res/drawable/hcircle_bottom.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 11 | -------------------------------------------------------------------------------- /src/main/res/anim/button_touch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 15 | -------------------------------------------------------------------------------- /src/main/res/drawable/arrow_down.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/res/drawable/arrow_up.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/res/drawable/rotate.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #333 4 | #2a2a2a 5 | #444 6 | #ccc 7 | #fff 8 | #222 9 | #80303030 10 | #aa303030 11 | #70348564 12 | 13 | -------------------------------------------------------------------------------- /src/main/res/drawable/refresh.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /src/main/res/drawable/keyboard_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/res/values/styleable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/res/drawable/close.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/res/drawable/fastforward.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/res/drawable/sound_off.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/animation/ButtonAnimations.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.animation; 2 | 3 | import android.content.Context; 4 | import android.view.View; 5 | import android.view.animation.AnimationUtils; 6 | 7 | import cyou.joiplay.joipad.R; 8 | 9 | public class ButtonAnimations { 10 | static public void animateTouch(Context context, View view, Boolean isTouched){ 11 | if (isTouched) { 12 | view.startAnimation(AnimationUtils.loadAnimation(context, R.anim.button_touch_down)); 13 | } else { 14 | view.startAnimation(AnimationUtils.loadAnimation(context, R.anim.button_touch_up)); 15 | } 16 | } 17 | 18 | static public void animateTouchOnce(Context context, View view){ 19 | view.startAnimation(AnimationUtils.loadAnimation(context, R.anim.button_touch)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/res/drawable/action_back_square.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/res/drawable/sound_on.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 JoiPlay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/res/drawable/action_back_circle.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 取消 4 | 关闭 5 | 您想要关闭游戏吗? 6 | 错误 7 | 8 | 好的 9 | 10 | 抱歉,无法保存游戏手柄配置。 11 | 按钮不透明度 12 | 按钮大小 13 | 左键 14 | 右键 15 | 第一个按钮 16 | 第二个按钮 17 | 第三个按钮 18 | 第四个按钮 19 | 第五个按钮 20 | 第六个按钮 21 | 保存 22 | 左迷你按钮 23 | 右迷你按钮 24 | 主要 25 | 选择 26 | 27 | -------------------------------------------------------------------------------- /src/main/res/drawable/settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/res/values-vi/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hủy bỏ 4 | Đóng 5 | Bạn có muốn đóng trò chơi không? 6 | Lỗi 7 | Không 8 | Đồng ý 9 | 10 | Xin lỗi, không thể lưu cấu hình gamepad. 11 | Độ mờ của nút 12 | Kích thước nút 13 | Nút trái 14 | Nút phải 15 | Nút đầu tiên 16 | Nút thứ hai 17 | Nút thứ ba 18 | Nút thứ tư 19 | Nút thứ năm 20 | Nút thứ sáu 21 | Cứu 22 | Nút trái nhỏ 23 | Nút mini phải 24 | Chủ yếu 25 | Thay thế 26 | 27 | -------------------------------------------------------------------------------- /src/main/res/drawable/flare.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 34 | 35 | -------------------------------------------------------------------------------- /src/main/res/values-tr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | İptal 4 | Kapat 5 | Oyunu kapatmak istiyor musunuz? 6 | Hata 7 | Hayır 8 | Tamam 9 | Evet 10 | Maalesef gamepad yapılandırması kaydedilemedi. 11 | Buton Opaklığı 12 | Buton Boyutu 13 | Sol Buton 14 | Sağ Buton 15 | Birinci Buton 16 | İkinci Buton 17 | Üçüncü Buton 18 | Dördüncü Buton 19 | Beşinci Buton 20 | Altıncı Buton 21 | Kaydet 22 | Sol Mini Buton 23 | Sağ Mini Buton 24 | Ana 25 | Alternatif 26 | 27 | -------------------------------------------------------------------------------- /src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Отмена 4 | Закрыть 5 | Вы хотите закрыть игру? 6 | Ошибка 7 | Нет 8 | ОК 9 | Да 10 | К сожалению, не удалось сохранить конфигурацию геймпада. 11 | Непрозрачность кнопки 12 | Размер кнопки 13 | Левая кнопка 14 | Правая кнопка 15 | Первая кнопка 16 | Вторая кнопка 17 | Третья кнопка 18 | Четвертая кнопка 19 | Пятая кнопка 20 | Шестая кнопка 21 | Сохранить 22 | Левая мини-кнопка 23 | Правая мини-кнопка 24 | Главный 25 | Альтернатива 26 | 27 | -------------------------------------------------------------------------------- /src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Cancelar 4 | Cerrar 5 | ¿Quieres cerrar el juego? 6 | Error 7 | No 8 | Aceptar 9 | 10 | Lo sentimos, no se pudo guardar la configuración del gamepad. 11 | Opacidad del botón 12 | Tamaño del botón 13 | Botón izquierdo 14 | Botón derecho 15 | Primer botón 16 | Segundo botón 17 | Tercer botón 18 | Cuarto botón 19 | Quinto botón 20 | Sexto botón 21 | Guardar 22 | Mini botón izquierdo 23 | Mini botón derecho 24 | Principal 25 | Alternativa 26 | 27 | -------------------------------------------------------------------------------- /src/main/res/drawable/dpad_front.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/res/drawable/dpad_back.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | .idea/workspace.xml 42 | .idea/tasks.xml 43 | .idea/gradle.xml 44 | .idea/assetWizardSettings.xml 45 | .idea/dictionaries 46 | .idea/libraries 47 | # Android Studio 3 in .gitignore file. 48 | .idea/caches 49 | .idea/modules.xml 50 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 51 | .idea/navEditor.xml 52 | 53 | # Keystore files 54 | # Uncomment the following lines if you do not want to check your keystore files in. 55 | #*.jks 56 | #*.keystore 57 | 58 | # External native build folder generated in Android Studio 2.2 and later 59 | .externalNativeBuild 60 | .cxx/ 61 | 62 | # Google Services (e.g. APIs or Firebase) 63 | # google-services.json 64 | 65 | # Freeline 66 | freeline.py 67 | freeline/ 68 | freeline_project_description.json 69 | 70 | # fastlane 71 | fastlane/report.xml 72 | fastlane/Preview.html 73 | fastlane/screenshots 74 | fastlane/test_output 75 | fastlane/readme.md 76 | 77 | # Version control 78 | vcs.xml 79 | 80 | # lint 81 | lint/intermediates/ 82 | lint/generated/ 83 | lint/outputs/ 84 | lint/tmp/ 85 | # lint/reports/ 86 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/drawable/TextDrawable.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.drawable; 2 | 3 | import android.content.res.Resources; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.ColorFilter; 7 | import android.graphics.Paint; 8 | import android.graphics.drawable.Drawable; 9 | import android.util.TypedValue; 10 | 11 | public class TextDrawable extends Drawable { 12 | private final int mColor = Color.WHITE; 13 | private final int mTextSize = 15; 14 | private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 15 | private int mIntrinsicWidth; 16 | private int mIntrinsicHeight; 17 | private Resources mResources; 18 | private CharSequence mText; 19 | 20 | public TextDrawable(Resources resources, CharSequence text){ 21 | this.mResources = resources; 22 | this.mText = text; 23 | init(); 24 | } 25 | 26 | @Override 27 | public void draw(Canvas canvas) { 28 | canvas.drawText(this.mText, 0, mText.length(), getBounds().centerX(), getBounds().centerY() - mPaint.ascent() / 2f, mPaint); 29 | } 30 | 31 | @Override 32 | public void setAlpha(int i) { 33 | mPaint.setAlpha(i); 34 | } 35 | 36 | @Override 37 | public void setColorFilter(ColorFilter colorFilter) { 38 | mPaint.setColorFilter(colorFilter); 39 | } 40 | 41 | @Override 42 | public int getOpacity() { 43 | return mPaint.getAlpha(); 44 | } 45 | 46 | @Override 47 | public int getIntrinsicWidth() { 48 | return mIntrinsicWidth; 49 | } 50 | 51 | @Override 52 | public int getIntrinsicHeight() { 53 | return mIntrinsicHeight; 54 | } 55 | 56 | private void init(){ 57 | mPaint.setColor(mColor); 58 | mPaint.setTextAlign(Paint.Align.CENTER); 59 | float textSize = TypedValue.applyDimension( 60 | TypedValue.COMPLEX_UNIT_SP, 61 | mTextSize, mResources.getDisplayMetrics() 62 | ); 63 | mPaint.setTextSize(textSize); 64 | mIntrinsicWidth = (int) Math.round(mPaint.measureText(mText, 0, mText.length()) + .5); 65 | mIntrinsicHeight = mPaint.getFontMetricsInt(null); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/res/drawable/keyboard.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 34 | 41 | 48 | 55 | 62 | 63 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/util/DirectionUtils.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.util; 2 | 3 | public class DirectionUtils { 4 | public enum Direction{ 5 | UP, 6 | UP_RIGHT, 7 | RIGHT, 8 | DOWN_RIGHT, 9 | DOWN, 10 | DOWN_LEFT, 11 | LEFT, 12 | UP_LEFT, 13 | UNKNOWN 14 | } 15 | 16 | public static Direction getDir(float x, float y, boolean isDiagonal){ 17 | return getAngle(0,x,0,y,isDiagonal); 18 | } 19 | 20 | public static Direction getDir(double angle, boolean isDiagonal) { 21 | if (isDiagonal){ 22 | return get8dir(angle); 23 | } else { 24 | return get4dir(angle); 25 | } 26 | } 27 | 28 | private static Direction get4dir(double angle) { 29 | if ((angle > 45) && (angle < 136)){ 30 | return Direction.UP; 31 | } else if ((angle > 135) && (angle < 226)){ 32 | return Direction.RIGHT; 33 | } else if ((angle > 225) && (angle < 316)){ 34 | return Direction.DOWN; 35 | } else if (((angle >= 0) && (angle < 46)) || ((angle > 315) && (angle <361))){ 36 | return Direction.LEFT; 37 | } else { 38 | return Direction.UNKNOWN; 39 | } 40 | } 41 | 42 | private static Direction get8dir(double angle) { 43 | if ((angle >= 67.5) && (angle < 113.5)){ 44 | return Direction.UP; 45 | } else if ((angle >= 113.5) && (angle < 158.5)){ 46 | return Direction.UP_RIGHT; 47 | } else if ((angle >= 158.5) && (angle < 203.5)){ 48 | return Direction.RIGHT; 49 | } else if ((angle >= 203.5) && (angle < 248.5)){ 50 | return Direction.DOWN_RIGHT; 51 | } else if ((angle >= 248.5) && (angle < 293.5)){ 52 | return Direction.DOWN; 53 | } else if ((angle >= 293.5) && (angle < 338.5)){ 54 | return Direction.DOWN_LEFT; 55 | } else if (((angle >= 0) && (angle < 23.5)) || ((angle >= 338.5) && (angle <361))){ 56 | return Direction.LEFT; 57 | } else if ((angle >= 23.5) && (angle < 67.5)){ 58 | return Direction.UP_LEFT; 59 | } else { 60 | return Direction.UNKNOWN; 61 | } 62 | } 63 | 64 | public static Direction getAngle(float initX, float posX, float initY, float posY, boolean isDiagonal){ 65 | double angle = 0; 66 | try{ 67 | angle = Math.toDegrees(Math.atan2((initY - posY), (initX - posX))); 68 | } catch (Exception e){} 69 | 70 | if (angle < 0) 71 | angle += 360; 72 | 73 | return DirectionUtils.getDir(angle, isDiagonal); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/view/KeyCodeSpinner.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.view; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.res.Resources; 6 | import android.util.AttributeSet; 7 | import android.view.KeyEvent; 8 | import android.view.View; 9 | import android.widget.AdapterView; 10 | import android.widget.ArrayAdapter; 11 | import android.widget.Spinner; 12 | 13 | import cyou.joiplay.joipad.R; 14 | 15 | public class KeyCodeSpinner extends Spinner { 16 | private ArrayAdapter adapter; 17 | private int keyCode = 0; 18 | 19 | public KeyCodeSpinner(Context context) { 20 | super(context); 21 | init(context); 22 | } 23 | 24 | public KeyCodeSpinner(Context context, int mode) { 25 | super(context, mode); 26 | init(context); 27 | } 28 | 29 | public KeyCodeSpinner(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | init(context); 32 | } 33 | 34 | public KeyCodeSpinner(Context context, AttributeSet attrs, int defStyleAttr) { 35 | super(context, attrs, defStyleAttr); 36 | init(context); 37 | } 38 | 39 | public KeyCodeSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) { 40 | super(context, attrs, defStyleAttr, mode); 41 | init(context); 42 | } 43 | 44 | public KeyCodeSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, int mode) { 45 | super(context, attrs, defStyleAttr, defStyleRes, mode); 46 | init(context); 47 | } 48 | 49 | @TargetApi(23) 50 | public KeyCodeSpinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, int mode, Resources.Theme popupTheme) { 51 | super(context, attrs, defStyleAttr, defStyleRes, mode, popupTheme); 52 | init(context); 53 | } 54 | 55 | private void init(Context context){ 56 | adapter = ArrayAdapter.createFromResource(context, R.array.key_array, R.layout.spinner_item_keycode); 57 | adapter.setDropDownViewResource(R.layout.spinner_item_keycode); 58 | setAdapter(adapter); 59 | 60 | 61 | setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 62 | @Override 63 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 64 | try { 65 | keyCode = getKeyCode((String) parent.getItemAtPosition(position)); 66 | } catch (Exception e){/*ignore*/} 67 | } 68 | 69 | @Override 70 | public void onNothingSelected(AdapterView parent) {} 71 | }); 72 | } 73 | 74 | Integer getKeyCode(String keyString){ 75 | return KeyEvent.keyCodeFromString("KEYCODE_"+keyString); 76 | } 77 | 78 | String getKeyName(Integer keyCode){ 79 | return KeyEvent.keyCodeToString(keyCode).replace("KEYCODE_",""); 80 | } 81 | 82 | int getPosition(String value, ArrayAdapter arrayAdapter){ 83 | try { 84 | return arrayAdapter.getPosition(value); 85 | } catch (Exception e){ 86 | return 0; 87 | } 88 | } 89 | 90 | public void setKey(int keyCode){ 91 | this.keyCode = keyCode; 92 | setSelection(getPosition(getKeyName(keyCode), adapter)); 93 | } 94 | 95 | public int getKey(){ 96 | return this.keyCode; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Cancel 3 | Close 4 | Do you want to close the game? 5 | Error 6 | Sorry, could not save the gamepad configuration. 7 | No 8 | OK 9 | Yes 10 | Button Opacity 11 | Button Size 12 | Left Button 13 | Right Button 14 | Left Mini Button 15 | Right Mini Button 16 | First Button 17 | Second Button 18 | Third Button 19 | Fourth Button 20 | Fifth Button 21 | Sixth Button 22 | Save 23 | Main 24 | Alternative 25 | 26 | 27 | 0 28 | 1 29 | 2 30 | 3 31 | 4 32 | 4 33 | 5 34 | 6 35 | 7 36 | 8 37 | 9 38 | A 39 | B 40 | C 41 | D 42 | E 43 | F 44 | G 45 | H 46 | I 47 | J 48 | K 49 | L 50 | M 51 | N 52 | O 53 | P 54 | Q 55 | R 56 | S 57 | T 58 | U 59 | V 60 | W 61 | X 62 | Y 63 | Z 64 | ALT_LEFT 65 | ALT_RIGHT 66 | SHIFT_LEFT 67 | SHIFT_RIGHT 68 | TAB 69 | SPACE 70 | ENTER 71 | ESCAPE 72 | DEL 73 | PAGE_UP 74 | PAGE_DOWN 75 | CTRL_LEFT 76 | CTRL_RIGHT 77 | F1 78 | F2 79 | F3 80 | F4 81 | F5 82 | F6 83 | F7 84 | F8 85 | F9 86 | F10 87 | F11 88 | F12 89 | 90 | 91 | 0 92 | 10 93 | 20 94 | 30 95 | 40 96 | 50 97 | 60 98 | 70 99 | 80 100 | 90 101 | 100 102 | 103 | 104 | 60 105 | 70 106 | 80 107 | 90 108 | 100 109 | 110 110 | 120 111 | 130 112 | 140 113 | 150 114 | 115 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/util/KeyboardUtils.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.util; 2 | 3 | import android.view.KeyEvent; 4 | 5 | import java.util.Collections; 6 | import java.util.HashMap; 7 | import java.util.LinkedHashMap; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class KeyboardUtils { 13 | public static List> getKeyboardLayoutSpecial() { 14 | List> keyList = new LinkedList<>(); 15 | 16 | Map specialFirstRow = new LinkedHashMap<>(); 17 | specialFirstRow.put(KeyEvent.KEYCODE_1, "1"); 18 | specialFirstRow.put(KeyEvent.KEYCODE_2, "2"); 19 | specialFirstRow.put(KeyEvent.KEYCODE_3, "3"); 20 | specialFirstRow.put(KeyEvent.KEYCODE_4, "4"); 21 | specialFirstRow.put(KeyEvent.KEYCODE_5, "5"); 22 | specialFirstRow.put(KeyEvent.KEYCODE_6, "6"); 23 | specialFirstRow.put(KeyEvent.KEYCODE_7, "7"); 24 | specialFirstRow.put(KeyEvent.KEYCODE_8, "8"); 25 | specialFirstRow.put(KeyEvent.KEYCODE_9, "9"); 26 | specialFirstRow.put(KeyEvent.KEYCODE_0, "0"); 27 | 28 | Map specialSecondRow = new LinkedHashMap<>(); 29 | specialSecondRow.put(KeyEvent.KEYCODE_AT, "@"); 30 | specialSecondRow.put(KeyEvent.KEYCODE_POUND, "#"); 31 | specialSecondRow.put(KeyEvent.KEYCODE_MINUS, "-"); 32 | specialSecondRow.put(KeyEvent.KEYCODE_NUMPAD_ADD, "+"); 33 | specialSecondRow.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY, "*"); 34 | specialSecondRow.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE, "/"); 35 | specialSecondRow.put(KeyEvent.KEYCODE_EQUALS, "="); 36 | specialSecondRow.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN, "("); 37 | specialSecondRow.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN, ")"); 38 | specialSecondRow.put(KeyEvent.KEYCODE_BACKSLASH, "\\"); 39 | 40 | Map specialThirdRow = new LinkedHashMap<>(); 41 | specialThirdRow.put(KeyEvent.KEYCODE_SWITCH_CHARSET, "ABC"); 42 | specialThirdRow.put(KeyEvent.KEYCODE_GRAVE, "`"); 43 | specialThirdRow.put(KeyEvent.KEYCODE_APOSTROPHE, "'"); 44 | specialThirdRow.put(KeyEvent.KEYCODE_PERIOD, "."); 45 | specialThirdRow.put(KeyEvent.KEYCODE_COMMA, ","); 46 | specialThirdRow.put(KeyEvent.KEYCODE_SEMICOLON, ";"); 47 | specialThirdRow.put(KeyEvent.KEYCODE_LEFT_BRACKET, "["); 48 | specialThirdRow.put(KeyEvent.KEYCODE_RIGHT_BRACKET, "]"); 49 | specialThirdRow.put(KeyEvent.KEYCODE_DEL, "⌫"); 50 | 51 | Map specialFourthRow = new LinkedHashMap<>(); 52 | specialFourthRow.put(KeyEvent.KEYCODE_SHIFT_LEFT, "⇧"); 53 | specialFourthRow.put(KeyEvent.KEYCODE_SPACE, " Space "); 54 | specialFourthRow.put(KeyEvent.KEYCODE_ENTER, "⏎"); 55 | 56 | keyList.add(specialFirstRow); 57 | keyList.add(specialSecondRow); 58 | keyList.add(specialThirdRow); 59 | keyList.add(specialFourthRow); 60 | 61 | return keyList; 62 | } 63 | 64 | public static List> getKeyboardLayout(){ 65 | List> keyList = new LinkedList<>(); 66 | 67 | Map firstRow = new LinkedHashMap<>(); 68 | firstRow.put(KeyEvent.KEYCODE_Q, "Q"); 69 | firstRow.put(KeyEvent.KEYCODE_W, "W"); 70 | firstRow.put(KeyEvent.KEYCODE_E, "E"); 71 | firstRow.put(KeyEvent.KEYCODE_R, "R"); 72 | firstRow.put(KeyEvent.KEYCODE_T, "T"); 73 | firstRow.put(KeyEvent.KEYCODE_Y, "Y"); 74 | firstRow.put(KeyEvent.KEYCODE_U, "U"); 75 | firstRow.put(KeyEvent.KEYCODE_I, "I"); 76 | firstRow.put(KeyEvent.KEYCODE_O, "O"); 77 | firstRow.put(KeyEvent.KEYCODE_P, "P"); 78 | 79 | Map secondRow = new LinkedHashMap<>(); 80 | secondRow.put(KeyEvent.KEYCODE_A, "A"); 81 | secondRow.put(KeyEvent.KEYCODE_S, "S"); 82 | secondRow.put(KeyEvent.KEYCODE_D, "D"); 83 | secondRow.put(KeyEvent.KEYCODE_F, "F"); 84 | secondRow.put(KeyEvent.KEYCODE_G, "G"); 85 | secondRow.put(KeyEvent.KEYCODE_H, "H"); 86 | secondRow.put(KeyEvent.KEYCODE_J, "J"); 87 | secondRow.put(KeyEvent.KEYCODE_K, "K"); 88 | secondRow.put(KeyEvent.KEYCODE_L, "L"); 89 | secondRow.put(KeyEvent.KEYCODE_DPAD_UP, "↑"); 90 | secondRow.put(KeyEvent.KEYCODE_DEL, "⌫"); 91 | 92 | Map thirdRow = new LinkedHashMap<>(); 93 | thirdRow.put(KeyEvent.KEYCODE_SWITCH_CHARSET, "123"); 94 | thirdRow.put(KeyEvent.KEYCODE_Z, "Z"); 95 | thirdRow.put(KeyEvent.KEYCODE_X, "X"); 96 | thirdRow.put(KeyEvent.KEYCODE_C, "C"); 97 | thirdRow.put(KeyEvent.KEYCODE_V, "V"); 98 | thirdRow.put(KeyEvent.KEYCODE_B, "B"); 99 | thirdRow.put(KeyEvent.KEYCODE_N, "N"); 100 | thirdRow.put(KeyEvent.KEYCODE_M, "M"); 101 | thirdRow.put(KeyEvent.KEYCODE_DPAD_LEFT, "←"); 102 | thirdRow.put(KeyEvent.KEYCODE_DPAD_DOWN, "↓"); 103 | thirdRow.put(KeyEvent.KEYCODE_DPAD_RIGHT, "→"); 104 | 105 | Map fourthRow = new LinkedHashMap<>(); 106 | fourthRow.put(KeyEvent.KEYCODE_SHIFT_LEFT, "⇧"); 107 | fourthRow.put(KeyEvent.KEYCODE_SPACE, " Space "); 108 | fourthRow.put(KeyEvent.KEYCODE_ENTER, "⏎"); 109 | 110 | keyList.add(firstRow); 111 | keyList.add(secondRow); 112 | keyList.add(thirdRow); 113 | keyList.add(fourthRow); 114 | 115 | return keyList; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/util/ViewUtils.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.util; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | import android.util.Log; 6 | import android.view.MotionEvent; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.Button; 10 | import android.widget.ImageButton; 11 | import android.widget.ImageView; 12 | 13 | import java.lang.reflect.Field; 14 | 15 | import cyou.joiplay.joipad.R; 16 | import cyou.joiplay.joipad.view.GamePadButton; 17 | 18 | public class ViewUtils { 19 | static public void changeOpacity(ViewGroup viewGroup, int opacity){ 20 | for (int i=0; viewGroup.getChildCount() > i; i++){ 21 | View view = viewGroup.getChildAt(i); 22 | if (view instanceof Button){ 23 | view.getBackground().setAlpha(Math.round(opacity*2.25f)); 24 | view.setAlpha(((opacity + 20) / 100f)*2); 25 | } else if(view instanceof GamePadButton){ 26 | ((GamePadButton) view).setImageAlpha(Math.round(Math.min(opacity+20, 100)*2.55f)); 27 | } else if (view instanceof ViewGroup){ 28 | changeOpacity((ViewGroup) view, opacity); 29 | } 30 | } 31 | } 32 | 33 | static public void resize(View view, float scale){ 34 | ViewGroup.LayoutParams lParams = view.getLayoutParams(); 35 | if (lParams.width > 0) 36 | lParams.width = Math.round(lParams.width * (scale/100f)); 37 | 38 | if (lParams.height > 0) 39 | lParams.height = Math.round(lParams.height * (scale/100f)); 40 | 41 | view.setLayoutParams(lParams); 42 | 43 | int tPadding = Math.round(view.getPaddingTop() * (scale/100f)); 44 | int bPadding = Math.round(view.getPaddingBottom() * (scale/100f)); 45 | int lPadding = Math.round(view.getPaddingLeft() * (scale/100f)); 46 | int rPadding = Math.round(view.getPaddingRight() * (scale/100f)); 47 | view.setPadding(lPadding, tPadding, rPadding, bPadding); 48 | 49 | if (view instanceof ViewGroup){ 50 | for (int i=0; ((ViewGroup) view).getChildCount() > i; i++){ 51 | View v = ((ViewGroup) view).getChildAt(i); 52 | if (v != null) 53 | resize(v,scale); 54 | } 55 | } 56 | } 57 | 58 | static public int sdpToPx(Context context, int sdp){ 59 | try{ 60 | Field field = R.dimen.class.getDeclaredField(String.format("_%ssdp", sdp)); 61 | int id = field.getInt(field); 62 | return Math.round(context.getResources().getDimension(id)); 63 | } catch (Exception e){ 64 | return 0; 65 | } 66 | } 67 | 68 | public enum GridMovement{ 69 | XY, 70 | X, 71 | Y 72 | } 73 | 74 | 75 | public static class MovementData{ 76 | public long downTime = 0L; 77 | public float x0 = 0; 78 | public float y0 = 0; 79 | public float x1 = 0; 80 | public float y1 = 0; 81 | } 82 | 83 | static public boolean onMoveView(View view, MotionEvent motionEvent, MovementData movementData, GridMovement grid, int slope){ 84 | long longTime = 500L; 85 | switch (motionEvent.getAction()){ 86 | case MotionEvent.ACTION_DOWN: 87 | movementData.downTime = System.currentTimeMillis(); 88 | movementData.x0 = view.getX(); 89 | movementData.y0 = view.getY(); 90 | 91 | movementData.x1 = view.getX() - motionEvent.getRawX(); 92 | movementData.y1 = view.getY() - motionEvent.getRawY(); 93 | break; 94 | case MotionEvent.ACTION_MOVE: 95 | switch (grid){ 96 | case X: 97 | if (Math.abs(movementData.x1) > slope) 98 | view.animate() 99 | .x(motionEvent.getRawX() + movementData.x1 - (view.getWidth() / 2f)) 100 | .setDuration(0) 101 | .start(); 102 | break; 103 | case Y: 104 | if (Math.abs(movementData.y1) > slope) 105 | view.animate() 106 | .y(motionEvent.getRawY() + movementData.y1 - (view.getHeight() / 2f)) 107 | .setDuration(0) 108 | .start(); 109 | break; 110 | case XY: 111 | if (Math.abs(movementData.x1) > slope || Math.abs(movementData.y1) > slope) 112 | view.animate() 113 | .x(motionEvent.getRawX() + movementData.x1 - (view.getWidth() / 2f)) 114 | .y(motionEvent.getRawY() + movementData.y1 - (view.getHeight() / 2f)) 115 | .setDuration(0) 116 | .start(); 117 | break; 118 | } 119 | break; 120 | case MotionEvent.ACTION_CANCEL: 121 | case MotionEvent.ACTION_UP: 122 | if (System.currentTimeMillis() - movementData.downTime < longTime){ 123 | view.animate() 124 | .x(movementData.x0) 125 | .y(movementData.y0) 126 | .setDuration(0) 127 | .start(); 128 | } 129 | break; 130 | default: 131 | return false; 132 | } 133 | 134 | return false; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/view/GamePadDPad.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.KeyEvent; 6 | import android.view.MotionEvent; 7 | 8 | import cyou.joiplay.joipad.util.DirectionUtils; 9 | import cyou.joiplay.joipad.util.DirectionUtils.Direction; 10 | 11 | public class GamePadDPad extends GamePadButton { 12 | 13 | private OnKeyDownListener mOnKeyDownListener = key -> {}; 14 | 15 | private OnKeyUpListener mOnKeyUpListener = key -> {}; 16 | 17 | private Direction angle = Direction.UNKNOWN; 18 | private Direction nAngle = Direction.UNKNOWN; 19 | private float posX = 0f; 20 | private float posY = 0f; 21 | 22 | public Boolean isDiagonal = false; 23 | 24 | 25 | public GamePadDPad(Context context) { 26 | super(context); 27 | } 28 | 29 | public GamePadDPad(Context context, AttributeSet attrs) { 30 | super(context, attrs); 31 | } 32 | 33 | public GamePadDPad(Context context, AttributeSet attrs, int defStyleAttr) { 34 | super(context, attrs, defStyleAttr); 35 | } 36 | 37 | public GamePadDPad(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 38 | super(context, attrs, defStyleAttr, defStyleRes); 39 | } 40 | 41 | public void setOnKeyDownListener(OnKeyDownListener onKeyDownListener){ 42 | mOnKeyDownListener = onKeyDownListener; 43 | } 44 | 45 | public void setOnKeyUpListener(OnKeyUpListener onKeyUpListener){ 46 | mOnKeyUpListener = onKeyUpListener; 47 | } 48 | 49 | @Override 50 | public boolean onTouchEvent(MotionEvent event) { 51 | float initX = this.getWidth() / 2f; 52 | float initY = this.getHeight() / 2f; 53 | posX = this.getWidth() / 2f; 54 | posY = this.getHeight() / 2f; 55 | 56 | switch (event.getAction()){ 57 | case MotionEvent.ACTION_DOWN: 58 | case MotionEvent.ACTION_MOVE: 59 | case MotionEvent.ACTION_POINTER_DOWN: 60 | posX = event.getX(); 61 | posY = event.getY(); 62 | nAngle = DirectionUtils.getAngle(initX,posX,initY,posY,isDiagonal); 63 | if (angle == nAngle){ 64 | return false; 65 | } else { 66 | switch (angle){ 67 | case UP: 68 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_UP); 69 | break; 70 | case UP_RIGHT: 71 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_UP); 72 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT); 73 | break; 74 | case UP_LEFT: 75 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_UP); 76 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_LEFT); 77 | break; 78 | case DOWN: 79 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_DOWN); 80 | break; 81 | case DOWN_RIGHT: 82 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_DOWN); 83 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT); 84 | break; 85 | case DOWN_LEFT: 86 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_DOWN); 87 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_LEFT); 88 | break; 89 | case RIGHT: 90 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT); 91 | break; 92 | case LEFT: 93 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_LEFT); 94 | break; 95 | } 96 | 97 | angle = nAngle; 98 | 99 | switch (angle){ 100 | case UP: 101 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_UP); 102 | break; 103 | case UP_RIGHT: 104 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_UP); 105 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT); 106 | break; 107 | case UP_LEFT: 108 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_UP); 109 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_LEFT); 110 | break; 111 | case DOWN: 112 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_DOWN); 113 | break; 114 | case DOWN_RIGHT: 115 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_DOWN); 116 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT); 117 | break; 118 | case DOWN_LEFT: 119 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_DOWN); 120 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_LEFT); 121 | break; 122 | case RIGHT: 123 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT); 124 | break; 125 | case LEFT: 126 | mOnKeyDownListener.onKeyDown(KeyEvent.KEYCODE_DPAD_LEFT); 127 | break; 128 | } 129 | } 130 | break; 131 | case MotionEvent.ACTION_UP: 132 | case MotionEvent.ACTION_CANCEL: 133 | case MotionEvent.ACTION_POINTER_UP: 134 | case MotionEvent.ACTION_OUTSIDE: 135 | posX = this.getWidth() / 2f; 136 | posY = this.getHeight() / 2f; 137 | 138 | switch (angle){ 139 | case UP: 140 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_UP); 141 | break; 142 | case UP_RIGHT: 143 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_UP); 144 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT); 145 | break; 146 | case UP_LEFT: 147 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_UP); 148 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_LEFT); 149 | break; 150 | case DOWN: 151 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_DOWN); 152 | break; 153 | case DOWN_RIGHT: 154 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_DOWN); 155 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT); 156 | break; 157 | case DOWN_LEFT: 158 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_DOWN); 159 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_LEFT); 160 | break; 161 | case RIGHT: 162 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_RIGHT); 163 | break; 164 | case LEFT: 165 | mOnKeyUpListener.onKeyUp(KeyEvent.KEYCODE_DPAD_LEFT); 166 | break; 167 | } 168 | angle = Direction.UNKNOWN; 169 | break; 170 | } 171 | 172 | return true; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/view/GamePadButton.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.view; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Canvas; 7 | import android.graphics.Color; 8 | import android.graphics.Paint; 9 | import android.graphics.Typeface; 10 | import android.graphics.drawable.Drawable; 11 | import android.media.Image; 12 | import android.os.Environment; 13 | import android.util.AttributeSet; 14 | import android.util.Log; 15 | import android.util.TypedValue; 16 | import android.view.KeyEvent; 17 | import android.view.MotionEvent; 18 | import android.widget.ImageButton; 19 | import android.widget.ImageView; 20 | 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.File; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | 26 | import cyou.joiplay.joipad.R; 27 | import cyou.joiplay.joipad.animation.ButtonAnimations; 28 | import cyou.joiplay.joipad.drawable.TextDrawable; 29 | 30 | public class GamePadButton extends ImageView { 31 | enum Variant{ 32 | Action, 33 | Gamepad 34 | } 35 | 36 | Variant mVariant = Variant.Action; 37 | Drawable mBackgroundDrawable; 38 | Drawable mForegroundDrawable; 39 | String mForegroundText; 40 | Integer mBackgroundTint; 41 | Integer mForegroundTint; 42 | 43 | Bitmap mBitmap; 44 | 45 | private int mTextSize = 32; 46 | private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 47 | 48 | public interface OnKeyDownListener{ 49 | void onKeyDown(int key); 50 | } 51 | 52 | public interface OnKeyUpListener{ 53 | void onKeyUp(int key); 54 | } 55 | 56 | private OnKeyDownListener mOnKeyDownListener = key -> {}; 57 | 58 | private OnKeyUpListener mOnKeyUpListener = key -> {}; 59 | 60 | private int mKey = 0; 61 | 62 | public GamePadButton(Context context) { 63 | super(context); 64 | } 65 | 66 | public GamePadButton(Context context, AttributeSet attrs) { 67 | super(context, attrs); 68 | initGamepadButton(attrs, null); 69 | } 70 | 71 | public GamePadButton(Context context, AttributeSet attrs, int defStyleAttr) { 72 | super(context, attrs, defStyleAttr); 73 | initGamepadButton(attrs, defStyleAttr); 74 | } 75 | 76 | public GamePadButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 77 | super(context, attrs, defStyleAttr, defStyleRes); 78 | initGamepadButton(attrs, defStyleAttr); 79 | } 80 | 81 | public void initGamepadButton(AttributeSet attrs, Integer defStyleAttr){ 82 | if (attrs != null){ 83 | TypedArray a; 84 | if (defStyleAttr != null){ 85 | a = getContext().obtainStyledAttributes(attrs, R.styleable.GamePadButton, defStyleAttr, R.style.GamePadButton); 86 | } else { 87 | a = getContext().obtainStyledAttributes(attrs, R.styleable.GamePadButton); 88 | } 89 | 90 | int variant = a.getInt(R.styleable.GamePadButton_gp_variant, 0); 91 | String foregroundText = a.getString(R.styleable.GamePadButton_gp_foregroundText); 92 | Drawable backgroundDrawable = a.getDrawable(R.styleable.GamePadButton_gp_backgroundDrawable); 93 | Drawable foregroundDrawable = a.getDrawable(R.styleable.GamePadButton_gp_foregroundDrawable); 94 | int backgroundTint = a.getInteger(R.styleable.GamePadButton_gp_backgroundTint, Color.rgb(32,32,32)); 95 | int foregroundTint = a.getInteger(R.styleable.GamePadButton_gp_foregroundTint, Color.rgb(255,255,255)); 96 | 97 | this.mVariant = variant == 2 ? Variant.Action : Variant.Gamepad; 98 | this.mBackgroundDrawable = backgroundDrawable; 99 | this.mForegroundText = foregroundText; 100 | this.mForegroundDrawable = foregroundDrawable; 101 | this.mBackgroundTint = backgroundTint; 102 | this.mForegroundTint = foregroundTint; 103 | } 104 | } 105 | 106 | @Override 107 | public void setBackgroundDrawable(Drawable backgroundDrawable){ 108 | this.mBackgroundDrawable = backgroundDrawable; 109 | 110 | initBitmap(); 111 | } 112 | 113 | public void setBackgroundDrawableResource(int backgroundDrawableResource){ 114 | Drawable backgroundDrawable = getContext().getResources().getDrawable(backgroundDrawableResource); 115 | 116 | setBackgroundDrawable(backgroundDrawable); 117 | } 118 | 119 | public void setForegroundDrawable(Drawable foregroundDrawable){ 120 | this.mForegroundDrawable = foregroundDrawable; 121 | this.mForegroundText = null; 122 | 123 | initBitmap(); 124 | } 125 | 126 | public void setForegroundDrawableResource(int foregroundDrawableResource){ 127 | Drawable foregroundDrawable = getContext().getResources().getDrawable(foregroundDrawableResource); 128 | 129 | setForegroundDrawable(foregroundDrawable); 130 | } 131 | 132 | public void setForegroundText(String text){ 133 | this.mForegroundText = text; 134 | this.mForegroundDrawable = null; 135 | 136 | initBitmap(); 137 | } 138 | 139 | public void setBackgroundTint(Integer tint) { 140 | this.mBackgroundTint = tint; 141 | } 142 | 143 | public void setForegroundTint(Integer tint) { 144 | this.mForegroundTint = tint; 145 | } 146 | 147 | public void initBitmap(){ 148 | if(this.getWidth() < 1 || this.getHeight() < 1) return; 149 | 150 | //Reset text size 151 | mTextSize = 32; 152 | 153 | Bitmap tmpBitmap = Bitmap.createBitmap(this.getWidth(), this.getHeight(), Bitmap.Config.ARGB_8888); 154 | Canvas tmpCanvas = new Canvas(tmpBitmap); 155 | 156 | if (mBackgroundDrawable != null) { 157 | mBackgroundDrawable.setBounds(0, 0, tmpCanvas.getWidth(), tmpCanvas.getHeight()); 158 | mBackgroundDrawable.setTint(mBackgroundTint); 159 | mBackgroundDrawable.draw(tmpCanvas); 160 | } 161 | 162 | int w = (int) Math.round(tmpCanvas.getWidth()*0.9); 163 | int h = (int) Math.round(tmpCanvas.getHeight()*0.9); 164 | int pw = (tmpCanvas.getWidth() - w)/2; 165 | int ph = (tmpCanvas.getHeight() - h)/2; 166 | 167 | if (mForegroundDrawable != null){ 168 | mForegroundDrawable.setBounds(pw, ph, w+pw, h+ph); 169 | mForegroundDrawable.setTint(mForegroundTint); 170 | mForegroundDrawable.draw(tmpCanvas); 171 | } 172 | 173 | if (mForegroundText != null){ 174 | initPaint(); 175 | tmpCanvas.drawText(mForegroundText, 0, mForegroundText.length(), tmpCanvas.getWidth() /2, tmpCanvas.getHeight() / 2 - mPaint.ascent() / 2f, mPaint); 176 | } 177 | 178 | this.mBitmap = tmpBitmap; 179 | 180 | this.setImageBitmap(mBitmap); 181 | } 182 | 183 | private void initPaint(){ 184 | mPaint.setColor(mForegroundTint); 185 | mPaint.setTypeface(Typeface.create("sans-serif",Typeface.BOLD)); 186 | mPaint.setTextAlign(Paint.Align.CENTER); 187 | mPaint.setTextSize(getTextSize()); 188 | } 189 | 190 | private int getTextSize(){ 191 | mPaint.setTextSize(mTextSize); 192 | int mIntrinsicWidth = (int) Math.round(mPaint.measureText(mForegroundText, 0, mForegroundText.length()) + .5); 193 | int mIntrinsicHeight = mPaint.getFontMetricsInt(null); 194 | 195 | while(mIntrinsicHeight > this.getHeight() * 0.85 || mIntrinsicWidth > this.getWidth() * 0.85){ 196 | mTextSize -= 2; 197 | mPaint.setTextSize(mTextSize); 198 | mIntrinsicWidth = (int) Math.round(mPaint.measureText(mForegroundText, 0, mForegroundText.length()) + .5); 199 | mIntrinsicHeight = mPaint.getFontMetricsInt(null); 200 | } 201 | 202 | return mTextSize; 203 | } 204 | 205 | public void setOnKeyDownListener(OnKeyDownListener onKeyDownListener){ 206 | mOnKeyDownListener = onKeyDownListener; 207 | } 208 | 209 | public void setOnKeyUpListener(OnKeyUpListener onKeyUpListener){ 210 | mOnKeyUpListener = onKeyUpListener; 211 | } 212 | 213 | public void setKey(int key){ 214 | mKey = key; 215 | initBitmap(); 216 | } 217 | 218 | @Override 219 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 220 | super.onLayout(changed, left, top, right, bottom); 221 | initBitmap(); 222 | } 223 | 224 | @Override 225 | public boolean onTouchEvent(MotionEvent event) { 226 | if (mVariant == Variant.Action) return super.onTouchEvent(event); 227 | switch (event.getAction()) { 228 | case MotionEvent.ACTION_DOWN: 229 | ButtonAnimations.animateTouch(getContext(), this, true); 230 | mOnKeyDownListener.onKeyDown(mKey); 231 | break; 232 | case MotionEvent.ACTION_CANCEL: 233 | case MotionEvent.ACTION_UP: 234 | ButtonAnimations.animateTouch(getContext(), this, false); 235 | mOnKeyUpListener.onKeyUp(mKey); 236 | break; 237 | } 238 | return true; 239 | } 240 | 241 | @Override 242 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 243 | super.onSizeChanged(w, h, oldw, oldh); 244 | initBitmap(); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/main/java/cyou/joiplay/joipad/dialog/SettingsDialog.java: -------------------------------------------------------------------------------- 1 | package cyou.joiplay.joipad.dialog; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Environment; 6 | import android.util.Log; 7 | import android.view.KeyEvent; 8 | import android.view.View; 9 | import android.view.Window; 10 | import android.view.WindowManager; 11 | import android.widget.AdapterView; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.Button; 14 | import android.widget.Spinner; 15 | 16 | import java.io.File; 17 | 18 | import cyou.joiplay.commons.dialog.ConfirmationDialog; 19 | import cyou.joiplay.commons.model.Game; 20 | import cyou.joiplay.commons.model.GamePad; 21 | import cyou.joiplay.commons.parser.GamePadParser; 22 | import cyou.joiplay.joipad.R; 23 | import cyou.joiplay.joipad.view.KeyCodeSpinner; 24 | 25 | public class SettingsDialog{ 26 | private static final String TAG = "SettingsDialog"; 27 | private Context mContext; 28 | private GamePad mGamePad; 29 | private Game mGame; 30 | private Dialog mDialog; 31 | 32 | public interface OnSettingsChanged{ 33 | void onSettingsChanged(GamePad gamePad); 34 | } 35 | 36 | private OnSettingsChanged mOnSettingsChanged; 37 | 38 | public SettingsDialog(Context context, GamePad gamePad, Game game){ 39 | mContext = context; 40 | mGamePad = gamePad; 41 | mGame = game; 42 | 43 | init(); 44 | } 45 | 46 | void init(){ 47 | mDialog = new Dialog(mContext); 48 | mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 49 | mDialog.setCancelable(true); 50 | mDialog.setContentView(R.layout.settings_layout); 51 | 52 | Spinner opacitySpinner = mDialog.findViewById(R.id.opacitySpinner); 53 | Spinner sizeSpinner = mDialog.findViewById(R.id.sizeSpinner); 54 | KeyCodeSpinner leftSpinner = mDialog.findViewById(R.id.leftSpinner); 55 | KeyCodeSpinner rightSpinner = mDialog.findViewById(R.id.rightSpinner); 56 | KeyCodeSpinner cleftSpinner = mDialog.findViewById(R.id.cleftSpinner); 57 | KeyCodeSpinner crightSpinner = mDialog.findViewById(R.id.crightSpinner); 58 | KeyCodeSpinner firstSpinner = mDialog.findViewById(R.id.firstSpinner); 59 | KeyCodeSpinner secondSpinner = mDialog.findViewById(R.id.secondSpinner); 60 | KeyCodeSpinner thirdSpinner = mDialog.findViewById(R.id.thirdSpinner); 61 | KeyCodeSpinner fourthSpinner = mDialog.findViewById(R.id.fourthSpinner); 62 | KeyCodeSpinner fifthSpinner = mDialog.findViewById(R.id.fifthSpinner); 63 | KeyCodeSpinner sixthSpinner = mDialog.findViewById(R.id.sixthSpinner); 64 | KeyCodeSpinner leftSpinner1 = mDialog.findViewById(R.id.leftSpinner1); 65 | KeyCodeSpinner rightSpinner1 = mDialog.findViewById(R.id.rightSpinner1); 66 | KeyCodeSpinner cleftSpinner1 = mDialog.findViewById(R.id.cleftSpinner1); 67 | KeyCodeSpinner crightSpinner1 = mDialog.findViewById(R.id.crightSpinner1); 68 | KeyCodeSpinner firstSpinner1 = mDialog.findViewById(R.id.firstSpinner1); 69 | KeyCodeSpinner secondSpinner1 = mDialog.findViewById(R.id.secondSpinner1); 70 | KeyCodeSpinner thirdSpinner1 = mDialog.findViewById(R.id.thirdSpinner1); 71 | KeyCodeSpinner fourthSpinner1 = mDialog.findViewById(R.id.fourthSpinner1); 72 | KeyCodeSpinner fifthSpinner1 = mDialog.findViewById(R.id.fifthSpinner1); 73 | KeyCodeSpinner sixthSpinner1 = mDialog.findViewById(R.id.sixthSpinner1); 74 | Button saveButton = mDialog.findViewById(R.id.saveButton); 75 | 76 | leftSpinner.setKey(mGamePad.lKeyCode[0]); 77 | rightSpinner.setKey(mGamePad.rKeyCode[0]); 78 | cleftSpinner.setKey(mGamePad.clKeyCode[0]); 79 | crightSpinner.setKey(mGamePad.crKeyCode[0]); 80 | firstSpinner.setKey(mGamePad.xKeyCode[0]); 81 | secondSpinner.setKey(mGamePad.yKeyCode[0]); 82 | thirdSpinner.setKey(mGamePad.zKeyCode[0]); 83 | fourthSpinner.setKey(mGamePad.aKeyCode[0]); 84 | fifthSpinner.setKey(mGamePad.bKeyCode[0]); 85 | sixthSpinner.setKey(mGamePad.cKeyCode[0]); 86 | 87 | leftSpinner1.setKey(mGamePad.lKeyCode[1]); 88 | rightSpinner1.setKey(mGamePad.rKeyCode[1]); 89 | cleftSpinner1.setKey(mGamePad.clKeyCode[1]); 90 | crightSpinner1.setKey(mGamePad.crKeyCode[1]); 91 | firstSpinner1.setKey(mGamePad.xKeyCode[1]); 92 | secondSpinner1.setKey(mGamePad.yKeyCode[1]); 93 | thirdSpinner1.setKey(mGamePad.zKeyCode[1]); 94 | fourthSpinner1.setKey(mGamePad.aKeyCode[1]); 95 | fifthSpinner1.setKey(mGamePad.bKeyCode[1]); 96 | sixthSpinner1.setKey(mGamePad.cKeyCode[1]); 97 | 98 | ArrayAdapter opacityAdapter = ArrayAdapter.createFromResource(mContext, R.array.opacity_array, R.layout.spinner_item); 99 | ArrayAdapter sizeAdapter = ArrayAdapter.createFromResource(mContext, R.array.scale_array, R.layout.spinner_item); 100 | 101 | opacityAdapter.setDropDownViewResource(R.layout.spinner_item); 102 | sizeAdapter.setDropDownViewResource(R.layout.spinner_item); 103 | 104 | opacitySpinner.setAdapter(opacityAdapter); 105 | sizeSpinner.setAdapter(sizeAdapter); 106 | 107 | opacitySpinner.setSelection(getPosition(mGamePad.btnOpacity.toString(), opacityAdapter)); 108 | sizeSpinner.setSelection(getPosition(mGamePad.btnScale.toString(), sizeAdapter)); 109 | 110 | opacitySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 111 | @Override 112 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 113 | try { 114 | mGamePad.btnOpacity = Integer.parseInt((String) parent.getItemAtPosition(position)); 115 | } catch (Exception e){ 116 | Log.d(TAG, Log.getStackTraceString(e)); 117 | } 118 | } 119 | 120 | @Override 121 | public void onNothingSelected(AdapterView parent) {} 122 | }); 123 | 124 | sizeSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 125 | @Override 126 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 127 | try { 128 | mGamePad.btnScale = Integer.parseInt((String) parent.getItemAtPosition(position)); 129 | } catch (Exception e){ 130 | Log.d(TAG, Log.getStackTraceString(e)); 131 | } 132 | } 133 | 134 | @Override 135 | public void onNothingSelected(AdapterView parent) {} 136 | }); 137 | 138 | saveButton.setOnClickListener(v -> { 139 | GamePad gamePad = new GamePad(); 140 | gamePad.btnOpacity = mGamePad.btnOpacity; 141 | gamePad.btnScale = mGamePad.btnScale; 142 | gamePad.aKeyCode = new Integer[]{fourthSpinner.getKey(), fourthSpinner1.getKey()}; 143 | gamePad.bKeyCode = new Integer[]{fifthSpinner.getKey(), fifthSpinner1.getKey()}; 144 | gamePad.cKeyCode = new Integer[]{sixthSpinner.getKey(), sixthSpinner1.getKey()}; 145 | gamePad.xKeyCode = new Integer[]{firstSpinner.getKey(), firstSpinner1.getKey()}; 146 | gamePad.yKeyCode = new Integer[]{secondSpinner.getKey(), secondSpinner1.getKey()}; 147 | gamePad.zKeyCode = new Integer[]{thirdSpinner.getKey(), thirdSpinner1.getKey()}; 148 | gamePad.lKeyCode = new Integer[]{leftSpinner.getKey(), leftSpinner1.getKey()}; 149 | gamePad.rKeyCode = new Integer[]{rightSpinner.getKey(), rightSpinner1.getKey()}; 150 | gamePad.clKeyCode = new Integer[]{cleftSpinner.getKey(), cleftSpinner1.getKey()}; 151 | gamePad.crKeyCode = new Integer[]{crightSpinner.getKey(), crightSpinner1.getKey()}; 152 | 153 | try{ 154 | String configPath; 155 | if (mGame.folder.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())){ 156 | configPath = mGame.folder+"/gamepad.json"; 157 | } else { 158 | configPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/JoiPlay/games/"+mGame.id+"/gamepad.json"; 159 | } 160 | 161 | File configFile = new File(configPath); 162 | if (!configFile.getParentFile().exists()){ 163 | configFile.getParentFile().mkdirs(); 164 | } 165 | 166 | GamePadParser.saveToFile(gamePad, configFile); 167 | } catch (Exception e){ 168 | Log.d(TAG, Log.getStackTraceString(e)); 169 | mDialog.dismiss(); 170 | ConfirmationDialog errorDialog = new ConfirmationDialog(); 171 | errorDialog.setTitle(R.string.error); 172 | errorDialog.setMessage(R.string.could_not_save_settings); 173 | errorDialog.show(mContext); 174 | } 175 | 176 | mDialog.dismiss(); 177 | mOnSettingsChanged.onSettingsChanged(gamePad); 178 | }); 179 | } 180 | 181 | public void show(){ 182 | mDialog.show(); 183 | Window window = mDialog.getWindow(); 184 | window.setLayout(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT); 185 | } 186 | 187 | public void setOnSettingsChanged(OnSettingsChanged onSettingsChanged){ 188 | mOnSettingsChanged = onSettingsChanged; 189 | } 190 | 191 | int getPosition(String value, ArrayAdapter arrayAdapter){ 192 | try { 193 | return arrayAdapter.getPosition(value); 194 | } catch (Exception e){ 195 | Log.d(TAG, Log.getStackTraceString(e)); 196 | return 0; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/res/layout/settings_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 14 | 21 | 27 | 33 | 34 | 40 | 46 | 52 | 53 | 60 | 65 | 71 | 77 | 78 | 84 | 90 | 96 | 102 | 103 | 109 | 115 | 121 | 127 | 128 | 134 | 140 | 146 | 152 | 153 | 159 | 165 | 171 | 177 | 178 | 184 | 190 | 196 | 202 | 203 | 209 | 215 | 221 | 227 | 228 | 234 | 240 | 246 | 252 | 253 | 259 | 265 | 271 | 277 | 278 | 284 | 290 | 296 | 302 | 303 | 309 | 315 | 321 | 327 | 328 |