切换页面或锁屏时关闭串口电源

This commit is contained in:
cxc
2023-09-04 17:27:55 +08:00
parent 6dbda9c163
commit d925542852
20 changed files with 324 additions and 73 deletions

View File

@ -1,10 +1,30 @@
# This file tracks properties of this Flutter project. # This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc. # Used by Flutter tool to assess capabilities and perform upgrades etc.
# #
# This file should be version controlled and should not be manually edited. # This file should be version controlled.
version: version:
revision: b101bfe32f634566e7cb2791a9efe19cf8828b15 revision: cd41fdd495f6944ecd3506c21e94c6567b073278
channel: beta channel: unknown
project_type: app project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
- platform: android
create_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
base_revision: cd41fdd495f6944ecd3506c21e94c6567b073278
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -8,7 +8,7 @@ if (localPropertiesFile.exists()) {
def flutterRoot = localProperties.getProperty('flutter.sdk') def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) { if (flutterRoot == null) {
throw new Exception("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
} }
def flutterVersionCode = localProperties.getProperty('flutter.versionCode') def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
@ -28,24 +28,19 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
// compileSdkVersion flutter.compileSdkVersion // compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 32 compileSdkVersion 32
ndkVersion "23.1.7779620" ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "cn.com.motse.fengshui_compass" applicationId "cn.com.motse.fengshui_compass"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
// minSdkVersion flutter.minSdkVersion
minSdkVersion 19 minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
@ -59,19 +54,8 @@ android {
signingConfig signingConfigs.debug signingConfig signingConfigs.debug
} }
} }
lintOptions {
disable 'InvalidPackage'
disable "Instantiatable"
checkReleaseBuilds false
abortOnError false
}
} }
flutter { flutter {
source '../..' source '../..'
} }
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.com.motse.fengshui_compass"> package="cn.com.motse.fengshui_compass">
<!-- Flutter needs it to communicate with the running application <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

View File

@ -29,7 +29,6 @@
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:resource="@drawable/launch_image"
android:value="2" /> android:value="2" />
</application> </application>
</manifest> </manifest>

View File

@ -0,0 +1,142 @@
package cn.com.motse.fengshui_compass;
import android.util.Log;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* <pre>
* author: Blankj
* blog : http://blankj.com
* time : 2016/08/07
* desc : utils about shell
* </pre>
*/
public final class ComShellUtils {
public static final String TAG = "CommandExecution";
public final static String COMMAND_SU = "su";
public final static String COMMAND_SH = "sh";
public final static String COMMAND_EXIT = "exit\n";
public final static String COMMAND_LINE_END = "\n";
/**
* Command执行结果
*
* @author Mountain
*/
public static class CommandResult {
public int result = -1;
public String errorMsg;
public String successMsg;
@Override
public String toString() {
return "CommandResult{" +
"result=" + result +
", errorMsg='" + errorMsg + '\'' +
", successMsg='" + successMsg + '\'' +
'}';
}
}
/**
* 执行命令—单条
*
* @param command
* @param isRoot
* @return
*/
public static CommandResult execCommand(String command, boolean isRoot) {
String[] commands = {command};
return execCommand(commands, isRoot);
}
/**
* 执行命令-多条
*
* @param commands
* @param isRoot
* @return
*/
public static CommandResult execCommand(String[] commands, boolean isRoot) {
CommandResult commandResult = new CommandResult();
if (commands == null || commands.length == 0) return commandResult;
Process process = null;
DataOutputStream os = null;
BufferedReader successResult = null;
BufferedReader errorResult = null;
StringBuilder successMsg = null;
StringBuilder errorMsg = null;
try {
process = Runtime.getRuntime().exec(isRoot ? COMMAND_SU : COMMAND_SH);
os = new DataOutputStream(process.getOutputStream());
for (String command : commands) {
if (command != null) {
os.write(command.getBytes());
os.writeBytes(COMMAND_LINE_END);
os.flush();
}
}
os.writeBytes(COMMAND_EXIT);
os.flush();
commandResult.result = process.waitFor();
//获取错误信息
successMsg = new StringBuilder();
errorMsg = new StringBuilder();
successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s;
while ((s = successResult.readLine()) != null) {
successMsg.append(s);
}
while ((s = errorResult.readLine()) != null) {
errorMsg.append(s);
}
commandResult.successMsg = successMsg.toString();
commandResult.errorMsg = errorMsg.toString();
Log.i(TAG, commandResult.result + " | " + commandResult.successMsg
+ " | " + commandResult.errorMsg);
} catch (IOException e) {
String errmsg = e.getMessage();
if (errmsg != null) {
Log.e(TAG, errmsg);
} else {
e.printStackTrace();
}
} catch (Exception e) {
String errmsg = e.getMessage();
if (errmsg != null) {
Log.e(TAG, errmsg);
} else {
e.printStackTrace();
}
} finally {
try {
if (os != null) {
os.close();
}
if (successResult != null) {
successResult.close();
}
if (errorResult != null) {
errorResult.close();
}
} catch (IOException e) {
String errmsg = e.getMessage();
if (errmsg != null) {
Log.e(TAG, errmsg);
} else {
e.printStackTrace();
}
}
if (process != null) {
process.destroy();
}
}
return commandResult;
}
}

