Просмотр исходного кода

Merge branch 'master' of http://git.izouma.com/panhui/mobileCyberGamesApp

panhui 6 лет назад
Родитель
Сommit
174d50f4d6

+ 26 - 16
android/app/src/main/AndroidManifest.xml

@@ -1,42 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.izouma.mobilecybergames">
 
-    <!-- The INTERNET permission is required for development. Specifically,
+    <!--
+     The INTERNET permission is required for development. Specifically,
          flutter needs it to communicate with the running application
          to allow setting breakpoints, to provide hot reload, etc.
     -->
-    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.CAMERA" />
-    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
-         calls FlutterMain.startInitialization(this); in its onCreate method.
-         In most cases you can leave this as-is, but you if you want to provide
-         additional functionality it is fine to subclass or reimplement
-         FlutterApplication and put your custom class here. -->
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
     <application
         android:name="io.flutter.app.FlutterApplication"
-        android:label="全民电竞"
-        android:icon="@mipmap/ic_launcher">
+        android:icon="@mipmap/ic_launcher"
+        android:label="全民电竞">
+        <service
+            android:name=".ScreenStreamService"
+            android:enabled="true"
+            android:exported="true"></service>
+
         <activity
             android:name=".MainActivity"
-            android:launchMode="singleTop"
-            android:theme="@style/LaunchTheme"
             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
             android:hardwareAccelerated="true"
+            android:launchMode="singleTop"
+            android:theme="@style/LaunchTheme"
             android:windowSoftInputMode="adjustResize">
-            <!-- This keeps the window background of the activity showing
+
+            <!--
+                 This keeps the window background of the activity showing
                  until Flutter renders its first frame. It can be removed if
                  there is no splash screen (such as the default splash screen
-                 defined in @style/LaunchTheme). -->
+                 defined in @style/LaunchTheme).
+            -->
             <meta-data
                 android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
                 android:value="true" />
+
             <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-                <category android:name="android.intent.category.LAUNCHER"/>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
     </application>
-</manifest>
+
+</manifest>

+ 21 - 0
android/app/src/main/java/com/izouma/mobilecybergames/MainActivity.java

@@ -1,5 +1,6 @@
 package com.izouma.mobilecybergames;
 
+import android.content.Intent;
 import android.os.Bundle;
 
 import com.mr.flutter.plugin.filepicker.FilePickerPlugin;
@@ -9,10 +10,30 @@ import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugins.GeneratedPluginRegistrant;
 
 public class MainActivity extends FlutterActivity {
+    public static final String APP_LIFECYCLE_ACTION = "APP_LIFECYCLE_ACTION";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         GeneratedPluginRegistrant.registerWith(this);
         ScreenStreamPlugin.registerWith(registrarFor("com.izouma.mobilecybergames.ScreenStreamPlugin"));
     }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Intent intent = new Intent();
+        intent.setAction(APP_LIFECYCLE_ACTION);
+        intent.putExtra("event", "onResume");
+        sendBroadcast(intent);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        Intent intent = new Intent();
+        intent.setAction(APP_LIFECYCLE_ACTION);
+        intent.putExtra("event", "onPause");
+        sendBroadcast(intent);
+    }
 }

+ 152 - 4
android/app/src/main/java/com/izouma/mobilecybergames/ScreenStreamPlugin.java

@@ -1,12 +1,23 @@
 package com.izouma.mobilecybergames;
 
 import android.app.Activity;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
 import android.media.projection.MediaProjectionManager;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
 import android.widget.Toast;
 
 import com.alivc.live.pusher.AlivcAudioAACProfileEnum;
@@ -26,9 +37,12 @@ import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
 import io.flutter.plugin.common.PluginRegistry;
 
+import static android.content.Context.WINDOW_SERVICE;
+
 public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, PluginRegistry.ActivityResultListener {
     private static final String TAG                             = "ScreenStreamPlugin";
-    public static final  int    CAPTURE_PERMISSION_REQUEST_CODE = 0x1123;
+    private static final int    CAPTURE_PERMISSION_REQUEST_CODE = 0x1123;
+    private static final int    FLOATING_WINDOW_REQUEST_CODE    = 0x1124;
 
     private final PluginRegistry.Registrar registrar;
 
@@ -38,6 +52,10 @@ public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, Plug
     private String               url;
     private MethodChannel.Result result;
 
+    private WindowManager              windowManager;
+    private WindowManager.LayoutParams layoutParams;
+    private Button                     floatButton;
+
     public static void registerWith(PluginRegistry.Registrar registrar) {
         final MethodChannel channel = new MethodChannel(registrar.messenger(), "screen_stream");
         MethodChannel.MethodCallHandler methodCallHandler = new ScreenStreamPlugin(registrar);
@@ -47,6 +65,20 @@ public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, Plug
 
     public ScreenStreamPlugin(PluginRegistry.Registrar registrar) {
         this.registrar = registrar;
+        this.registrar.context().registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if ("onResume".equals(intent.getStringExtra("event"))) {
+                    if (floatButton != null) {
+                        floatButton.setVisibility(View.GONE);
+                    }
+                } else if ("onPause".equals(intent.getStringExtra("event"))) {
+                    if (floatButton != null) {
+                        floatButton.setVisibility(View.VISIBLE);
+                    }
+                }
+            }
+        }, new IntentFilter(MainActivity.APP_LIFECYCLE_ACTION));
         AlivcLivePushConfig.setMediaProjectionPermissionResultData(null);
         mAlivcLivePushConfig = new AlivcLivePushConfig();//初始化推流配置类
         mAlivcLivePushConfig.setResolution(AlivcResolutionEnum.RESOLUTION_720P);//分辨率540P,最大支持720P
