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

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.
# 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:
revision: b101bfe32f634566e7cb2791a9efe19cf8828b15
channel: beta
revision: cd41fdd495f6944ecd3506c21e94c6567b073278
channel: unknown
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')
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')
@ -28,24 +28,19 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
// compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 32
ndkVersion "23.1.7779620"
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
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
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
@ -59,19 +54,8 @@ android {
signingConfig signingConfigs.debug
}
}
lintOptions {
disable 'InvalidPackage'
disable "Instantiatable"
checkReleaseBuilds false
abortOnError false
}
}
flutter {
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"
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.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -29,7 +29,6 @@
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:resource="@drawable/launch_image"
android:value="2" />
</application>
</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"?>
<!-- Modify this file to customize your launch splash screen -->
<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 -->
<item>
<!-- <item>
<bitmap
android:gravity="center"
android:src="@drawable/launch_image" />
</item>
android:src="@mipmap/launch_image" />
</item> -->
</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 -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- 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>
</style>
<!-- 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 -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- 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>
</style>
<!-- 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. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
<color name="transparent">#00000000</color>
</resources>

View File

@ -1,6 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
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.
-->
<uses-permission android:name="android.permission.INTERNET"/>

View File

@ -6,7 +6,7 @@ buildscript {
}
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"
}
}

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
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/states/region.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_serial_port_api/flutter_serial_port_api.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
@ -23,7 +24,7 @@ class CompassPage extends StatefulWidget {
State<StatefulWidget> createState() => _CompassState();
}
class _CompassState extends State<CompassPage> {
class _CompassState extends State<CompassPage> with WidgetsBindingObserver {
// 串口相关
bool isPortOpened = false;
SerialPort _serialPort;
@ -54,16 +55,53 @@ class _CompassState extends State<CompassPage> {
@override
void initState() {
super.initState();
print("init");
// 添加观察者以监听生命周期事件
WidgetsBinding.instance?.addObserver(this);
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 {
await turnOnPower();
// todo:
setState(() {
isLock = true;
isUpClose = true;
isSideClose = true;
});
// 20ms接收一次串口数据防抖定义接收缓存
final debounceTransformer = StreamTransformer<Uint8List, dynamic>.fromBind(
(s) => s.transform(debounceBuffer(const Duration(milliseconds: 20))));
if (!isPortOpened) {
Device theDevice = Device("/dev/ttyS2", "/dev/ttyS2");
Device theDevice = Device("/dev/ttyS3", "/dev/ttyS3");
var serialPort = await FlutterSerialPortApi.createSerialPort(
theDevice, 115200,
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 {
bool closeResult = await _serialPort.close();
setState(() {
@ -204,40 +260,50 @@ class _CompassState extends State<CompassPage> {
w_y_tmp = 0.5 - 0.07 * roll_tmp / 30.0;
w_x_tmp = 0.5 - 0.07 * pitch_tmp / 30.0;
} else if (w_total > 30) {
//todo
w_y_tmp = 0.5 - 0.07 * w_total / 30.0;
w_x_tmp = 0.5 - 0.07 * w_total / 30.0;
// //todo
// w_y_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 其他情况
var meanValue;
if (lista.length < 20) {
lista.add(temp_myaw);
meanValue = null;
} else {
lista.removeAt(0);
lista.add(temp_myaw);
meanValue = lista.map((e) => e).reduce((a, b) => a + b) / lista.length;
// if (lista.length < 20) {
// lista.add(temp_myaw);
// meanValue = null;
// } else {
// lista.removeAt(0);
// lista.add(temp_myaw);
// meanValue = lista.map((e) => e).reduce((a, b) => a + b) / lista.length;
// }
if (mounted) {
setState(() {
// print("roll: $roll_tmp pitch: $pitch_tmp");
// print("w_x: $w_x_tmp, w_y: $w_y_tmp");
// if (meanValue != null) {
// myaw = meanValue;
// } else {
// myaw = 0;
// }
myaw = temp_myaw;
w_x = w_x_tmp;
w_y = w_y_tmp;
// 水平仪 0.5+-0.07范围
// x旋转y在动 y旋转x在动 roll改变y坐标pitch改变x坐标
});
}
setState(() {
// print("roll: $roll_tmp pitch: $pitch_tmp");
// print("w_x: $w_x_tmp, w_y: $w_y_tmp");
// if (meanValue != null) {
// myaw = meanValue;
// } else {
// myaw = 0;
// }
myaw = temp_myaw;
w_x = w_x_tmp;
w_y = w_y_tmp;
// 水平仪 0.5+-0.07范围
// x旋转y在动 y旋转x在动 roll改变y坐标pitch改变x坐标
});
} else if (str.contains("9E010401") &&
(str.length >= ((str.indexOf("9E010401") + 26)))) {
var pos = str.indexOf("9E010401");
@ -311,7 +377,8 @@ class _CompassState extends State<CompassPage> {
} else if (angle > 180) {
result = angle - 180;
} else {
result = 0.0;
// result = 0.0;
result = angle;
}
return result;
}