View File

@ -0,0 +1,45 @@
package cn.com.motse.fengshui_compass;
import androidx.annotation.NonNull;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodChannel;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "toggle_power";
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("turnOnPower")) {
String state = "1";
String ioValue = "236";
String cmdStr1 = "echo " + ioValue + " > /sys/class/gpio/export";
String cmdStr2 = "echo out > /sys/class/gpio/gpio" + ioValue + "/direction";
String cmdStr3 = "echo " + state + " > /sys/class/gpio/gpio" + ioValue + "/value";
ComShellUtils.execCommand(cmdStr1, false);
ComShellUtils.execCommand(cmdStr2, false);
ComShellUtils.execCommand(cmdStr3, false);
result.success("on");
} else if (call.method.equals("turnOffPower")) {
String state = "0";
String ioValue = "236";
String cmdStr1 = "echo " + ioValue + " > /sys/class/gpio/export";
String cmdStr2 = "echo out > /sys/class/gpio/gpio" + ioValue + "/direction";
String cmdStr3 = "echo " + state + " > /sys/class/gpio/gpio" + ioValue + "/value";
ComShellUtils.execCommand(cmdStr1, false);
ComShellUtils.execCommand(cmdStr2, false);
ComShellUtils.execCommand(cmdStr3, false);
result.success("off");
} else {
result.notImplemented();
}
}
);
}
}

View File

@ -1,6 +0,0 @@
package cn.com.motse.fengshui_compass
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen --> <!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/transparent" /> <item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here --> <!-- You can insert your own image assets here -->
<item> <!-- <item>
<bitmap <bitmap
android:gravity="center" android:gravity="center"
android:src="@drawable/launch_image" /> android:src="@mipmap/launch_image" />
</item> </item> -->
</layer-list> </layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -3,7 +3,7 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.

View File

@ -3,7 +3,7 @@
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when <!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame --> the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item> <item name="android:windowBackground">@drawable/launch_background</item>
</style> </style>
<!-- Theme applied to the Android Window as soon as the process has started. <!-- Theme applied to the Android Window as soon as the process has started.
@ -14,7 +14,5 @@
This Theme is only used starting with V2 of Flutter's Android embedding. --> This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item> <item name="android:windowBackground">?android:colorBackground</item>
</style> </style>
<color name="transparent">#00000000</color>
</resources> </resources>

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.com.motse.fengshui_compass"> package="cn.com.motse.fengshui_compass">
<!-- Flutter needs it to communicate with the running application <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.
--> -->
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>

View File

@ -6,7 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:4.1.0' classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

View File