@@ -55,6 +87,21 @@ public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, Plug
         mAlivcLivePushConfig.setPreviewOrientation(AlivcPreviewOrientationEnum.ORIENTATION_LANDSCAPE_HOME_RIGHT); // 默认为竖屏,可设置home键向左或向右横屏。
         mAlivcLivePushConfig.setAudioProfile(AlivcAudioAACProfileEnum.AAC_LC);//设置音频编码模式
         mAlivcLivePushConfig.setEnableBitrateControl(true);// 打开码率自适应,默认为true
+
+        windowManager = (WindowManager) this.registrar.context().getSystemService(WINDOW_SERVICE);
+        layoutParams = new WindowManager.LayoutParams();
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+        } else {
+            layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
+        }
+        layoutParams.format = PixelFormat.RGBA_8888;
+        layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+        layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+        layoutParams.width = 200;
+        layoutParams.height = 100;
+        layoutParams.x = 300;
+        layoutParams.y = 300;
     }
 
     @Override
@@ -74,10 +121,18 @@ public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, Plug
         } else if ("stop".equals(methodCall.method)) {
             stop();
             result.success("success");
+        } else if ("checkPermission".equals(methodCall.method)) {
+            if (checkPermission()) {
+                result.success(true);
+            } else {
+                result.success(false);
+            }
+        } else if ("requestPermission".equals(methodCall.method)) {
+            requestPermission();
         }
     }
 
-    public boolean start() {
+    private boolean start() {
         if (started) {
             mAlivcLivePusher.stopPush();
             return true;
@@ -91,14 +146,55 @@ public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, Plug
         return false;
     }
 
-    public boolean stop() {
+    private boolean checkPermission() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            return Settings.canDrawOverlays(registrar.context());
+        }
+        return true;
+    }
+
+    private void requestPermission() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if (!Settings.canDrawOverlays(registrar.context())) {
+                registrar.activity().startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + registrar.context().getPackageName())), FLOATING_WINDOW_REQUEST_CODE);
+                return;
+            }
+        }
+        result.success(true);
+    }
+
+    private void showFloatWindow() {
+        if (floatButton != null) {
+            return;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {//判断系统版本
+            if (Settings.canDrawOverlays(registrar.context())) {
+                floatButton = new Button(registrar.context().getApplicationContext());
+                floatButton.setText("CLICK ME!");
+                floatButton.setBackgroundColor(Color.LTGRAY);
+                floatButton.setVisibility(View.GONE);
+                windowManager.addView(floatButton, layoutParams);
+                floatButton.setOnTouchListener(new FloatingOnTouchListener());
+            }
+        } else {
+            floatButton = new Button(registrar.context().getApplicationContext());
+            floatButton.setText("CLICK ME!");
+            floatButton.setBackgroundColor(Color.LTGRAY);
+            floatButton.setVisibility(View.GONE);
+            windowManager.addView(floatButton, layoutParams);
+            floatButton.setOnTouchListener(new FloatingOnTouchListener());
+        }
+    }
+
+    private boolean stop() {
         if (mAlivcLivePusher != null && mAlivcLivePusher.isPushing()) {
             mAlivcLivePusher.stopPush();
         }
         return true;
     }
 
-    public void startPushWithoutSurface() {
+    private void startPushWithoutSurface() {
+        showFloatWindow();
         mAlivcLivePusher = new AlivcLivePusher();
         try {
             mAlivcLivePusher.init(registrar.context(), mAlivcLivePushConfig);
@@ -268,8 +364,60 @@ public class ScreenStreamPlugin implements MethodChannel.MethodCallHandler, Plug
                     result.error("needs permission", "permission not granted", null);
                 }
             }
+            case FLOATING_WINDOW_REQUEST_CODE: {
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                    if (!Settings.canDrawOverlays(registrar.context())) {
+                        if (result != null) {
+                            result.success(false);
+                        }
+                        return true;
+                    }
+                }
+                if (result != null) {
+                    result.success(false);
+                }
+            }
             break;
         }
         return false;
     }
