From c3bbf70d0db9cdd48a868544660b56bdde1cfa0f Mon Sep 17 00:00:00 2001
From: 18631081161 <2088094923@qq.com>
Date: Tue, 7 Jan 2025 19:40:39 +0800
Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E6=8B=9F=E8=93=9D=E7=89=99=E6=89=8B?=
=?UTF-8?q?=E6=9F=84.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
android/app/src/main/AndroidManifest.xml | 29 +-
.../main/java/co/steamcloud/game/Config.java | 6 +
.../co/steamcloud/game/HandleActivity.java | 478 ++++++++++++++++++
.../java/co/steamcloud/game/MainActivity.java | 419 +++++++++++----
.../co/steamcloud/game/PlayGameActivity.java | 12 +
.../game/{ => customize}/GameFrameLayOut.java | 14 +-
.../game/{ => customize}/JoystickView2.java | 85 ++--
.../game/{ => customize}/KeyButton.java | 46 +-
.../game/{ => customize}/KeyboardUtil.java | 4 +-
.../game/{ => customize}/MoveButton.java | 6 +-
.../game/{ => customize}/MyButton.java | 35 +-
.../game/{ => utils}/ActivityCollector.java | 2 +-
.../co/steamcloud/game/utils/AppUtil.java | 75 ++-
.../game/{ => utils}/CountDownTimerUtil.java | 2 +-
.../game/{ => utils}/EventBusParams.java | 2 +-
.../co/steamcloud/game/utils/GamePad.java | 245 +++++++++
.../steamcloud/game/{ => utils}/GamePara.java | 12 +-
.../steamcloud/game/utils/GamepadAction.java | 14 +
.../steamcloud/game/utils/GamepadButton.java | 27 +
.../game/{ => utils}/GsonJsonUtil.java | 4 +-
.../{ => utils}/PeterTimeCountRefresh.java | 2 +-
.../game/{ => utils}/RoundCalculator.java | 2 +-
.../game/wxapi/WXPayEntryActivity.java | 2 +-
.../app/src/main/res/layout/activity_game.xml | 10 +-
.../src/main/res/layout/activity_handle.xml | 386 ++++++++++++++
.../app/src/main/res/layout/layout_handle.xml | 64 +--
assets/images/ic_bt_device.png | Bin 0 -> 5460 bytes
assets/images/ic_bt_off.png | Bin 0 -> 3042 bytes
assets/images/ic_bt_on.png | Bin 0 -> 2534 bytes
assets/images/ic_bt_refresh.png | Bin 0 -> 5794 bytes
assets/images/ic_describe.png | Bin 0 -> 2266 bytes
lib/common/EventBusUtil.dart | 5 +
lib/dialog/bt_description_dialog.dart | 50 ++
lib/main.dart | 19 +
lib/network/NetworkConfig.dart | 6 +-
lib/tools/me/bt_game_pad_page.dart | 263 ++++++++++
lib/tools/me/my_page.dart | 39 ++
lib/tools/start_page.dart | 5 +-
38 files changed, 2117 insertions(+), 253 deletions(-)
create mode 100644 android/app/src/main/java/co/steamcloud/game/HandleActivity.java
rename android/app/src/main/java/co/steamcloud/game/{ => customize}/GameFrameLayOut.java (75%)
rename android/app/src/main/java/co/steamcloud/game/{ => customize}/JoystickView2.java (73%)
rename android/app/src/main/java/co/steamcloud/game/{ => customize}/KeyButton.java (87%)
rename android/app/src/main/java/co/steamcloud/game/{ => customize}/KeyboardUtil.java (99%)
rename android/app/src/main/java/co/steamcloud/game/{ => customize}/MoveButton.java (95%)
rename android/app/src/main/java/co/steamcloud/game/{ => customize}/MyButton.java (84%)
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/ActivityCollector.java (95%)
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/CountDownTimerUtil.java (98%)
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/EventBusParams.java (87%)
create mode 100644 android/app/src/main/java/co/steamcloud/game/utils/GamePad.java
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/GamePara.java (89%)
create mode 100644 android/app/src/main/java/co/steamcloud/game/utils/GamepadAction.java
create mode 100644 android/app/src/main/java/co/steamcloud/game/utils/GamepadButton.java
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/GsonJsonUtil.java (96%)
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/PeterTimeCountRefresh.java (97%)
rename android/app/src/main/java/co/steamcloud/game/{ => utils}/RoundCalculator.java (97%)
create mode 100644 android/app/src/main/res/layout/activity_handle.xml
create mode 100644 assets/images/ic_bt_device.png
create mode 100644 assets/images/ic_bt_off.png
create mode 100644 assets/images/ic_bt_on.png
create mode 100644 assets/images/ic_bt_refresh.png
create mode 100644 assets/images/ic_describe.png
create mode 100644 lib/dialog/bt_description_dialog.dart
create mode 100644 lib/tools/me/bt_game_pad_page.dart
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index c931bef..f27b501 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,20 +1,19 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
@@ -53,6 +52,12 @@
android:exported="false"
android:screenOrientation="landscape"
android:theme="@style/NormalTheme" />
+
-
+
\ No newline at end of file
diff --git a/android/app/src/main/java/co/steamcloud/game/Config.java b/android/app/src/main/java/co/steamcloud/game/Config.java
index 74b058f..85bb00b 100644
--- a/android/app/src/main/java/co/steamcloud/game/Config.java
+++ b/android/app/src/main/java/co/steamcloud/game/Config.java
@@ -1,9 +1,15 @@
package co.steamcloud.game;
+import android.bluetooth.BluetoothHidDevice;
+
+import co.steamcloud.game.utils.GamePara;
+
public class Config {
public static String url = "https://api.onelight.vip/";//接口域名
public static String BsUrl = "https://os.zhijierongxing.com/";//游戏域名
+ public static BluetoothHidDevice mBtHidDevice;
+
public static String channel_id = "d612a79436ef9ceeee4d6847d854b2e1";
public static String sign_key = "";//加密key
public static String userToken = "";//用户token
diff --git a/android/app/src/main/java/co/steamcloud/game/HandleActivity.java b/android/app/src/main/java/co/steamcloud/game/HandleActivity.java
new file mode 100644
index 0000000..0655655
--- /dev/null
+++ b/android/app/src/main/java/co/steamcloud/game/HandleActivity.java
@@ -0,0 +1,478 @@
+package co.steamcloud.game;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.bluetooth.BluetoothDevice;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Vibrator;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.app.ActivityCompat;
+
+import org.simple.eventbus.EventBus;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import co.steamcloud.game.customize.JoystickView2;
+import co.steamcloud.game.customize.MoveButton;
+import co.steamcloud.game.utils.AppConfigFileImpl;
+import co.steamcloud.game.utils.EventBusParams;
+import co.steamcloud.game.utils.GamePad;
+import co.steamcloud.game.utils.GamepadAction;
+import co.steamcloud.game.utils.GamepadButton;
+import co.steamcloud.game.utils.GsonJsonUtil;
+
+public class HandleActivity extends Activity implements GamepadAction {
+
+ private final String TAG = "MainActivity";
+ private int SCREEN_WIDTH = 1920;
+ private int SCREEN_HEIGHT = 1080;
+
+ private boolean isShock = false;// 震动
+ private FrameLayout visualAngle;
+ private MoveButton yaogan_left, direction_key, yaogan_right, Move_a, Move_b, Move_x, Move_y, Move_rs, Move_rt,
+ Move_rb, Move_lt, Move_lb, Move_ls, Move_back, Move_start;
+ ImageView dpad_up, dpad_right, dpad_up_left, dpad_left, dpad_down;
+ Map CustomButtonMap = new HashMap<>();
+
+ private float downJoystickX = 0, downJoystickY = 0, moveJoystickX, moveJoystickY;
+
+ private Vibrator vibrator;
+
+ private final ArrayList mDevices = new ArrayList<>();
+
+ public GamePad gamepad = new GamePad(this);
+ private TextView button_a, button_b, button_x, button_y, button_rs, button_rt, button_rb, button_lt, button_lb, button_ls, button_back, button_start;
+ private TextView tvExitHandle;
+
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ setContentView(R.layout.activity_handle);
+
+ hideBottomUIMenu();
+ initUI();
+ }
+
+ @SuppressLint("MissingPermission")
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+
+ @SuppressLint("ClickableViewAccessibility")
+ private void initUI() {
+ // 判断屏幕大小
+ DisplayMetrics dm = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(dm);// display =
+ // getWindowManager().getDefaultDisplay();display.getMetrics(dm)(把屏幕尺寸信息赋值给DisplayMetrics
+ // dm);
+ if (dm.widthPixels > dm.heightPixels) {
+ SCREEN_WIDTH = dm.widthPixels/*- getNavigationHeight(PlayGameActivity.this)*/;
+ SCREEN_HEIGHT = dm.heightPixels;
+ } else {
+ SCREEN_WIDTH = dm.heightPixels/*- getNavigationHeight(PlayGameActivity.this)*/;
+ SCREEN_HEIGHT = dm.widthPixels;
+ }
+ Globe.landscapeScaleWidht = ((float) SCREEN_WIDTH / Globe.landscapeSW);
+ Globe.landscapeScaleHeight = ((float) SCREEN_HEIGHT / Globe.landscapeSH);
+
+ visualAngle = findViewById(R.id.visual_angle);
+
+ String CustomButton = AppConfigFileImpl.getStringParams(getApplicationContext(), "CustomButton");
+ if (!TextUtils.isEmpty(CustomButton)) {
+ Log.d("TAG", "CustomButton " + CustomButton);
+ CustomButtonMap = GsonJsonUtil.stringToMap(CustomButton);
+ } else {
+ initDefault();// 默认配置
+ }
+
+ tvExitHandle = findViewById(R.id.tv_exit_handle);
+
+ tvExitHandle.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showDialog("退出手柄", "确定退出手柄吗?", "退出", "取消", new IDialogTwoView() {
+ @Override
+ public void cancel() {
+ hideBottomUIMenu();
+ }
+
+ @Override
+ public void onSure() {
+ finish();
+ }
+
+ @Override
+ public void returnApp() {
+ }
+ });
+
+ }
+ });
+
+ yaogan_left = findViewById(R.id.yaogan_left);
+ direction_key = findViewById(R.id.direction_key);
+ yaogan_right = findViewById(R.id.yaogan_right);
+ Move_a = findViewById(R.id.Move_a);
+ Move_b = findViewById(R.id.Move_b);
+ Move_x = findViewById(R.id.Move_x);
+ Move_y = findViewById(R.id.Move_y);
+ Move_rs = findViewById(R.id.Move_rs);
+ Move_rt = findViewById(R.id.Move_rt);
+ Move_rb = findViewById(R.id.Move_rb);
+ Move_lt = findViewById(R.id.Move_lt);
+ Move_lb = findViewById(R.id.Move_lb);
+ Move_ls = findViewById(R.id.Move_ls);
+ Move_back = findViewById(R.id.Move_back);
+ Move_start = findViewById(R.id.Move_start);
+
+
+ button_a = findViewById(R.id.button_a);
+ button_b = findViewById(R.id.button_b);
+ button_x = findViewById(R.id.button_x);
+ button_y = findViewById(R.id.button_y);
+ button_rs = findViewById(R.id.button_rs);
+ button_rt = findViewById(R.id.button_rt);
+ button_rb = findViewById(R.id.button_rb);
+ button_lt = findViewById(R.id.button_lt);
+ button_lb = findViewById(R.id.button_lb);
+ button_ls = findViewById(R.id.button_ls);
+ button_back = findViewById(R.id.button_back);
+ button_start = findViewById(R.id.button_start);
+
+
+ initButtonPosition();
+ dpad_up = findViewById(R.id.dpad_up);
+ dpad_left = findViewById(R.id.dpad_left);
+ dpad_down = findViewById(R.id.dpad_down);
+ dpad_right = findViewById(R.id.dpad_right);
+ dpad_up_left = findViewById(R.id.dpad_up_left);
+ dpad_up.setClickable(true);
+ dpad_left.setClickable(true);
+ dpad_down.setClickable(true);
+ dpad_right.setClickable(true);
+ dpad_up_left.setClickable(true);
+
+ JoystickView2 joystickLeft = findViewById(R.id.joystick_left);
+ JoystickView2 joystickRight = findViewById(R.id.joystick_right);
+ joystickLeft.setOnJoystickListener(new JoystickView2.OnJoystickListener() {
+ @SuppressLint("MissingPermission")
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ public void onPosition(float x, float y) {
+ // Config.keyEvent_time = 0;
+ Log.w("TAG", "stick x=" + x + " y=" + y);
+ gamepad.SetLeftRemoteSensing(x, y);
+ // System.out.println("player1 setXY x="+x+"y="+y);
+ }
+
+ @Override
+ public void onVibrator() {
+ // joystickLeft.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ // HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ if (isShock) {
+ joystickLeft.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+ });
+ joystickRight.setOnJoystickListener(new JoystickView2.OnJoystickListener() {
+ @SuppressLint("MissingPermission")
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ public void onPosition(float x, float y) {
+ // Config.keyEvent_time = 0;
+ Log.w("TAG", "stick x=" + x + " y=" + y);
+ gamepad.SetRightRemoteSensing(x, y);
+
+ }
+
+ @Override
+ public void onVibrator() {
+ // joystickLeft.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ // HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ if (isShock) {
+ joystickLeft.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
+ }
+ });
+
+ setBtnKey(button_a, gamepad.BUTTON_A);
+ setBtnKey(button_b, gamepad.BUTTON_B);
+ setBtnKey(button_x, gamepad.BUTTON_X);
+ setBtnKey(button_y, gamepad.BUTTON_Y);
+ setBtnKey(button_lb, gamepad.BUTTON_L1);
+ setBtnKey(button_rb, gamepad.BUTTON_R1);
+ setBtnKey(button_lt, gamepad.BUTTON_L2);
+ setBtnKey(button_rt, gamepad.BUTTON_R2);
+ setBtnKey(button_start, gamepad.BUTTON_START);
+ setBtnKey(button_back, gamepad.BUTTON_SELECT);
+ setBtnKey(button_ls, gamepad.BUTTON_L3);
+ setBtnKey(button_rs, gamepad.BUTTON_R3);
+ setBtnKey(dpad_up, gamepad.BUTTON_DPAD_UP);
+ setBtnKey(dpad_right, gamepad.BUTTON_DPAD_RIGHT);
+ setBtnKey(dpad_down, gamepad.BUTTON_DPAD_DOWN);
+ setBtnKey(dpad_left, gamepad.BUTTON_DPAD_LEFT);
+
+ setBtnKey(dpad_up_left, gamepad.BUTTON_DPAD_UP, gamepad.BUTTON_DPAD_LEFT);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ showDialog("退出手柄", "确定退出手柄吗?", "退出", "取消", new IDialogTwoView() {
+ @Override
+ public void cancel() {
+ hideBottomUIMenu();
+ }
+
+ @Override
+ public void onSure() {
+ finish();
+ }
+
+ @Override
+ public void returnApp() {
+ }
+ });
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+
+ }
+
+ public void showDialog(String title, String content, String cancelStr, String sureStr, final IDialogTwoView iDialogView) {
+ BaseAlertDialog baseAlertDialog = new BaseAlertDialog.Builder(this).show(title, content, cancelStr, sureStr, iDialogView).create();
+ try {
+ baseAlertDialog.show();
+ } catch (Exception e) {
+ }
+ }
+
+ public void setBtnKey(TextView btn, GamepadButton gamepadButton) {
+ btn.setOnTouchListener(createButtonTouchListener(gamepadButton));
+ }
+
+ public void setBtnKey(ImageView btn, GamepadButton gamepadButton) {
+ btn.setOnTouchListener(createButtonTouchListener(gamepadButton));
+ }
+
+ public void setBtnKey(ImageView btn, GamepadButton gamepadButton, GamepadButton gamepadButton2) {
+ btn.setOnTouchListener(createButtonTouchListener(gamepadButton, gamepadButton2));
+ }
+
+ private byte testKeyCode = (byte) 0x01;
+
+ public void initDefault() {
+ try {
+ CustomButtonMap.put("yaogan_left", 106 * Globe.landscapeScaleWidht + "@" + 653.5 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("direction_key", 565 * Globe.landscapeScaleWidht + "@" + 730.0 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("yaogan_right", 1248 * Globe.landscapeScaleWidht + "@" + 770 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_a", 1645 * Globe.landscapeScaleWidht + "@" + 799 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_b", 1774 * Globe.landscapeScaleWidht + "@" + 680 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_x", 1521 * Globe.landscapeScaleWidht + "@" + 680 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_y", 1645 * Globe.landscapeScaleWidht + "@" + 562 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_rs", 1547 * Globe.landscapeScaleWidht + "@" + 300 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_rt", 1698 * Globe.landscapeScaleWidht + "@" + 343 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_rb", 1452 * Globe.landscapeScaleWidht + "@" + 453 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_ls", 134 * Globe.landscapeScaleWidht + "@" + 303 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_lt", 44 * Globe.landscapeScaleWidht + "@" + 480 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_lb", 261 * Globe.landscapeScaleWidht + "@" + 404 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_back", 1774 * Globe.landscapeScaleWidht + "@" + 30 * Globe.landscapeScaleHeight);
+ CustomButtonMap.put("Move_start", 1774 * Globe.landscapeScaleWidht + "@" + 149 * Globe.landscapeScaleHeight);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // 调用前必须先初始化按钮
+ public void initButtonPosition() {
+ try {
+ if (CustomButtonMap.get("yaogan_left") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("yaogan_left").toString())) {
+ String[] split = CustomButtonMap.get("yaogan_left").toString().split("@");
+ yaogan_left.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("direction_key") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("direction_key").toString())) {
+ String[] split = CustomButtonMap.get("direction_key").toString().split("@");
+ direction_key.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("yaogan_right") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("yaogan_right").toString())) {
+ String[] split = CustomButtonMap.get("yaogan_right").toString().split("@");
+ yaogan_right.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_a") != null && !TextUtils.isEmpty(CustomButtonMap.get("Move_a").toString())) {
+ String[] split = CustomButtonMap.get("Move_a").toString().split("@");
+ // Log.d(TAG, "initButtonPosition: aaaaaaaaaaaaaaaaaaaaaaaa");
+ Move_a.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_b") != null && !TextUtils.isEmpty(CustomButtonMap.get("Move_b").toString())) {
+ String[] split = CustomButtonMap.get("Move_b").toString().split("@");
+ Move_b.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_x") != null && !TextUtils.isEmpty(CustomButtonMap.get("Move_x").toString())) {
+ String[] split = CustomButtonMap.get("Move_x").toString().split("@");
+ Move_x.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_y") != null && !TextUtils.isEmpty(CustomButtonMap.get("Move_y").toString())) {
+ String[] split = CustomButtonMap.get("Move_y").toString().split("@");
+ Move_y.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_rs") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_rs").toString())) {
+ String[] split = CustomButtonMap.get("Move_rs").toString().split("@");
+ Move_rs.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_rt") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_rt").toString())) {
+ String[] split = CustomButtonMap.get("Move_rt").toString().split("@");
+ Move_rt.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_rb") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_rb").toString())) {
+ String[] split = CustomButtonMap.get("Move_rb").toString().split("@");
+ Move_rb.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_lt") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_lt").toString())) {
+ String[] split = CustomButtonMap.get("Move_lt").toString().split("@");
+ Move_lt.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_lb") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_lb").toString())) {
+ String[] split = CustomButtonMap.get("Move_lb").toString().split("@");
+ Move_lb.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_ls") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_ls").toString())) {
+ String[] split = CustomButtonMap.get("Move_ls").toString().split("@");
+ Move_ls.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_back") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_back").toString())) {
+ String[] split = CustomButtonMap.get("Move_back").toString().split("@");
+ Move_back.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ if (CustomButtonMap.get("Move_start") != null
+ && !TextUtils.isEmpty(CustomButtonMap.get("Move_start").toString())) {
+ String[] split = CustomButtonMap.get("Move_start").toString().split("@");
+ Move_start.setPosition(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
+ }
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected void hideBottomUIMenu() {
+ // 隐藏虚拟按键,并且全屏
+ if (Build.VERSION.SDK_INT <= 11 && Build.VERSION.SDK_INT < 19) { // lower api
+ View v = this.getWindow().getDecorView();
+ v.setSystemUiVisibility(View.GONE);
+ } else if (Build.VERSION.SDK_INT > 19) {
+ // for new api versions.
+ View decorView = getWindow().getDecorView();
+ int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_FULLSCREEN;
+ decorView.setSystemUiVisibility(uiOptions);
+
+ }
+ }
+
+
+ private View.OnTouchListener createButtonTouchListener(GamepadButton gamepadButton) {
+ return new View.OnTouchListener() {
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ Log.d(TAG, "Button event: " + event.getAction() + ",key=");
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ gamepad.KeyDown(gamepadButton);
+ break;
+ case MotionEvent.ACTION_UP:
+ gamepad.KeyUp(gamepadButton);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ };
+ }
+
+ private View.OnTouchListener createButtonTouchListener(GamepadButton gamepadButton, GamepadButton gamepadButton2) {
+ return new View.OnTouchListener() {
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ Log.d(TAG, "Button event:2 " + event.getAction() + ",key=");
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ gamepad.KeyDown(gamepadButton);
+ gamepad.KeyDown(gamepadButton2);
+ break;
+ case MotionEvent.ACTION_UP:
+ gamepad.KeyUp(gamepadButton);
+ gamepad.KeyUp(gamepadButton2);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ };
+ }
+
+ /**
+ * 发送手柄数据
+ *
+ * @param params 字节
+ */
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ @Override
+ public void SendRequest(byte[] params) {
+
+// HashMap TagMap = new HashMap<>();
+// TagMap.put("tag", "SendRequest");
+// TagMap.put("params", params);
+// EventBus.getDefault().post(TagMap, EventBusParams.MAIN);
+
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ Log.d(TAG, "clickKey: =====>>>>" + Config.mBtHidDevice.getConnectedDevices().size());
+ for (BluetoothDevice btDev : Config.mBtHidDevice.getConnectedDevices()) {
+ // 发送 HID 报文
+ Config.mBtHidDevice.sendReport(btDev, 1, params);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/co/steamcloud/game/MainActivity.java b/android/app/src/main/java/co/steamcloud/game/MainActivity.java
index e28f6b5..80ef7c1 100644
--- a/android/app/src/main/java/co/steamcloud/game/MainActivity.java
+++ b/android/app/src/main/java/co/steamcloud/game/MainActivity.java
@@ -1,18 +1,29 @@
package co.steamcloud.game;
+import android.Manifest;
import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHidDevice;
+import android.bluetooth.BluetoothHidDeviceAppQosSettings;
+import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
+import android.bluetooth.BluetoothProfile;
+import android.content.BroadcastReceiver;
+import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.PersistableBundle;
import android.text.TextUtils;
import android.util.Log;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.app.ActivityCompat;
import com.alipay.sdk.app.PayTask;
import com.tencent.mm.opensdk.modelpay.PayReq;
@@ -25,11 +36,16 @@ import org.json.JSONObject;
import org.simple.eventbus.EventBus;
import org.simple.eventbus.Subscriber;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executors;
import co.steamcloud.game.pay.PayResult;
import co.steamcloud.game.utils.AppUtil;
+import co.steamcloud.game.utils.EventBusParams;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodCall;
@@ -39,40 +55,83 @@ import io.flutter.plugin.common.StandardMethodCodec;
public class MainActivity extends FlutterActivity {
private static final String TAG = "MainActivity";
private static final String CHANNEL = "samples.flutter.dev/battery";
-
private MethodChannel nativeChannel;
-
private final int SDK_PAY_FLAG = 1;
+ private boolean setBtState = false;
+
+ private static final int REQUEST_ENABLE_BT = 99;
+
+ private final BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter();
+
+
+
+ private BluetoothHidDeviceAppQosSettings mBluetoothHidDeviceAppQosSettings;
+
+ private BluetoothDevice mBtDevice;
+
+ private final ArrayList mDevices = new ArrayList<>();//蓝牙设备列表
+
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
WhaleCloud.getInstance().isShowLog(true);
+
+ IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ registerReceiver(mBluetoothReceiver, filter);
+
}
-// @Override
-// public boolean dispatchGenericMotionEvent(MotionEvent ev) {
-// return super.dispatchGenericMotionEvent(ev);
-//
-// }
+ // BroadcastReceiver实现
+ private final BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ // 蓝牙已关闭
+ Log.d(TAG, "onReceive: 蓝牙已关闭");
+ Map map = new HashMap<>();
+ map.put("isBtOpen", false);
+ nativeChannel.invokeMethod("isBtOpen", map);
+ break;
+ case BluetoothAdapter.STATE_TURNING_OFF:
+ // 蓝牙正在关闭
+ Log.d(TAG, "onReceive: 蓝牙正在关闭");
-// @Override
-// public boolean dispatchKeyEvent(KeyEvent event) {
-// //Log.d(TAG, "dispatchKeyEvent1: event==" + event);
-// if (event.getAction() == KeyEvent.ACTION_DOWN) {
-// // 处理按键按下事件
-// int keyCode = event.getKeyCode();
-// // 根据keyCode进行相应处理
-// Log.d(TAG, "dispatchKeyEvent: keyCode==" + keyCode);
-// }
-// return super.dispatchKeyEvent(event);
-// }
+ break;
+ case BluetoothAdapter.STATE_ON:
+ // 蓝牙已打开
+ Log.d(TAG, "onReceive: 蓝牙已打开");
+ Map BtMap = new HashMap<>();
+ BtMap.put("isBtOpen", true);
+ nativeChannel.invokeMethod("isBtOpen", BtMap);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ btListDevices();
+ }
+ break;
+ case BluetoothAdapter.STATE_TURNING_ON:
+ // 蓝牙正在打开
+ Log.d(TAG, "onReceive: 蓝牙正在打开");
+ break;
+ }
+ }
+ }
+ };
@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
+ if (Config.mBtHidDevice != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ btConnect(null); // disconnect
+ }
+ }
super.onDestroy();
}
@@ -87,6 +146,8 @@ public class MainActivity extends FlutterActivity {
exitGameMap.put("exitGame", "exitGame");
nativeChannel.invokeMethod("exitGame", exitGameMap);
break;
+
+ //微信支付回调
case "wxPaySuccess":
Map map = new HashMap<>();
map.put("wxPaySuccess", "wxPaySuccess");
@@ -98,11 +159,22 @@ public class MainActivity extends FlutterActivity {
mapError.put("payError", "payError");
nativeChannel.invokeMethod("payError", mapError);
break;
+
+ case "SendRequest":
+ byte[] params = TagMap.get("params").getBytes();
+ Log.d(TAG, "Ev: SendRequest");
+
+ break;
}
}
}
+ /**
+ * flutter to 原生 传递数据
+ *
+ * @param flutterEngine The Flutter engine.
+ */
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
@@ -112,7 +184,7 @@ public class MainActivity extends FlutterActivity {
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
- case "initData":
+ case "initData": //初始化数据
Log.d(TAG, "onMethodCall: initData");
String gameToken = call.argument("gameToken");
Config.BsUrl = call.argument("BsUrl");
@@ -124,67 +196,266 @@ public class MainActivity extends FlutterActivity {
Config.url = call.argument("Url");
initSdk(Config.BsUrl, Config.client_sid, gameToken, Config.sn);
- Log.d(TAG, "onMethodCall: initData");
- Log.d(TAG, "onMethodCall: gameToken==" + gameToken);
- Log.d(TAG, "onMethodCall: Config.client_sid==" + Config.client_sid);
- Log.d(TAG, "onMethodCall: Config.BsUrl==" + Config.BsUrl);
break;
- case "playGame":
+
+ case "setBt"://开启/关闭蓝牙
+ setBtState = Boolean.TRUE.equals(call.argument("setBtState"));
+ Log.d(TAG, "onMethodCall: setBtState==" + setBtState);
+ if (setBtState) {
+ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+ startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
+ } else {
+ if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ mBtAdapter.disable();
+ }
+ break;
+
+ case "playGame": //开始游戏
Log.d("TAG", "onMethodCall: 66666666666");
Config.gameId = call.argument("gameId");
String playGameData = call.argument("playGameData");
String isReconPlay = call.argument("isReconPlay");
-
- Log.d("TAG", "onMethodCall: gameId==" + Config.gameId);
- Log.d("TAG", "onMethodCall: playGameData==" + playGameData);
- Log.d("TAG", "onMethodCall: isReconPlay==" + isReconPlay);
-// Config.gamePara.game_key = gameId;
-// Config.gamePara.record_game_time = 5;
-// Config.gamePara.start_resolution = "720p";
-
-// initToken();
startGame(playGameData, isReconPlay);
break;
- case "getInfo":
+ case "getInfo": //获取设备信息
Map map = new HashMap<>();
Config.sn = AppUtil.getDeviceId(App.getInstance());
Config.Language = AppUtil.getSystemLanguage();
map.put("deviceID", Config.sn);
map.put("Language", Config.Language);
- result.success(map);
- Log.d(TAG, "onMethodCall: getInfo");
+ //蓝牙是否开启
+ if (mBtAdapter.isEnabled()) {
+ map.put("isBtOpen", true);
+ } else {
+ map.put("isBtOpen", false);
+ }
+ result.success(map);
break;
case "WxPay"://微信支付
String orderInfoWx = call.argument("orderInfoWx");
- Log.d("TAG", "onMethodCall: orderInfoWx===" + orderInfoWx);
-
WxPay(orderInfoWx);
-
-
break;
case "Alipay"://支付宝支付
-
- Log.d("TAG", "onMethodCall: Alipay");
-
String orderInfoZfb = call.argument("orderInfoZfb");
Alipay(orderInfoZfb);
-
break;
- case "test1":
- Log.d("TAG1", "onMethodCall: 66666666666");
+ case "getBtListDevices"://获取蓝牙设备
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ btListDevices();
+ }
+ break;
+
+ case "connectedDevice"://连接设备
+ int deviceIndex = call.argument("deviceIndex");
+ BluetoothDevice dev = mDevices.get(deviceIndex);
+ Log.d(TAG, "onItemSelected(): " + dev + " " + deviceIndex + " ");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ btConnect(dev);
+ }
+ break;
+
+ case "enterGamePad"://进入蓝牙手柄界面
+ Intent intent = new Intent(MainActivity.this, HandleActivity.class);
+ startActivity(intent);
break;
}
}
});
}
+ //设置蓝牙从设备
+ public void getProxy() {
+ mBtAdapter.getProfileProxy(App.getInstance(), new BluetoothProfile.ServiceListener() {
+ @Override
+ @SuppressLint({"NewApi", "MissingPermission"})
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (profile == BluetoothProfile.HID_DEVICE) {
+ Log.d(TAG, "Got HID device");
+ Config.mBtHidDevice = (BluetoothHidDevice) proxy;
+ mBluetoothHidDeviceAppQosSettings = new BluetoothHidDeviceAppQosSettings(1, 800, 9, 0, 11250, -1);
+ BluetoothHidDeviceAppSdpSettings sdp = new BluetoothHidDeviceAppSdpSettings(
+ "虚拟蓝牙手柄",
+ "Android BLE HID Keyboard",
+ "Android",
+ (byte) 0x00,
+ AppUtil.gamePadDescriptor);
+ Config.mBtHidDevice.registerApp(sdp, null, mBluetoothHidDeviceAppQosSettings,
+ Executors.newSingleThreadExecutor(), new BluetoothHidDevice.Callback() {
+ @Override
+ public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+ Log.v(TAG, "onGetReport: device=" + device + " type=" + type
+ + " id=" + id + " bufferSize=" + bufferSize);
+ }
+
+ @Override
+ public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+// Log.v(TAG, "onSetReport: device=" + device + " type=" + type
+// + " id=" + id + " data=" + data);
+ // 解析输出报告数据
+ if (data.length >= 2) {
+ int leftMotorStrength = data[0] & 0xFF;
+ int rightMotorStrength = data[1] & 0xFF;
+// handleVibration(leftMotorStrength, rightMotorStrength);
+ }
+ }
+
+ @Override
+ public void onConnectionStateChanged(BluetoothDevice device, final int state) {
+ Log.v(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
+ if (device.equals(mBtDevice)) {
+ Log.d(TAG, "onConnectionStateChanged: state==" + state);
+ // 0 断开的
+ // 1 连接中
+ // 2 已连接
+ // 3 正在断开连接…
+ //发送连接状态
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Map map = new HashMap<>();
+ map.put("connectionStatus", state);
+ nativeChannel.invokeMethod("connectionStatus", map);
+ }
+ });
+
+ }
+ if (state == 2) {
+ Log.d(TAG, "onConnectionStateChanged:32132132132132===》 重新new对象");
+// gamepad.setCallback((b) -> {
+// Log.d(TAG, "onConnectionStateChanged:32132132132132===》 " + b[0]);
+// });
+ }
+ }
+ });
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ if (profile == BluetoothProfile.HID_DEVICE) {
+ Log.d(TAG, "Lost HID device");
+ }
+ }
+ }, BluetoothProfile.HID_DEVICE);
+ }
+
+ //获取蓝牙设备
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ private void btListDevices() {
+ getProxy(); // 需要先启用蓝牙
+
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ Set pairedDevices = mBtAdapter.getBondedDevices();
+
+ // 将设备添加到适配器
+ List names = new ArrayList<>();
+
+ for (BluetoothDevice btDev : pairedDevices) {
+ names.add(btDev.getName());
+ mDevices.add(btDev);
+ Log.d(TAG, "btListDevices: btDev==" + btDev.getName());
+ Log.d(TAG, "btListDevices: btDev==" + btDev.getAddress());
+ Log.d(TAG, "btListDevices: btDev,getUuids==" + btDev.getUuids());
+ }
+
+ Map map = new HashMap<>();
+ map.put("btDevices", names);
+ nativeChannel.invokeMethod("btDevices", map);
+
+ }
+
+
+ //连接蓝牙设备
+ @RequiresApi(api = Build.VERSION_CODES.P)
+ private void btConnect(BluetoothDevice device) {
+ Log.i(TAG, "btConnect: device=" + device);
+ Log.i(TAG, "btConnect: mBtHidDevice=" + Config.mBtHidDevice);
+ // disconnect from everything else
+ if (Config.mBtHidDevice != null) {
+ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ for (BluetoothDevice btDev : Config.mBtHidDevice.getDevicesMatchingConnectionStates(new int[]{
+ // BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_CONNECTED,
+ })) {
+ Config.mBtHidDevice.disconnect(btDev);
+ Log.i(TAG, "btConnect: disconnect");
+ }
+ }
+
+ if (device != null) {
+ mBtDevice = device;
+ Config.mBtHidDevice.connect(device);
+ }
+ }
+
+
+ /**
+ * 初始化游戏SDK
+ *
+ * @param bsUrl bsUrl
+ * @param channelId channelId
+ * @param token token
+ * @param sn 设备号
+ */
+ void initSdk(final String bsUrl, final String channelId, final String token, final String sn) {
+ WhaleCloud.getInstance().sdkLoading(getApplication(), channelId, bsUrl, token, sn, new WhaleCloud.OnSdkListener() {
+ @Override
+ public void onSdkFail(final int errCode, final String msg) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "鲸云 SDK 初始化失败|" + errCode + "|" + msg);
+ Map map = new HashMap<>();
+ map.put("SDKSuccess", false);
+ nativeChannel.invokeMethod("SDKSuccess", map);
+ }
+ });
+ }
+
+ @Override
+ public void onSdkSucc() {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Log.w(TAG, "鲸云 SDK 初始化成功");
+ Map map = new HashMap<>();
+ map.put("SDKSuccess", true);
+ nativeChannel.invokeMethod("SDKSuccess", map);
+ }
+ });
+ }
+ });
+ }
+
+ /**
+ * 启动游戏
+ *
+ * @param gameData 游戏启动数据
+ * @param isReconPlay 是否是重连
+ */
+ public void startGame(String gameData, String isReconPlay) {
+ String GameType = "";
+ getIntent().putExtra("GameType", "");
+ Config.is_Playing = true;
+ Intent intent = new Intent(this, PlayGameActivity.class);
+ intent.putExtra("GameData", gameData);
+ intent.putExtra("isReconPlay", isReconPlay);
+ startActivity(intent);
+ }
+
/**
* 支付宝 支付结果回调
@@ -226,55 +497,6 @@ public class MainActivity extends FlutterActivity {
};
- /**
- * 初始化游戏SDK
- *
- * @param bsUrl bsUrl
- * @param channelId channelId
- * @param token token
- * @param sn 设备号
- */
- void initSdk(final String bsUrl, final String channelId, final String token, final String sn) {
- WhaleCloud.getInstance().sdkLoading(getApplication(), channelId, bsUrl, token, sn, new WhaleCloud.OnSdkListener() {
- @Override
- public void onSdkFail(final int errCode, final String msg) {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Log.w(TAG, "鲸云 SDK 初始化失败|" + errCode + "|" + msg);
- Map map = new HashMap<>();
- map.put("SDKSuccess", false);
- nativeChannel.invokeMethod("SDKSuccess", map);
- }
- });
- }
-
- @Override
- public void onSdkSucc() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Log.w(TAG, "鲸云 SDK 初始化成功");
- Map map = new HashMap<>();
- map.put("SDKSuccess", true);
- nativeChannel.invokeMethod("SDKSuccess", map);
- }
- });
- }
- });
- }
-
- public void startGame(String gameData, String isReconPlay) {
- String GameType = "";
- getIntent().putExtra("GameType", "");
- Config.is_Playing = true;
- Intent intent = new Intent(this, PlayGameActivity.class);
- intent.putExtra("GameData", gameData);
- intent.putExtra("isReconPlay", isReconPlay);
- startActivity(intent);
- }
-
-
/**
* 微信支付
*
@@ -325,7 +547,6 @@ public class MainActivity extends FlutterActivity {
msg.obj = result;
mHandler.sendMessage(msg);
};
-
// 必须异步调用
Thread payThread = new Thread(payRunnable);
payThread.start();
diff --git a/android/app/src/main/java/co/steamcloud/game/PlayGameActivity.java b/android/app/src/main/java/co/steamcloud/game/PlayGameActivity.java
index e9aee9f..e5b6679 100644
--- a/android/app/src/main/java/co/steamcloud/game/PlayGameActivity.java
+++ b/android/app/src/main/java/co/steamcloud/game/PlayGameActivity.java
@@ -73,11 +73,23 @@ import java.util.Timer;
import java.util.TimerTask;
import co.steamcloud.game.HttpUtils.OpenApiRequest;
+import co.steamcloud.game.customize.GameFrameLayOut;
+import co.steamcloud.game.customize.JoystickView2;
+import co.steamcloud.game.customize.KeyButton;
+import co.steamcloud.game.customize.KeyboardUtil;
+import co.steamcloud.game.customize.MoveButton;
+import co.steamcloud.game.customize.MyButton;
import co.steamcloud.game.entity.VirtualKeyListBean;
import co.steamcloud.game.glide.GlideImageLoader;
import co.steamcloud.game.time.SubscribeTimeManage;
+import co.steamcloud.game.utils.ActivityCollector;
import co.steamcloud.game.utils.AppConfigFileImpl;
import co.steamcloud.game.utils.AppUtil;
+import co.steamcloud.game.utils.CountDownTimerUtil;
+import co.steamcloud.game.utils.EventBusParams;
+import co.steamcloud.game.utils.GsonJsonUtil;
+import co.steamcloud.game.utils.PeterTimeCountRefresh;
+import co.steamcloud.game.utils.RoundCalculator;
public class PlayGameActivity extends Activity {
public static final String TAG = "PlayGameActivity";
diff --git a/android/app/src/main/java/co/steamcloud/game/GameFrameLayOut.java b/android/app/src/main/java/co/steamcloud/game/customize/GameFrameLayOut.java
similarity index 75%
rename from android/app/src/main/java/co/steamcloud/game/GameFrameLayOut.java
rename to android/app/src/main/java/co/steamcloud/game/customize/GameFrameLayOut.java
index 7856c74..676715a 100644
--- a/android/app/src/main/java/co/steamcloud/game/GameFrameLayOut.java
+++ b/android/app/src/main/java/co/steamcloud/game/customize/GameFrameLayOut.java
@@ -1,33 +1,34 @@
-package co.steamcloud.game;
+package co.steamcloud.game.customize;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
-public class GameFrameLayOut extends FrameLayout {
+public class GameFrameLayOut extends FrameLayout {
private String TAG = "GameFrameLayOut";
int MAX_WIDTH = 1;
int MAX_HEIGHT = 1;
int SCREEN_WIDTH = 1920;
int SCREEN_HEIGHT = 1080;
- public void setScreenSize(int width,int height){
+ public void setScreenSize(int width, int height) {
SCREEN_WIDTH = width;
SCREEN_HEIGHT = height;
}
+
public GameFrameLayOut(Context context) {
- super((Context)context);
+ super((Context) context);
}
public GameFrameLayOut(Context context, AttributeSet attrs) {
- super((Context)context, attrs);
+ super((Context) context, attrs);
}
public GameFrameLayOut(Context context, AttributeSet attrs, int defStyleAttr) {
- super((Context)context, attrs, defStyleAttr);
+ super((Context) context, attrs, defStyleAttr);
}
@@ -37,6 +38,7 @@ public class GameFrameLayOut extends FrameLayout {
super.onLayout(changed, left, top, right, bottom);
}
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/android/app/src/main/java/co/steamcloud/game/JoystickView2.java b/android/app/src/main/java/co/steamcloud/game/customize/JoystickView2.java
similarity index 73%
rename from android/app/src/main/java/co/steamcloud/game/JoystickView2.java
rename to android/app/src/main/java/co/steamcloud/game/customize/JoystickView2.java
index 165fe79..97da21b 100644
--- a/android/app/src/main/java/co/steamcloud/game/JoystickView2.java
+++ b/android/app/src/main/java/co/steamcloud/game/customize/JoystickView2.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.customize;
import android.content.Context;
import android.graphics.Bitmap;
@@ -13,13 +13,17 @@ import android.view.View;
import androidx.annotation.Nullable;
+import co.steamcloud.game.Config;
+import co.steamcloud.game.R;
+import co.steamcloud.game.utils.RoundCalculator;
+
public class JoystickView2 extends View {
- public float currentX ;
- public float currentY ;
- public float initX ;
- public float initY ;
- public float currentX2 ;
- public float currentY2 ;
+ public float currentX;
+ public float currentY;
+ public float initX;
+ public float initY;
+ public float currentX2;
+ public float currentY2;
float lastX;
float lastY;
float lastVibX;
@@ -29,13 +33,14 @@ public class JoystickView2 extends View {
private float viewSizeH;
private float vibratorRanger;
public int pointSize;
- private float maxRanger=150;
+ private float maxRanger = 150;
public float maxRanger2 = 150;
public Context context;
public Bitmap map;
private Bitmap mDirectionBmp;
public static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
public OnJoystickListener mListener;
+
public JoystickView2(Context context) {
super(context);
this.context = context;
@@ -50,11 +55,12 @@ public class JoystickView2 extends View {
super(context, attrs, defStyleAttr);
this.context = context;
}
+
public Bitmap zoomImg(Bitmap bm, int newWidth, int newHeight) {
int width = bm.getWidth();
int height = bm.getHeight();
- float scaleWidth = (float)newWidth / (float)width;
- float scaleHeight = (float)newHeight / (float)height;
+ float scaleWidth = (float) newWidth / (float) width;
+ float scaleHeight = (float) newHeight / (float) height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
@@ -67,23 +73,22 @@ public class JoystickView2 extends View {
this.viewSize = this.getMeasuredWidth();
int var10000 = this.viewSize;
this.pointSize = var10000 / 3;
- viewSizeW=this.getMeasuredWidth();
- viewSizeH=this.getMeasuredHeight();
- vibratorRanger=viewSizeH/6;
- if (maxRanger>viewSizeH/4)
- {
- maxRanger=viewSizeH/4;
+ viewSizeW = this.getMeasuredWidth();
+ viewSizeH = this.getMeasuredHeight();
+ vibratorRanger = viewSizeH / 6;
+ if (maxRanger > viewSizeH / 4) {
+ maxRanger = viewSizeH / 4;
}
- if (maxRanger2>viewSizeH/3)
- {
- maxRanger2=viewSizeH/3;
+ if (maxRanger2 > viewSizeH / 3) {
+ maxRanger2 = viewSizeH / 3;
}
this.init();
}
+
public void init() {
- currentX = currentX2=initX=lastX=viewSizeW/2;
- currentY= currentY2 =initY=lastY=viewSizeH/2;
+ currentX = currentX2 = initX = lastX = viewSizeW / 2;
+ currentY = currentY2 = initY = lastY = viewSizeH / 2;
int var1 = this.pointSize;
Bitmap var10000 = BitmapFactory.decodeResource(this.context.getResources(), R.mipmap.joystick_round);
Bitmap mDirectionBmp1 = BitmapFactory.decodeResource(this.context.getResources(), R.mipmap.joystick_down);
@@ -93,7 +98,7 @@ public class JoystickView2 extends View {
}
@Override
- protected void onDraw(Canvas canvas){
+ protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setDither(true);
canvas.drawBitmap(this.map, currentX2 - this.pointSize / 2, currentY2 - this.pointSize / 2, paint);
@@ -106,7 +111,7 @@ public class JoystickView2 extends View {
int offsetY = mDirectionBmp.getHeight() / 2;
matrix.postTranslate(-offsetX, -offsetY);
matrix.postRotate(270 - rotationDegree);
- matrix.postTranslate(initX - mDirectionBmp.getWidth()/2 + offsetX, initY - mDirectionBmp.getHeight()/2 + offsetY);
+ matrix.postTranslate(initX - mDirectionBmp.getWidth() / 2 + offsetX, initY - mDirectionBmp.getHeight() / 2 + offsetY);
canvas.drawBitmap(mDirectionBmp, matrix, null);
}
@@ -115,12 +120,13 @@ public class JoystickView2 extends View {
public void setOnJoystickListener(OnJoystickListener listener) {
this.mListener = listener;
}
+
@Override
public boolean onTouchEvent(MotionEvent event) {
- if(Config.s_keyboard){
+ if (Config.s_keyboard) {
return false;
}
- if (event.getAction()==MotionEvent.ACTION_DOWN||event.getAction()==MotionEvent.ACTION_MOVE){
+ if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) {
/*if (event.getAction()==MotionEvent.ACTION_DOWN) {
initX2 = event.getX();
@@ -133,19 +139,17 @@ public class JoystickView2 extends View {
this.currentY2 = event.getY();
float tr = (float) RoundCalculator.calTwoPointDistant(initX, initY, event.getX(), event.getY());
- if (tr>maxRanger)
- {
+ if (tr > maxRanger) {
float dotCenterOnShow[] = RoundCalculator.calPointLocationByAngle(
initX, initY, event.getX(), event.getY(), maxRanger);
this.currentX = dotCenterOnShow[0];
this.currentY = dotCenterOnShow[1];
- Log.e("currentXY1","...x="+currentX2+",y="+currentY2+",maxRanger2="+maxRanger2);
+ Log.e("currentXY1", "...x=" + currentX2 + ",y=" + currentY2 + ",maxRanger2=" + maxRanger2);
}
float tr2 = (float) RoundCalculator.calTwoPointDistant(initX, initY, event.getX(), event.getY());
- if (tr2>maxRanger2)
- {
+ if (tr2 > maxRanger2) {
float dotCenterOnShow[] = RoundCalculator.calPointLocationByAngle(
initX, initY, event.getX(), event.getY(), maxRanger2);
this.currentX2 = dotCenterOnShow[0];
@@ -153,28 +157,27 @@ public class JoystickView2 extends View {
}
- }else {
- this.currentX =initX;
- this.currentY =initY;
+ } else {
+ this.currentX = initX;
+ this.currentY = initY;
this.currentX2 = initX;
this.currentY2 = initY;
}
-
- if (mListener!=null){
+ if (mListener != null) {
//if (Math.abs(currentX-lastX)>3||Math.abs(currentY-lastY)>3){
- mListener.onPosition((currentX-initX)/(float)maxRanger,(currentY-initY)/(float)maxRanger);
+ mListener.onPosition((currentX - initX) / (float) maxRanger, (currentY - initY) / (float) maxRanger);
- lastX=currentX;
- lastY=currentY;
+ lastX = currentX;
+ lastY = currentY;
// }
- if (Math.abs(currentX-lastVibX)>vibratorRanger||Math.abs(currentY-lastVibY)>vibratorRanger){//用来震动
+ if (Math.abs(currentX - lastVibX) > vibratorRanger || Math.abs(currentY - lastVibY) > vibratorRanger) {//用来震动
this.mListener.onVibrator();
- lastVibX=currentX;
- lastVibY=currentY;
+ lastVibX = currentX;
+ lastVibY = currentY;
}
}
diff --git a/android/app/src/main/java/co/steamcloud/game/KeyButton.java b/android/app/src/main/java/co/steamcloud/game/customize/KeyButton.java
similarity index 87%
rename from android/app/src/main/java/co/steamcloud/game/KeyButton.java
rename to android/app/src/main/java/co/steamcloud/game/customize/KeyButton.java
index 1753c91..08d9e00 100644
--- a/android/app/src/main/java/co/steamcloud/game/KeyButton.java
+++ b/android/app/src/main/java/co/steamcloud/game/customize/KeyButton.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.customize;
import android.content.Context;
import android.util.AttributeSet;
@@ -8,6 +8,8 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import co.steamcloud.game.Config;
+
public class KeyButton extends androidx.appcompat.widget.AppCompatTextView {
@@ -59,9 +61,10 @@ public class KeyButton extends androidx.appcompat.widget.AppCompatTextView {
public float touchInitX;
public float touchInitY;
boolean isMove = false;
+
@Override
public boolean onTouchEvent(MotionEvent event) {
- Config.keyEvent_time=0;
+ Config.keyEvent_time = 0;
/* if(!Config.s_keyboard){
return super.onTouchEvent(event);
}*/
@@ -77,24 +80,22 @@ public class KeyButton extends androidx.appcompat.widget.AppCompatTextView {
isMove = false;
- if (mListener!=null){
+ if (mListener != null) {
mListener.onActionDown(event);
}
-
-
break;
case MotionEvent.ACTION_MOVE:
- if(Config.s_keyboard){
+ if (Config.s_keyboard) {
if (Math.abs(event.getX() - touchInitX) > 10 || Math.abs(event.getY() - touchInitY) > 10) {
isMove = true;
setX(mOriginalX + event.getRawX() - mOriginalRawX);
setY(mOriginalY + event.getRawY() - mOriginalRawY);
}
- }else {
+ } else {
if (Math.abs(event.getX() - touchInitX) > 10 || Math.abs(event.getY() - touchInitY) > 10) {
- if (mListener!=null){
+ if (mListener != null) {
mListener.onActionMove(event);
}
/* setX(mOriginalX + event.getRawX() - mOriginalRawX);
@@ -105,7 +106,7 @@ public class KeyButton extends androidx.appcompat.widget.AppCompatTextView {
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
- if (mListener!=null){
+ if (mListener != null) {
mListener.onActionUp();
}
}
@@ -116,39 +117,46 @@ public class KeyButton extends androidx.appcompat.widget.AppCompatTextView {
return true;
}
- public void setOnClickCallBackListener(OnClickCallBackListener listener){
- this.mListener=listener;
+
+ public void setOnClickCallBackListener(OnClickCallBackListener listener) {
+ this.mListener = listener;
}
- public void keyBoardDelectListener(ViewChangMsgListener viewChangMsgListener){
- this.viewChangMsgListener =viewChangMsgListener;
+ public void keyBoardDelectListener(ViewChangMsgListener viewChangMsgListener) {
+ this.viewChangMsgListener = viewChangMsgListener;
}
- public interface OnClickCallBackListener{
+ public interface OnClickCallBackListener {
void onActionDown(MotionEvent event);
+
void onActionUp();
+
void onActionMove(MotionEvent event);
}
- public void setPosition(float x,float y) {
+ public void setPosition(float x, float y) {
this.setX(x);
this.setY(y);
}
- public interface ViewChangMsgListener{
+ public interface ViewChangMsgListener {
void changTexture(String path);
+
void showDeletPopup(KeyButton view);
+
void showDeletPopup(String index);
+
void showFloatPopup();
}
- public interface virtualHandleChangeListener{
+ public interface virtualHandleChangeListener {
void layerBtnSetVisible(int tag);
}
- public interface virtualKeyboardChangeListener
- {
+
+ public interface virtualKeyboardChangeListener {
void removeChild(View child);
+
void hideDirection();
}
diff --git a/android/app/src/main/java/co/steamcloud/game/KeyboardUtil.java b/android/app/src/main/java/co/steamcloud/game/customize/KeyboardUtil.java
similarity index 99%
rename from android/app/src/main/java/co/steamcloud/game/KeyboardUtil.java
rename to android/app/src/main/java/co/steamcloud/game/customize/KeyboardUtil.java
index 76c039e..4953f10 100644
--- a/android/app/src/main/java/co/steamcloud/game/KeyboardUtil.java
+++ b/android/app/src/main/java/co/steamcloud/game/customize/KeyboardUtil.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.customize;
import android.app.Activity;
import android.content.Context;
@@ -15,6 +15,8 @@ import com.zjrx.jyengine.WhaleCloud;
import java.util.List;
+import co.steamcloud.game.R;
+
public class KeyboardUtil {
private Context ctx;
private Activity act;
diff --git a/android/app/src/main/java/co/steamcloud/game/MoveButton.java b/android/app/src/main/java/co/steamcloud/game/customize/MoveButton.java
similarity index 95%
rename from android/app/src/main/java/co/steamcloud/game/MoveButton.java
rename to android/app/src/main/java/co/steamcloud/game/customize/MoveButton.java
index 3825e6d..6d04abe 100644
--- a/android/app/src/main/java/co/steamcloud/game/MoveButton.java
+++ b/android/app/src/main/java/co/steamcloud/game/customize/MoveButton.java
@@ -1,10 +1,12 @@
-package co.steamcloud.game;
+package co.steamcloud.game.customize;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
+import co.steamcloud.game.Config;
+
public class MoveButton extends RelativeLayout {
@@ -58,7 +60,7 @@ public class MoveButton extends RelativeLayout {
setX(moveX);
moveY = mOriginalY + event.getRawY() - mOriginalRawY;
setY(mOriginalY + event.getRawY() - mOriginalRawY);
- if (mListener != null){
+ if (mListener != null) {
mListener.onActionMove(moveX, moveY);
}
}
diff --git a/android/app/src/main/java/co/steamcloud/game/MyButton.java b/android/app/src/main/java/co/steamcloud/game/customize/MyButton.java
similarity index 84%
rename from android/app/src/main/java/co/steamcloud/game/MyButton.java
rename to android/app/src/main/java/co/steamcloud/game/customize/MyButton.java
index fbf098c..e6194f9 100644
--- a/android/app/src/main/java/co/steamcloud/game/MyButton.java
+++ b/android/app/src/main/java/co/steamcloud/game/customize/MyButton.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.customize;
import android.content.Context;
import android.util.AttributeSet;
@@ -23,6 +23,7 @@ public class MyButton extends androidx.appcompat.widget.AppCompatTextView {
public MyButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
+
private OnClickCallBackListener mListener;
private ViewChangMsgListener viewChangMsgListener;
@@ -43,6 +44,7 @@ public class MyButton extends androidx.appcompat.widget.AppCompatTextView {
public float touchInitX;
public float touchInitY;
boolean isMove = false;
+
@Override
public boolean onTouchEvent(MotionEvent event) {
@@ -68,7 +70,7 @@ public class MyButton extends androidx.appcompat.widget.AppCompatTextView {
break;
case MotionEvent.ACTION_UP:
if (!isMove) {
- if (mListener!=null){
+ if (mListener != null) {
mListener.onActionDown();
}
}
@@ -79,37 +81,44 @@ public class MyButton extends androidx.appcompat.widget.AppCompatTextView {
return true;
}
- public void setOnClickCallBackListener(OnClickCallBackListener listener){
- this.mListener=listener;
+
+ public void setOnClickCallBackListener(OnClickCallBackListener listener) {
+ this.mListener = listener;
}
- public void keyBoardDelectListener(ViewChangMsgListener viewChangMsgListener){
- this.viewChangMsgListener =viewChangMsgListener;
+ public void keyBoardDelectListener(ViewChangMsgListener viewChangMsgListener) {
+ this.viewChangMsgListener = viewChangMsgListener;
}
- public interface OnClickCallBackListener{
+ public interface OnClickCallBackListener {
void onActionDown();
- void onActionMove(float x,float y);
+
+ void onActionMove(float x, float y);
}
- public void setPosition(float x,float y) {
+ public void setPosition(float x, float y) {
this.setX(x);
this.setY(y);
}
- public interface ViewChangMsgListener{
+ public interface ViewChangMsgListener {
void changTexture(String path);
+
void showDeletPopup(MyButton view);
+
void showDeletPopup(String index);
+
void showFloatPopup();
}
- public interface virtualHandleChangeListener{
+
+ public interface virtualHandleChangeListener {
void layerBtnSetVisible(int tag);
}
- public interface virtualKeyboardChangeListener
- {
+
+ public interface virtualKeyboardChangeListener {
void removeChild(View child);
+
void hideDirection();
}
}
diff --git a/android/app/src/main/java/co/steamcloud/game/ActivityCollector.java b/android/app/src/main/java/co/steamcloud/game/utils/ActivityCollector.java
similarity index 95%
rename from android/app/src/main/java/co/steamcloud/game/ActivityCollector.java
rename to android/app/src/main/java/co/steamcloud/game/utils/ActivityCollector.java
index 924af3d..0a37d7d 100644
--- a/android/app/src/main/java/co/steamcloud/game/ActivityCollector.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/ActivityCollector.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
import android.app.Activity;
diff --git a/android/app/src/main/java/co/steamcloud/game/utils/AppUtil.java b/android/app/src/main/java/co/steamcloud/game/utils/AppUtil.java
index f526509..603b785 100644
--- a/android/app/src/main/java/co/steamcloud/game/utils/AppUtil.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/AppUtil.java
@@ -64,8 +64,75 @@ import co.steamcloud.game.App;
*/
public class AppUtil {
+ public static final byte[] gamePadDescriptor = new byte[]{
+ // 游戏手柄的 HID 描述符
+ 0x05, 0x01, // USAGE_PAGE (Game Controls) - 游戏控制设备
+ 0x09, 0x05, // USAGE (Gamepad) - 游戏手柄
+ (byte) 0xA1, 0x01, // COLLECTION (Application) - 应用集合
+ (byte) 0xA1, 0x00, // COLLECTION (Physical) - 物理集合
+
+ // 按钮部分
+ 0x05, 0x09, // USAGE_PAGE (Button) - 按钮输入
+ 0x19, 0x01, // USAGE_MINIMUM (Button 1)
+ 0x29, 0x10, // USAGE_MAXIMUM (Button 16)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0) - 按钮的逻辑最小值
+ 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 按钮的逻辑最大值
+ 0x75, 0x01, // REPORT_SIZE (1) - 每个按钮占用 1 位
+ (byte) 0x95, 0x10, // REPORT_COUNT (16) - 总共有 16 个按钮
+ (byte) 0x81, 0x02, // INPUT (Data, Var, Abs) - 按钮输入,数据类型、变量、绝对值
+
+ // 左摇杆
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+ 0x09, 0x30, // USAGE (X) - 左摇杆水平轴
+ 0x09, 0x31, // USAGE (Y) - 左摇杆垂直轴
+ 0x15, 0x00, // LOGICAL_MINIMUM (0) - 摇杆的逻辑最小值
+ 0x25, (byte) 0xFF, // LOGICAL_MAXIMUM (255) - 摇杆的逻辑最大值
+ 0x75, 0x08, // REPORT_SIZE (8)
+ (byte) 0x95, 0x02, // REPORT_COUNT (2)
+ (byte) 0x81, 0x02, // INPUT (Data, Var, Abs)
+
+// // 右摇杆
+ 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
+// 0x09, 0x33, // USAGE (Rx) - 右摇杆水平轴
+// 0x09, 0x34, // USAGE (Ry) - 右摇杆垂直轴
+ 0x09, 0x32, // USAGE (Z) - 右摇杆水平轴
+ 0x09, 0x35, // USAGE (Rz) - 右摇杆垂直轴
+ 0x15, 0x00, // LOGICAL_MINIMUM (0) - 摇杆的逻辑最小值
+ 0x25, (byte) 0xFF, // LOGICAL_MAXIMUM (255) - 摇杆的逻辑最大值
+ 0x75, 0x08, // REPORT_SIZE (8)
+ (byte) 0x95, 0x02, // REPORT_COUNT (2)
+ (byte) 0x81, 0x02, // INPUT (Data, Var, Abs)
+
+ // 方向键(HAT switch)
+ 0x09, 0x39, // USAGE (Hat switch)
+ 0x15, 0x00, // LOGICAL_MINIMUM (0)
+ 0x25, 0x07, // LOGICAL_MAXIMUM (7)
+ 0x35, 0x00, // PHYSICAL_MINIMUM (0)
+ 0x46, 0x3B, 0x01, // PHYSICAL_MAXIMUM (315)
+ 0x65, 0x14, // UNIT (Eng Rot: Angular Pos)
+ 0x75, 0x04, // REPORT_SIZE (4)
+ (byte) 0x95, 0x01, // REPORT_COUNT (1)
+ (byte) 0x81, 0x42, // INPUT (Data, Var, Abs, Null)
+
+ // 触发器(L2 和 R2)
+ 0x75, 0x04, // REPORT_SIZE (4) - 触发器占用 4 位
+ (byte) 0x95, 0x01, // REPORT_COUNT (1) - 只有 1 个触发器
+ (byte) 0x81, 0x01, // INPUT (Cnst, Ary, Abs) - 触发器输入,常量、数组、绝对值
+
+ // 震动输出报告
+ 0x09, 0x70, // USAGE (Vibration) - 震动
+ 0x15, 0x00, // LOGICAL_MINIMUM (0) - 震动强度的逻辑最小值
+ 0x26, (byte) 0xFF, 0x00, // LOGICAL_MAXIMUM (255) - 震动强度的逻辑最大值
+ 0x75, 0x08, // REPORT_SIZE (8) - 每个震动强度占用 8 位
+ (byte) 0x95, 0x02, // REPORT_COUNT (2) - 左、右震动马达各 1 个字节
+ (byte) 0x91, 0x02, // OUTPUT (Data, Var, Abs) - 震动输出,数据类型、变量、绝对值
+
+ // 结束集合
+ (byte) 0xC0, // END_COLLECTION (Physical) - 结束物理集合
+ (byte) 0xC0 // END_COLLECTION (Application) - 结束应用集合
+ };
+
/**
- *
* @param content 字符串内容
* @param width 二维码宽度
* @param height 二维码高度
@@ -79,8 +146,8 @@ public class AppUtil {
* @return
*/
public static Bitmap createQRCodeBitmap(String content, int width, int height, String character_set,
- String error_correction_level,String margin, int color_black,
- int color_white,Bitmap logoBitmap, float logoPercent) {
+ String error_correction_level, String margin, int color_black,
+ int color_white, Bitmap logoBitmap, float logoPercent) {
// 字符串内容判空
if (TextUtils.isEmpty(content)) {
return null;
@@ -125,7 +192,7 @@ public class AppUtil {
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
/** 5.为二维码添加logo图标 */
- if(logoBitmap != null){
+ if (logoBitmap != null) {
return addLogo(bitmap, logoBitmap, logoPercent);
}
return bitmap;
diff --git a/android/app/src/main/java/co/steamcloud/game/CountDownTimerUtil.java b/android/app/src/main/java/co/steamcloud/game/utils/CountDownTimerUtil.java
similarity index 98%
rename from android/app/src/main/java/co/steamcloud/game/CountDownTimerUtil.java
rename to android/app/src/main/java/co/steamcloud/game/utils/CountDownTimerUtil.java
index f8805cc..5ddeba7 100644
--- a/android/app/src/main/java/co/steamcloud/game/CountDownTimerUtil.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/CountDownTimerUtil.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
import android.os.CountDownTimer;
import android.view.View;
diff --git a/android/app/src/main/java/co/steamcloud/game/EventBusParams.java b/android/app/src/main/java/co/steamcloud/game/utils/EventBusParams.java
similarity index 87%
rename from android/app/src/main/java/co/steamcloud/game/EventBusParams.java
rename to android/app/src/main/java/co/steamcloud/game/utils/EventBusParams.java
index 19a6a6c..aa25fae 100644
--- a/android/app/src/main/java/co/steamcloud/game/EventBusParams.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/EventBusParams.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
/**
*
diff --git a/android/app/src/main/java/co/steamcloud/game/utils/GamePad.java b/android/app/src/main/java/co/steamcloud/game/utils/GamePad.java
new file mode 100644
index 0000000..2e882ba
--- /dev/null
+++ b/android/app/src/main/java/co/steamcloud/game/utils/GamePad.java
@@ -0,0 +1,245 @@
+package co.steamcloud.game.utils;
+
+import android.util.Log;
+
+/**
+ *
+ */
+public class GamePad {
+
+ private GamepadAction gamepadAction;
+
+ public GamePad(GamepadAction action) {
+ this.gamepadAction = action;
+ }
+
+ public void setCallback(GamepadAction callback) {
+ this.gamepadAction = callback;
+ }
+
+ private final String TAG = "Gamepad";
+ /**
+ * 按钮A
+ */
+ public GamepadButton BUTTON_A = new GamepadButton("BUTTON_A");
+
+ /**
+ * 按钮B
+ */
+ public GamepadButton BUTTON_B = new GamepadButton("BUTTON_B");
+
+ /**
+ * 按钮X
+ */
+ public GamepadButton BUTTON_X = new GamepadButton("BUTTON_X");
+
+ /**
+ * 按钮Y
+ */
+ public GamepadButton BUTTON_Y = new GamepadButton("BUTTON_Y");
+
+ /**
+ * 按钮L1
+ */
+ public GamepadButton BUTTON_L1 = new GamepadButton("BUTTON_L1");
+
+ /**
+ * 按钮R1
+ */
+ public GamepadButton BUTTON_R1 = new GamepadButton("BUTTON_R1");
+
+ /**
+ * 按钮L2
+ */
+ public GamepadButton BUTTON_L2 = new GamepadButton("BUTTON_L2");
+
+ /**
+ * 按钮R2
+ */
+ public GamepadButton BUTTON_R2 = new GamepadButton("BUTTON_R2");
+
+ /**
+ * 按钮Start
+ */
+ public GamepadButton BUTTON_START = new GamepadButton("BUTTON_START");
+
+ /**
+ * 按钮Select
+ */
+ public GamepadButton BUTTON_SELECT = new GamepadButton("BUTTON_SELECT");
+
+ /**
+ * 按钮L3
+ */
+ public GamepadButton BUTTON_L3 = new GamepadButton("BUTTON_L3");
+
+ /**
+ * 按钮R3
+ */
+ public GamepadButton BUTTON_R3 = new GamepadButton("BUTTON_R3");
+
+ /**
+ * 按钮DPad Up
+ */
+ public GamepadButton BUTTON_DPAD_UP = new GamepadButton("BUTTON_DPAD_UP");
+
+ /**
+ * 按钮DPad Down
+ */
+ public GamepadButton BUTTON_DPAD_DOWN = new GamepadButton("BUTTON_DPAD_DOWN");
+
+ /**
+ * 按钮DPad Left
+ */
+ public GamepadButton BUTTON_DPAD_LEFT = new GamepadButton("BUTTON_DPAD_LEFT");
+
+ /**
+ * 按钮DPad Right
+ */
+ public GamepadButton BUTTON_DPAD_RIGHT = new GamepadButton("BUTTON_DPAD_RIGHT");
+
+ public GamepadButton Placeholder = new GamepadButton("");
+
+ /**
+ * 第一位请求数据
+ */
+
+ public boolean KeyDown(GamepadButton btn) {
+ Log.d(TAG, "KeyDown——old: " + btn.Name + "===<>>>" + btn.getStatus());
+ btn.setStatus(true);
+ Log.d(TAG, "KeyDown: " + btn.Name + "===<>>>" + btn.getStatus());
+ GenerateParams();
+ return true;
+ }
+
+ public boolean KeyUp(GamepadButton btn) {
+ Log.d(TAG, "KeyDown——-up-old: " + btn.Name + "===<>>>" + btn.getStatus());
+ btn.setStatus(false);
+ Log.d(TAG, "KeyDown——-up: " + btn.Name + "===<>>>" + btn.getStatus());
+ GenerateParams();
+ return true;
+ }
+
+ private byte report3 = 0x00;
+ private byte report4 = 0x00;
+
+ private byte report5 = 0x00;
+ private byte report6 = 0x00;
+
+ /**
+ * 设置坐标
+ *
+ * @param x
+ * @param y
+ */
+ public void SetLeftRemoteSensing(float x, float y) {
+ report3 = (byte) mapToJoystick(x);
+ report4 = (byte) mapToJoystick(y);
+// Log.d(TAG, "SetLeftRemoteSensing: report3===>"+report3+"====>report4"+report4);
+ GenerateParams();
+ }
+
+ public void SetRightRemoteSensing(float x, float y) {
+ report5 = (byte) mapToJoystick(x);
+ report6 = (byte) mapToJoystick(y);
+ Log.d(TAG, "SetLeftRemoteSensing: report5===>" + report5 + "====>report6" + report6);
+ GenerateParams();
+ }
+
+ /**
+ * 将数组转换为二进制字符串
+ *
+ * @param boolArray
+ * @return
+ */
+ public String booleanArrayToBinaryString(GamepadButton[] boolArray) {
+ if (boolArray == null || boolArray.length != 8) {
+ throw new IllegalArgumentException("数组必须是8位长度的boolean数组");
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (GamepadButton b : boolArray) {
+// sb.append(b.getStatus() ? "1" : "0");
+ sb.insert(0, b.getStatus() ? "1" : "0");
+ }
+
+ return sb.toString();
+ }
+
+ public GamepadButton[] report1GamepadButton = {BUTTON_A, BUTTON_B, Placeholder, BUTTON_X, BUTTON_Y, Placeholder, BUTTON_L1, BUTTON_R1};
+ public GamepadButton[] report2GamepadButton = {BUTTON_L2, BUTTON_R2, BUTTON_SELECT, BUTTON_START, Placeholder, BUTTON_L3, BUTTON_R3, Placeholder};
+
+ /**
+ * 生产请求字节
+ *
+ * @param boolArray
+ * @return
+ */
+ public byte GetGenerateParams(GamepadButton[] boolArray) {
+ String binaryString = booleanArrayToBinaryString(boolArray);
+ // 将二进制字符串转换为整数
+ int intValue = Integer.parseInt(binaryString, 2); // 第二个参数是基数(2表示二进制)
+ // 将整数转换为byte
+ byte byteValue = (byte) intValue;
+ return byteValue;
+ }
+
+ public int mapToJoystick(float value) {
+ // 保证值在 [-1, 1] 范围内
+ value = Math.max(-1, Math.min(1, value));
+ return (int) ((value + 1) * 127.5);
+ }
+
+ private byte dpadState = 0x08; // 默认中立状态
+
+ public void SetDpadState() {
+ if (BUTTON_DPAD_UP.getStatus()) {
+ if (BUTTON_DPAD_LEFT.getStatus()) {
+ dpadState = 0x07; // 左上
+ } else if (BUTTON_DPAD_RIGHT.getStatus()) {
+ dpadState = 0x01; // 右上
+ } else {
+ dpadState = 0x00; // 上
+ }
+ } else if (BUTTON_DPAD_DOWN.getStatus()) {
+ if (BUTTON_DPAD_LEFT.getStatus()) {
+ dpadState = 0x05; // 左下
+ } else if (BUTTON_DPAD_RIGHT.getStatus()) {
+ dpadState = 0x03; // 右下
+ } else {
+ dpadState = 0x04; // 下
+ }
+ } else if (BUTTON_DPAD_LEFT.getStatus()) {
+ dpadState = 0x06; // 左
+ } else if (BUTTON_DPAD_RIGHT.getStatus()) {
+ dpadState = 0x02; // 右
+ } else {
+ dpadState = 0x08; // 中立
+ }
+ }
+
+ public void GenerateParams() {
+ SetDpadState();
+ //按钮1
+ byte report1 = GetGenerateParams(report1GamepadButton);
+ byte report2 = GetGenerateParams(report2GamepadButton);
+ byte[] report = new byte[8];
+ // 设置按钮 A 按下(假设 A 是第一个按钮)
+ report[0] = report1; // 0x01 表示 A 按下,0x00 表示释放
+ report[1] = report2;//; // 其他按钮未按下
+ // 设置摇杆为中立值
+ report[2] = report3; // 左摇杆水平中立位置
+ report[3] = report4; // 左摇杆垂直中立位置
+ report[4] = report5; // 右摇杆水平中立位置
+ report[5] = report6; // 右摇杆垂直中立位置
+// Log.d(TAG, "GenerateParams: report3==>"+report3+"===>report4===>"+report3);
+ // 设置触发器未按下
+ report[6] = dpadState; // 左右触发器未按下
+ // 保留字节
+ report[7] = 0x00; // 保留字段
+ Log.d(TAG, "SetLeftRemoteSensing: report7" + dpadState);
+ gamepadAction.SendRequest(report);
+ }
+
+
+}
diff --git a/android/app/src/main/java/co/steamcloud/game/GamePara.java b/android/app/src/main/java/co/steamcloud/game/utils/GamePara.java
similarity index 89%
rename from android/app/src/main/java/co/steamcloud/game/GamePara.java
rename to android/app/src/main/java/co/steamcloud/game/utils/GamePara.java
index eb783b5..04c20fb 100644
--- a/android/app/src/main/java/co/steamcloud/game/GamePara.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/GamePara.java
@@ -1,17 +1,17 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
import android.os.Build;
public class GamePara {
String game_key;
- int display_grade;
- int queue_grade;
- int able_queue;
+ int display_grade;
+ int queue_grade;
+ int able_queue;
String gs_name;
String save_time;
String room_name;
String start_resolution;
- int record_game_time;
+ int record_game_time;
String model_name;
String sc_id;
String hangup_time;
@@ -23,7 +23,7 @@ public class GamePara {
int enable_restart;//是否支持重新启动游戏,可选, 1为支持,0为不支持,默认为0
String room_key; //设置字符串房间随机密码,只有使用相同密码的操控设备权限变更才能判定有效
- public GamePara(){
+ public GamePara() {
game_key = "";
display_grade = 1;
queue_grade = 1;
diff --git a/android/app/src/main/java/co/steamcloud/game/utils/GamepadAction.java b/android/app/src/main/java/co/steamcloud/game/utils/GamepadAction.java
new file mode 100644
index 0000000..35c60bd
--- /dev/null
+++ b/android/app/src/main/java/co/steamcloud/game/utils/GamepadAction.java
@@ -0,0 +1,14 @@
+package co.steamcloud.game.utils;
+
+/**
+ * 手柄请求方法
+ */
+
+@FunctionalInterface
+public interface GamepadAction {
+ /**
+ *
+ * @return
+ */
+ void SendRequest(byte[] params);
+}
diff --git a/android/app/src/main/java/co/steamcloud/game/utils/GamepadButton.java b/android/app/src/main/java/co/steamcloud/game/utils/GamepadButton.java
new file mode 100644
index 0000000..ee5dcbe
--- /dev/null
+++ b/android/app/src/main/java/co/steamcloud/game/utils/GamepadButton.java
@@ -0,0 +1,27 @@
+package co.steamcloud.game.utils;
+
+public class GamepadButton {
+
+ public GamepadButton(String name) {
+ this.Name = name;
+ this.Status = false;
+ }
+
+ /**
+ * 按钮名称
+ */
+ public String Name;
+ /**
+ * 按钮状态
+ */
+ public Boolean Status;
+
+
+ public Boolean getStatus() {
+ return Status;
+ }
+
+ public void setStatus(boolean value) {
+ this.Status = value;
+ }
+}
diff --git a/android/app/src/main/java/co/steamcloud/game/GsonJsonUtil.java b/android/app/src/main/java/co/steamcloud/game/utils/GsonJsonUtil.java
similarity index 96%
rename from android/app/src/main/java/co/steamcloud/game/GsonJsonUtil.java
rename to android/app/src/main/java/co/steamcloud/game/utils/GsonJsonUtil.java
index f7c2864..f246c6f 100644
--- a/android/app/src/main/java/co/steamcloud/game/GsonJsonUtil.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/GsonJsonUtil.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@@ -54,7 +54,7 @@ public class GsonJsonUtil {
* 转成list
*
* @param string 字符串
- * @param cls 类
+ * @param cls 类
* @return list
*/
public static List stringToList(String string, Class cls) {
diff --git a/android/app/src/main/java/co/steamcloud/game/PeterTimeCountRefresh.java b/android/app/src/main/java/co/steamcloud/game/utils/PeterTimeCountRefresh.java
similarity index 97%
rename from android/app/src/main/java/co/steamcloud/game/PeterTimeCountRefresh.java
rename to android/app/src/main/java/co/steamcloud/game/utils/PeterTimeCountRefresh.java
index 90ae816..3c57315 100644
--- a/android/app/src/main/java/co/steamcloud/game/PeterTimeCountRefresh.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/PeterTimeCountRefresh.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
import android.os.CountDownTimer;
diff --git a/android/app/src/main/java/co/steamcloud/game/RoundCalculator.java b/android/app/src/main/java/co/steamcloud/game/utils/RoundCalculator.java
similarity index 97%
rename from android/app/src/main/java/co/steamcloud/game/RoundCalculator.java
rename to android/app/src/main/java/co/steamcloud/game/utils/RoundCalculator.java
index 195bce2..79a138b 100644
--- a/android/app/src/main/java/co/steamcloud/game/RoundCalculator.java
+++ b/android/app/src/main/java/co/steamcloud/game/utils/RoundCalculator.java
@@ -1,4 +1,4 @@
-package co.steamcloud.game;
+package co.steamcloud.game.utils;
/**
diff --git a/android/app/src/main/java/co/steamcloud/game/wxapi/WXPayEntryActivity.java b/android/app/src/main/java/co/steamcloud/game/wxapi/WXPayEntryActivity.java
index 6a4eb8f..5c99ef4 100644
--- a/android/app/src/main/java/co/steamcloud/game/wxapi/WXPayEntryActivity.java
+++ b/android/app/src/main/java/co/steamcloud/game/wxapi/WXPayEntryActivity.java
@@ -17,7 +17,7 @@ import org.simple.eventbus.EventBus;
import java.util.HashMap;
-import co.steamcloud.game.EventBusParams;
+import co.steamcloud.game.utils.EventBusParams;
import co.steamcloud.game.MainActivity;
diff --git a/android/app/src/main/res/layout/activity_game.xml b/android/app/src/main/res/layout/activity_game.xml
index 6dc1b6d..498c1cc 100644
--- a/android/app/src/main/res/layout/activity_game.xml
+++ b/android/app/src/main/res/layout/activity_game.xml
@@ -7,7 +7,7 @@
android:layout_height="match_parent"
android:background="#000000">
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/app/src/main/res/layout/layout_handle.xml b/android/app/src/main/res/layout/layout_handle.xml
index 5fa755d..836d46d 100644
--- a/android/app/src/main/res/layout/layout_handle.xml
+++ b/android/app/src/main/res/layout/layout_handle.xml
@@ -35,7 +35,7 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />
-
-
-
+
-
-
+
-
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
-
-
+
diff --git a/assets/images/ic_bt_device.png b/assets/images/ic_bt_device.png
new file mode 100644
index 0000000000000000000000000000000000000000..6d713fa2d2627e9a2cd8c5c807abc4acc2d2d311
GIT binary patch
literal 5460
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
KMNUMnLSTY$&PP%J
literal 0
HcmV?d00001
diff --git a/assets/images/ic_bt_off.png b/assets/images/ic_bt_off.png
new file mode 100644
index 0000000000000000000000000000000000000000..d7e7bba816c089a8dc701f7dc2b2f9b33f69fefd
GIT binary patch
literal 3042
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
literal 0
HcmV?d00001
diff --git a/assets/images/ic_bt_on.png b/assets/images/ic_bt_on.png
new file mode 100644
index 0000000000000000000000000000000000000000..7735e107438bc1ad5a84ac94bdd2bc6268ae57a6
GIT binary patch
literal 2534
zcmVH00001b5ch_0Itp)
z=>Px;ph-kQRCr$PU3-jF)fxYN_s(ktelPMH2>io@YdB_`!EnSU1yc_8tl!C*WUgcJaF
z!<+6sh6*_9AyFMTzX*L+Q^rYLS_IA?1CXqx;dq_M2sD6bJ0td*IzIOVN{PiX
zE-`~=D{#*zPGukr1t7)JSiEezBf!9Mo)iB~u)_6bEMwGl@Md|k?H0ZQoPHTg1M#vk
zi~#YC3a}P?U01qNt4rWH^4-Ijg!M#J1djM&q$?wj%Pss&v5|0>piHkkh
zCS|Yd+4gF-1+RFgZ1Yce3WNOF9|WL~W9aUH*1?3Uoos7qVvY32)fZia+Kvl5U$(T?1787YYb
zhm}CBraI$5AjsiuWH|Rt82gSYc)ijGZ=>NLG7xQ56fC`6mDWNW%WU_3GSNOWs=UN@
z=*o~W?|uiC7bhV(q0?JbyKC2TjbY834G|!}@y4X>>TEA)2t&>fRSc?%7Q7{)unzdA
z?g#jc&bSP;{{?XKA8?|iFah_ButL_U^ix9|hig>4^llRl98(a|WjkoGQ65yBhw`J=
zh@0Tq3q7;IX^+{i1x`Dmbbd0P_>mLIPHn5Lqse@^j$!TgMr^C}LjbCuCexy2!7?T$ul+~q^ikOK{WUvXZa
zDGa(1@T~c@lO!3tzbfNEY6=0PvNX9RU5@53$yW(2+Rt*EI{#=hu6+WoKlBbVq
z8KXJs{+uHOaIzQFE(6h8Gg-i&eJBy73zA^(X5gYYU29e@XWV=4~b5`x!Y3
z+(S9262RRt%F-{3Cpiflo=U-jA37nlCOt{wURbuh5$m=$>TR}0l5I1{nO{4JNnG3=
zOyPqvakLeCpUXh1#(SLJQE%{bOkQ#YUwCw_zL3Od3fT)ii@~u;uTcG^{#z^3Q1m@V
zuZsJZ_xyPJCE?*Kv}60YPM!0$keW1udku@x(*Et
z$7Zq@x<3FX;V3)c>GA1K{Ao#QulMtMBf}FL>LS_~%|!eV*$Znw2j{}Gc|em;D!;Qj
z9kV9%zLz~?z}g)Rcz#C{#34o#5uFv=<|PoaH8JQ%l~NK2A8p9Qw+Hu)bJ<`2>Bo{S
z_3$Yg(c`#G_5x2}fCYWDJEcasEI3e>jv?9Iqz8*}B3FK_VBw2(5SMq2q?iW0u_LY6
zy*dr9%)SBMht)y+(M)rr9>fkFw(JRp}6h*1kah{w#=j>%7`+evxg{BnOtf
zVHWHuO~>G@zOnM&|4$GLUhv|I)|D4Iu$bhkAij0~cs>Ke@9G=i?K!Mq(Z)K|HEG^9
zKM>pICJ+|)g{`DJWe{ecsuwdO4n=)|{fP_&@iQuBuDph(faY~qL*O%`#@KBD9@aVy`cu6X^OCT7ep0WB
zyzI3Gys}4ABy&{mzbOIVlcsZn7?LHU;<%FKrqQ{y*9u~&j8xBb3bNw5fDPz}f
z(=c{K&jtAk!|Pjz{1GRl2A}3{orrR2>G~&Th}oQOzcbW={cAFi(?jaAPtS!g|94)T
zxv0nYuRW(*fu0H7-Sh9YnEE~rb0?h@`yUM^=roX{u#Wzsc0!2)EW{L!d
zRe(qEDJ&9`{=dr%=P&E>e(peG3URRA!7wexf!xdO6^bX(F&J#4*y&56b3=+->8mi_
z%q!3+P&(%$RvbU6JAGBOp<9XLq$0C^13Mpd14zNv;%D~1RyW|Cgg
we~t#s4MyTQnp5IA8ZI$UAmFd-j^}9p59%P{8_qts9smFU07*qoM6N<$f_Oo_x&QzG
literal 0
HcmV?d00001
diff --git a/assets/images/ic_bt_refresh.png b/assets/images/ic_bt_refresh.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc80c7bf3c38f52340d24b427ddba90f833b2d52
GIT binary patch
literal 5794
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 createState() => _BtDescriptionDialogState();
+}
+
+class _BtDescriptionDialogState extends State {
+ @override
+ Widget build(BuildContext context) {
+ final size = MediaQuery.of(context).size;
+ final l14 = size.width / 25.71428571428571;
+ final t20 = size.width / 18;
+ final s13 = size.width / 27.692307692307;
+
+ return Material(
+ type: MaterialType.transparency, //透明类型
+ color: Color(0x1A000000),
+ child: Center(
+ child: Container(
+ margin: EdgeInsets.only(right: l14, left: l14),
+ decoration: BoxDecoration(color: Color(0xFF202530), borderRadius: BorderRadius.all(Radius.circular(7))),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ alignment: Alignment.centerLeft,
+ margin: EdgeInsets.only(top: t20, left: l14),
+ child: Text(
+ "使用说明:",
+ style: TextStyle(fontSize: s13, color: Color(0xFF9D9D9D)),
+ ),
+ ),
+ Container(
+ alignment: Alignment.centerLeft,
+ margin: EdgeInsets.only(top: t20, left: l14, right: l14, bottom: t20),
+ child: Text(
+ "1. 本功能适用于蒸汽云游车机版,TV版,帮助用户在无实体手柄的情 况下尽可能还原手柄游戏体验。\n\n 2. 使用本功能前,请打开当前设备,游戏端设备的蓝牙开关。在搜索 列表中找到游戏端设备,点击连接,连接成功后即可正常游戏。",
+ style: TextStyle(fontSize: s13, color: Color(0xFF9D9D9D)),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index e4a5fb7..e529ff6 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -10,6 +10,7 @@ import 'package:game/tools/home/my_home_page.dart';
import 'package:game/tools/home/search_page.dart';
import 'package:game/tools/home_page.dart';
import 'package:game/tools/login/login_page.dart';
+import 'package:game/tools/me/bt_game_pad_page.dart';
import 'package:game/tools/me/edit_info_page.dart';
import 'package:game/tools/me/feedback_page.dart';
import 'package:game/tools/me/game_history_page.dart';
@@ -77,6 +78,23 @@ class _MyAppState extends State {
case "SDKSuccess": //游戏Sdk是否初始化成功
NetworkConfig.isGameSdk = methodCall.arguments['SDKSuccess'];
break;
+
+ case "isBtOpen": //蓝牙状态
+ NetworkConfig.isBtOpen = methodCall.arguments['isBtOpen'];
+ EventBusUtil.fire(RefreshBtState());
+ print("NetworkConfig.isBtOpen==${NetworkConfig.isBtOpen}");
+ break;
+
+ case "btDevices": //蓝牙设备集合
+ NetworkConfig.btDevices = List.from(methodCall.arguments['btDevices']);
+ print("NetworkConfig.btDevices==${NetworkConfig.btDevices}");
+ EventBusUtil.fire(RefreshBtState());
+ break;
+
+ case "connectionStatus": //蓝牙设备连接状态
+ NetworkConfig.connectionStatus = methodCall.arguments['connectionStatus'];
+ EventBusUtil.fire(RefreshBtState());
+ break;
}
}
@@ -115,6 +133,7 @@ class _MyAppState extends State {
'/GameHistoryPage': (BuildContext context) => GameHistoryPage(),
'/FeedbackPage': (BuildContext context) => FeedbackPage(),
'/MessageCenterPage': (BuildContext context) => MessageCenterPage(),
+ '/BtGamePadPage': (BuildContext context) => BtGamePadPage(),
},
);
}
diff --git a/lib/network/NetworkConfig.dart b/lib/network/NetworkConfig.dart
index 9a2efd2..3a79f14 100644
--- a/lib/network/NetworkConfig.dart
+++ b/lib/network/NetworkConfig.dart
@@ -35,8 +35,11 @@ class NetworkConfig {
static String orderId = ""; //订单号
static SignBean? signData; //签到
static ConfigBean? configBean; //app配置
- static bool isGameSdk = false; //游戏SDK是否启动成功
static String gameId = ""; //游戏SDK是否启动成功
+ static bool isGameSdk = false; //游戏SDK是否启动成功
+ static bool isBtOpen = false; //蓝牙是否启动
+ static List btDevices = []; //蓝牙设备集合
+ static int connectionStatus = 0; //蓝牙连接状态
static const String getAppConfig = "api/App/GetAppConfig"; //应用配置
@@ -115,5 +118,4 @@ class NetworkConfig {
static const String accountLogOff = "api/Account/AccountLogOff"; //注销账号
static const String getGameUserInfo = "api/Game/GetGameUserInfo"; //获取用户游戏详情数据
-
}
diff --git a/lib/tools/me/bt_game_pad_page.dart b/lib/tools/me/bt_game_pad_page.dart
new file mode 100644
index 0000000..7fd2dce
--- /dev/null
+++ b/lib/tools/me/bt_game_pad_page.dart
@@ -0,0 +1,263 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_easyloading/flutter_easyloading.dart';
+
+import '../../common/EventBusUtil.dart';
+import '../../common/Global.dart';
+import '../../common/func.dart';
+import '../../dialog/bt_description_dialog.dart';
+import '../../network/NetworkConfig.dart';
+
+class BtGamePadPage extends StatefulWidget {
+ const BtGamePadPage({super.key});
+
+ @override
+ State createState() => _BtGamePadPageState();
+}
+
+class _BtGamePadPageState extends State {
+ late StreamSubscription _refreshBtStateEvent;
+
+ int currentBtIndex = -1;
+
+ @override
+ void initState() {
+ // TODO: implement initState
+ super.initState();
+
+ ///刷新用户信息
+ _refreshBtStateEvent = EventBusUtil.listen((event) {
+ setState(() {});
+ });
+
+ //蓝牙已打开,获取蓝牙设备列表
+ if (NetworkConfig.isBtOpen) {
+ Map gameMap = {
+ "getBtListDevices": "getBtListDevices",
+ };
+
+ ///传值初始化游戏token
+ invokeNativeMethod("getBtListDevices", gameMap);
+ }
+ }
+
+ @override
+ void dispose() {
+ // TODO: implement dispose
+ super.dispose();
+ _refreshBtStateEvent.cancel();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final size = MediaQuery.of(context).size;
+ final h50 = size.width / 7.2;
+ final s16 = size.width / 22.5;
+ final l14 = size.width / 25.71428571428571;
+ final w19 = size.width / 18.94736842105263;
+ final h26 = size.width / 13.84615384615385;
+ final w33 = size.width / 10.909090909090;
+ final h18 = size.width / 20;
+ final t20 = size.width / 18;
+ final s13 = size.width / 27.692307692307;
+ final w223 = size.width / 1.6143497757847;
+ final h44 = size.width / 8.1818181818181;
+ final w40 = size.width / 9;
+
+ return Scaffold(
+ backgroundColor: const Color(0xFF17181A),
+ body: Column(
+ children: [
+ Container(
+ width: size.width,
+ height: h50,
+ margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top),
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ Text(
+ "蓝牙手柄",
+ style: TextStyle(fontSize: s16, color: Color(0xFFD6D6D7)),
+ ),
+ Positioned(
+ left: l14,
+ child: GestureDetector(
+ onTap: () {
+ Navigator.pop(context);
+ },
+ child: Image(
+ width: w19,
+ height: h26,
+ image: const AssetImage('assets/images/btn_fanhui.png'),
+ ),
+ ),
+ ),
+ Positioned(
+ right: l14,
+ child: GestureDetector(
+ onTap: () {
+ FunctionUtil.popDialog(context, BtDescriptionDialog());
+ },
+ child: Image(
+ width: w19,
+ height: h26,
+ image: const AssetImage('assets/images/ic_describe.png'),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ Container(
+ margin: EdgeInsets.only(left: l14, right: l14, top: l14),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "开启蓝牙",
+ style: TextStyle(fontSize: l14, color: Color(0xFFD6D6D7)),
+ ),
+ GestureDetector(
+ onTap: () {
+ //蓝牙开关
+ Map gameMap = {
+ "setBtState": !NetworkConfig.isBtOpen,
+ };
+ invokeNativeMethod("setBt", gameMap);
+ },
+ child: Image(
+ width: w33,
+ height: h18,
+ image: AssetImage(NetworkConfig.isBtOpen ? 'assets/images/ic_bt_on.png' : 'assets/images/ic_bt_off.png'),
+ ),
+ )
+ ],
+ ),
+ ),
+ Container(
+ margin: EdgeInsets.only(left: l14, right: l14, top: t20),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "连接状态",
+ style: TextStyle(fontSize: l14, color: Color(0xFFD6D6D7)),
+ ),
+ Text(
+ NetworkConfig.connectionStatus == 0
+ ? "未连接"
+ : NetworkConfig.connectionStatus == 1
+ ? "连接中"
+ : NetworkConfig.connectionStatus == 2
+ ? "已连接"
+ : "正在断开连接",
+ style: TextStyle(fontSize: l14, color: NetworkConfig.connectionStatus == 2 ? Colors.green : Color(0xFFD6D6D7)),
+ ),
+ ],
+ ),
+ ),
+ GestureDetector(
+ onTap: () {
+ if (NetworkConfig.connectionStatus == 2) {
+ //进入手柄界面
+ Map map = {
+ "enterGamePad": "enterGamePad",
+ };
+ invokeNativeMethod("enterGamePad", map);
+ } else {
+ EasyLoading.showToast("请先连接设备");
+ }
+ },
+ child: Container(
+ width: w223,
+ height: h44,
+ margin: EdgeInsets.only(top: t20),
+ alignment: Alignment.center,
+ decoration: BoxDecoration(color: Color(0xFF074CE7), borderRadius: BorderRadius.all(Radius.circular(22))),
+ child: Text(
+ "进入蓝牙手柄",
+ style: TextStyle(fontSize: l14, color: Colors.white),
+ ),
+ ),
+ ),
+ Container(
+ margin: EdgeInsets.only(left: l14, right: l14, top: l14),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(
+ "可用设备",
+ style: TextStyle(fontSize: l14, color: Color(0xFFD6D6D7)),
+ ),
+ Image(
+ width: w33,
+ height: w33,
+ image: AssetImage('assets/images/ic_bt_refresh.png'),
+ )
+ ],
+ ),
+ ),
+ Expanded(
+ child: Container(
+ margin: EdgeInsets.only(left: l14, right: l14, top: l14),
+ child: ListView.builder(
+ padding: EdgeInsets.all(0),
+ itemCount: NetworkConfig.btDevices.length,
+ itemBuilder: (context, index) {
+ return _btItem(index, w40);
+ }),
+ ))
+ ],
+ ),
+ );
+ }
+
+ _btItem(index, w40) {
+ return GestureDetector(
+ onTap: () {
+ NetworkConfig.connectionStatus = 0;
+ currentBtIndex = index;
+ //点击连接设备
+ Map gameMap = {
+ "deviceIndex": index,
+ };
+ invokeNativeMethod("connectedDevice", gameMap);
+ setState(() {});
+ },
+ child: Container(
+ margin: EdgeInsets.only(bottom: 10),
+ padding: EdgeInsets.all(5),
+ decoration: BoxDecoration(
+ color: currentBtIndex == index ? Color(0xFF202530) : Color(0x00FFFFFF),
+ borderRadius: BorderRadius.all(Radius.circular(5)),
+ ),
+ child: Row(
+ children: [
+ Image(
+ width: w40,
+ height: w40,
+ image: AssetImage('assets/images/ic_bt_device.png'),
+ ),
+ Container(
+ margin: EdgeInsets.only(left: 17),
+ child: Text(
+ "${NetworkConfig.btDevices[index]}",
+ style: TextStyle(color: Color(0xFFD6D6D7)),
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+
+ // 获取原生的值
+ invokeNativeMethod(String method, Map map) async {
+ dynamic args;
+ try {
+ args = await Global.method.invokeMethod(method, map);
+ } on PlatformException catch (e) {}
+ }
+}
diff --git a/lib/tools/me/my_page.dart b/lib/tools/me/my_page.dart
index 3524691..f572d00 100644
--- a/lib/tools/me/my_page.dart
+++ b/lib/tools/me/my_page.dart
@@ -629,6 +629,45 @@ class _MyPageState extends State with AutomaticKeepAliveClientMixin {
),
),
),
+
+ ///蓝牙手柄
+ GestureDetector(
+ behavior: HitTestBehavior.opaque,
+ onTap: () {
+ Navigator.pushNamed(context, "/BtGamePadPage");
+ },
+ child: SizedBox(
+ height: h60,
+ child: Row(
+ children: [
+ Container(
+ margin: EdgeInsets.only(left: l23),
+ child: Image(
+ width: w20,
+ height: w20,
+ image: const AssetImage('assets/images/ic_set.png'),
+ ),
+ ),
+ Container(
+ margin: EdgeInsets.only(left: l14),
+ child: Text(
+ "蓝牙手柄",
+ style: TextStyle(fontSize: s14, color: Color(0xFFD6D6D7)),
+ ),
+ ),
+ Expanded(child: Container()),
+ Container(
+ margin: EdgeInsets.only(right: l23),
+ child: Image(
+ width: l4,
+ height: h8,
+ image: AssetImage('assets/images/ic_arrow.png'),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
],
),
),
diff --git a/lib/tools/start_page.dart b/lib/tools/start_page.dart
index b563f1f..b8cde5a 100644
--- a/lib/tools/start_page.dart
+++ b/lib/tools/start_page.dart
@@ -80,11 +80,10 @@ class _StartPageState extends State {
Map headersMap = Map.from(infoData);
NetworkConfig.deviceID = headersMap["deviceID"];
NetworkConfig.Language = headersMap["Language"]; //系统语言
- print("deviceID==${NetworkConfig.deviceID}");
- print("Language==${NetworkConfig.Language}");
+ NetworkConfig.isBtOpen = headersMap["isBtOpen"]; //蓝牙
+ print("isBtOpen==${NetworkConfig.isBtOpen}");
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
NetworkConfig.deviceName = androidInfo.model;
- print("model==${androidInfo.model}");
}
}