@ -7,6 +7,7 @@ import 'package:fengshui_compass/components/my_icon.dart';
import 'package:fengshui_compass/pages/login_page.dart'; import 'package:fengshui_compass/pages/login_page.dart';
import 'package:fengshui_compass/states/region.dart'; import 'package:fengshui_compass/states/region.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_serial_port_api/flutter_serial_port_api.dart'; import 'package:flutter_serial_port_api/flutter_serial_port_api.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -23,7 +24,7 @@ class CompassPage extends StatefulWidget {
State<StatefulWidget> createState() => _CompassState(); State<StatefulWidget> createState() => _CompassState();
} }
class _CompassState extends State<CompassPage> { class _CompassState extends State<CompassPage> with WidgetsBindingObserver {
// 串口相关 // 串口相关
bool isPortOpened = false; bool isPortOpened = false;
SerialPort _serialPort; SerialPort _serialPort;
@ -54,16 +55,53 @@ class _CompassState extends State<CompassPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
print("init");
// 添加观察者以监听生命周期事件
WidgetsBinding.instance?.addObserver(this);
initPort(); initPort();
} }
@override
void dispose() {
turnOffPower();
// 在小部件被销毁时移除观察者,以防止内存泄漏
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
print("dispose");
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
// 在这里处理应用程序生命周期状态变化事件
if (state == AppLifecycleState.paused) {
// 应用程序进入后台
print('App entered background');
turnOffPower();
} else if (state == AppLifecycleState.resumed) {
// 应用程序回到前台
print('App returned to foreground');
initPort();
} else if (state == AppLifecycleState.inactive) {
// 应用程序处于非活动状态(例如:锁屏、电话来电)
print('App in inactive state');
turnOffPower();
}
}
Future<void> initPort() async { Future<void> initPort() async {
await turnOnPower();
// todo:
setState(() {
isLock = true;
isUpClose = true;
isSideClose = true;
});
// 20ms接收一次串口数据防抖定义接收缓存 // 20ms接收一次串口数据防抖定义接收缓存
final debounceTransformer = StreamTransformer<Uint8List, dynamic>.fromBind( final debounceTransformer = StreamTransformer<Uint8List, dynamic>.fromBind(
(s) => s.transform(debounceBuffer(const Duration(milliseconds: 20)))); (s) => s.transform(debounceBuffer(const Duration(milliseconds: 20))));
if (!isPortOpened) { if (!isPortOpened) {
Device theDevice = Device("/dev/ttyS2", "/dev/ttyS2"); Device theDevice = Device("/dev/ttyS3", "/dev/ttyS3");
var serialPort = await FlutterSerialPortApi.createSerialPort( var serialPort = await FlutterSerialPortApi.createSerialPort(
theDevice, 115200, theDevice, 115200,
parity: 0, dataBits: 8, stopBit: 1); parity: 0, dataBits: 8, stopBit: 1);
@ -89,6 +127,24 @@ class _CompassState extends State<CompassPage> {
} }
} }
Future<void> turnOnPower() async {
const platform = MethodChannel('toggle_power');
await platform.invokeMethod("turnOnPower");
}
Future<void> turnOffPower() async {
const platform = MethodChannel('toggle_power');
await platform.invokeMethod("turnOffPower");
if (mounted) {
setState(() {
isLock = false;
isUpClose = false;
isSideClose = false;
isPortOpened = false;
});
}
}
Future<void> closePort() async { Future<void> closePort() async {
bool closeResult = await _serialPort.close(); bool closeResult = await _serialPort.close();
setState(() { setState(() {
@ -204,24 +260,33 @@ class _CompassState extends State<CompassPage> {
w_y_tmp = 0.5 - 0.07 * roll_tmp / 30.0; w_y_tmp = 0.5 - 0.07 * roll_tmp / 30.0;
w_x_tmp = 0.5 - 0.07 * pitch_tmp / 30.0; w_x_tmp = 0.5 - 0.07 * pitch_tmp / 30.0;
} else if (w_total > 30) { } else if (w_total > 30) {
//todo // //todo
w_y_tmp = 0.5 - 0.07 * w_total / 30.0; // w_y_tmp = 0.5 - 0.07 * w_total / 30.0;
w_x_tmp = 0.5 - 0.07 * w_total / 30.0; // w_x_tmp = 0.5 - 0.07 * w_total / 30.0;
var radius = sqrt(0.07 * 0.07 * 2);
//todo;
var _angle = atan2(roll_tmp, -pitch_tmp);
if (_angle < 0) {
_angle += 2 * pi;
}
w_y_tmp = 0.5 - radius * sin(_angle);
w_x_tmp = 0.5 + radius * cos(_angle);
} }
// todo 其他情况 // todo 其他情况
var meanValue; var meanValue;
if (lista.length < 20) { // if (lista.length < 20) {
lista.add(temp_myaw); // lista.add(temp_myaw);
meanValue = null; // meanValue = null;
} else { // } else {
lista.removeAt(0); // lista.removeAt(0);
lista.add(temp_myaw); // lista.add(temp_myaw);
meanValue = lista.map((e) => e).reduce((a, b) => a + b) / lista.length; // meanValue = lista.map((e) => e).reduce((a, b) => a + b) / lista.length;
} // }
if (mounted) {
setState(() { setState(() {
// print("roll: $roll_tmp pitch: $pitch_tmp"); // print("roll: $roll_tmp pitch: $pitch_tmp");
// print("w_x: $w_x_tmp, w_y: $w_y_tmp"); // print("w_x: $w_x_tmp, w_y: $w_y_tmp");
@ -238,6 +303,7 @@ class _CompassState extends State<CompassPage> {
// 水平仪 0.5+-0.07范围 // 水平仪 0.5+-0.07范围
// x旋转y在动 y旋转x在动 roll改变y坐标pitch改变x坐标 // x旋转y在动 y旋转x在动 roll改变y坐标pitch改变x坐标
}); });
}
} else if (str.contains("9E010401") && } else if (str.contains("9E010401") &&
(str.length >= ((str.indexOf("9E010401") + 26)))) { (str.length >= ((str.indexOf("9E010401") + 26)))) {
var pos = str.indexOf("9E010401"); var pos = str.indexOf("9E010401");
@ -311,7 +377,8 @@ class _CompassState extends State<CompassPage> {
} else if (angle > 180) { } else if (angle > 180) {
result = angle - 180; result = angle - 180;
} else { } else {
result = 0.0; // result = 0.0;
result = angle;
} }
return result; return result;
} }