+
+    private class FloatingOnTouchListener implements View.OnTouchListener {
+        private int  x;
+        private int  y;
+        private long ts;
+        boolean moved = false;
+
+        @Override
+        public boolean onTouch(View view, MotionEvent event) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    x = (int) event.getRawX();
+                    y = (int) event.getRawY();
+                    ts = System.currentTimeMillis();
+                    moved = false;
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    int nowX = (int) event.getRawX();
+                    int nowY = (int) event.getRawY();
+                    int movedX = nowX - x;
+                    int movedY = nowY - y;
+                    x = nowX;
+                    y = nowY;
+                    layoutParams.x = layoutParams.x + movedX;
+                    layoutParams.y = layoutParams.y + movedY;
+                    windowManager.updateViewLayout(view, layoutParams);
+                    moved = true;
+                    break;
+                case MotionEvent.ACTION_UP:
+                    if (System.currentTimeMillis() - ts < 300) {
+                        registrar.context().startActivity(new Intent(registrar.context(), MainActivity.class));
+                    }
+                    break;
+                default:
+                    break;
+            }
+            return false;
+        }
+    }
 }

+ 20 - 15
lib/pages/HomePage.dart

@@ -15,6 +15,7 @@ import 'package:flutter_redux/flutter_redux.dart';
 import '../redux/AppState.dart';
 import 'setting.dart';
 import '../widget/Dialog.dart';
+import '../plugins/ScreenStramPlugin.dart';
 
 class HomePage extends StatefulWidget {
   @override
@@ -43,7 +44,7 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
   }
 
   void showBackDialog() {
-     MyDialog.showDialog(context, '暂时没有进行中的房间,敬请期待...');
+    MyDialog.showDialog(context, '暂时没有进行中的房间,敬请期待...');
     // showDialog<Null>(
     //   context: context,
     //   barrierDismissible: false,
@@ -114,14 +115,6 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
     WidgetsBinding.instance.removeObserver(this);
   }
 
-
-  // @override
-  // void didChangeLocales(List<Locale> locale) {
-  //      print('1111111111111');
-  //   // TODO: implement didChangeLocales
-  //   super.didChangeLocales(locale);
-  // }
-
   @override
   void didChangeAppLifecycleState(AppLifecycleState state) {
     if (state == AppLifecycleState.resumed) {
@@ -168,7 +161,8 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
                             Navigator.push(
                                 context,
                                 new CupertinoPageRoute(
-                                    builder: (context) => new RankList(raceId: seasonList[index].competitionSeason.id, gameId: seasonList[index].competitionSeason.gameId)));
+                                    builder: (context) =>
+                                        new RankList(raceId: seasonList[index].competitionSeason.id, gameId: seasonList[index].competitionSeason.gameId)));
                           },
                           onIndexChanged: (index) {
                             setState(() {
@@ -226,6 +220,17 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
                                             // )
                                           ],
                                         ),
+                                      ),
+                                      Center(
+                                        child: MaterialButton(
+                                          child: Text('lalala'),
+                                          onPressed: () async {
+                                            bool success = await ScreenStreamPlugin.checkPermission();
+                                            print(success);
+                                            // bool success = await ScreenStreamPlugin.requestPermission();
+                                            // print(success);
+                                          },
+                                        ),
                                       )
                                     ],
                                   )),
@@ -299,9 +304,9 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
             double height = MediaQuery.of(context).size.height;
             double aspectRatio = 1;
             // if (height / width < 20 / 9) {
-             
+
             // }
-             aspectRatio = width / (height - 350);
+            aspectRatio = width / (height - 350);
             return Container(
               child: GridView.count(
                 physics: new BouncingScrollPhysics(),
@@ -344,9 +349,9 @@ class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
                   HomeMenu(
                     "images/home_icon_youjian.png",
                     "邮件",
-                    onTapHomeMenu: () async{
-                      bool result=await Navigator.push(context, new CupertinoPageRoute(builder: (context) => new TipList()));
-                      if(result){
+                    onTapHomeMenu: () async {
+                      bool result = await Navigator.push(context, new CupertinoPageRoute(builder: (context) => new TipList()));
+                      if (result) {
                         getUnreadMsg();
                       }
                     },

+ 25 - 3
lib/pages/openRoom.dart

@@ -16,6 +16,7 @@ import '../net/Result.dart';
 import 'package:dio/dio.dart';
 import '../model/GameInfo.dart';
 import '../model/HouseLevel.dart';
+import '../plugins/ScreenStramPlugin.dart';
 
 class OpenRoom extends StatefulWidget {
   OpenRoom({Key key, this.roomFlag}) : super(key: key);
@@ -68,6 +69,27 @@ class OpenRoomState extends State<OpenRoom> {
       Toast.show(context, '请录入游戏房间密码', 1500, 'info');
       return;
     }
+    bool hasPermission = await ScreenStreamPlugin.checkPermission();
+    if (!hasPermission) {
+      showDialog(
+        context: context,
+        builder: (context) => AlertDialog(
+              title: Text('需要悬浮窗权限'),
+              contentTextStyle: TextStyle(color: Colors.black87),
+              content: Text('请在点击确定后,勾选"允许显示在其他应用的上层"'),
+              actions: <Widget>[
+                FlatButton(
+                  child: Text('确定'),
+                  onPressed: () {
+                    Navigator.of(context).pop();
+                    ScreenStreamPlugin.requestPermission();
+                  },
+                ),
+              ],
+            ),
+      );
+      return;
+    }
     editRoomInfo['createUser'] = StoreProvider.of<AppState>(context).state.userInfo.id;
     Toast.show(context, '加载中', -1, 'loading');
     Result res = await HttpManager.post("houseInfo/save", data: editRoomInfo);
@@ -329,7 +351,7 @@ class OpenRoomState extends State<OpenRoom> {
                               child: Image.asset('images/icon_jinbi_da_bai.png', width: 20),
                             ),
                             Text(
-                              'X' + (chooseLevelInfo.entryCoin ?? 0).toString(),
+                              '×' + (chooseLevelInfo.entryCoin ?? 0).toString(),
                               style: TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w500),
                             ),
                             Padding(
@@ -394,7 +416,7 @@ class OpenRoomState extends State<OpenRoom> {
             height: 1,
             color: BG_SUB_COLOR,
           ),
-          //游戏中房间密
+          //游戏中房间密��
           Container(
             height: 60,
             color: BG_COLOR,
@@ -530,7 +552,7 @@ class ChooseContent extends StatelessWidget {
                           child: Image.asset('images/icon_jinbi_da_bai.png', width: 20),
                         ),
                         Text(
-                          'X' + (chooseLevelInfo.entryCoin ?? 0).toString(),
+                          '×' + (chooseLevelInfo.entryCoin ?? 0).toString(),
                           style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w500),
                         )
                       ],

+ 3 - 3
lib/pages/roomInfo.dart

@@ -549,7 +549,7 @@ class RoomInfoState extends State<RoomInfo>
                               ),
                             ),
                             Text(
-                              'X' +
+                              '×' +
                                   (houseInfo != null
                                       ? houseInfo.bonus.toString()
                                       : '0'),
@@ -606,7 +606,7 @@ class RoomInfoState extends State<RoomInfo>
                 Container(
                   margin: EdgeInsets.only(left: 6),
                   child: Text(
-                    'X' + joinMoney.toString(),
+                    '×' + joinMoney.toString(),
                     style: TextStyle(
                         color: Colors.white,
                         fontSize: 16,
@@ -929,7 +929,7 @@ class RankContentState extends State<RankContent> {
             children: <Widget>[
               Image.asset('images/icon_jinbi_xiao_hong.png', width: 20),
               Text(
-                  'x' +
+                  '×' +
                       (topList[_num - 1].bonus != null
                           ? topList[_num - 1].bonus.toString()
                           : '0'),

+ 28 - 0
lib/plugins/ScreenStramPlugin.dart

@@ -1,3 +1,5 @@
+import 'dart:io';
+
 import 'package:flutter/services.dart';
 
 class ScreenStreamPlugin {
@@ -20,4 +22,30 @@ class ScreenStreamPlugin {
       return false;
     }
   }
+
+  static Future<bool> checkPermission() async {
+    if (Platform.isAndroid) {
+      try {
+        bool success = await _channel.invokeMethod("checkPermission", []);
+        return success;
+      } catch (e) {
+        return false;
+      }
+    } else {
+      return true;
+    }
+  }
+
+  static Future<bool> requestPermission() async {
+    if (Platform.isAndroid) {
+      try {
+        bool success = await _channel.invokeMethod("requestPermission", []);
+        return success;
+      } catch (e) {
+        return false;
+      }
+    } else {
+      return true;
+    }
+  }
 }

+ 1 - 1
pubspec.yaml

@@ -4,7 +4,7 @@ description: Universal mobile cyber games app
 version: 1.0.0+1
 
 environment:
-  sdk: ">=2.0.0-dev.68.0 <3.0.0"
+  sdk: ">=2.1.0 <3.0.0"
 
 
 dependencies: