diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..d25fae7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2024 Fabian Baldeau + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 39157b2..a5be2f8 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,6 @@ Absolutely in-development! [![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] [![License: MIT][license_badge]][license_link] -Generated by the [Very Good CLI][very_good_cli_link] 🤖 - -A Very Good Project created by Very Good CLI. - --- ## Getting Started 🚀 diff --git a/android/app/build.gradle b/android/app/build.gradle index af6b17e..bc0674d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -31,7 +31,7 @@ if (keystorePropertiesFile.exists()) { android { namespace "com.example.verygoodcore.xiao_pet_tracker" compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + ndkVersion "27.0.12077973" compileOptions { sourceCompatibility JavaVersion.VERSION_17 diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 399f698..28aa0d7 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -4,4 +4,24 @@ to allow setting breakpoints, to provide hot reload, etc. --> + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 07a763b..a2313ec 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -30,9 +30,23 @@ android:name="flutterEmbedding" android:value="2" /> - - - + + + + + + + + + + + + + + + diff --git a/android/gradle.properties b/android/gradle.properties index 94adc3a..0c16a38 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.suppressUnsupportedCompileSdk=34 diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 6b66533..09523c0 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/android/gradlew b/android/gradlew old mode 100644 new mode 100755 index 9d82f78..f5feea6 --- a/android/gradlew +++ b/android/gradlew @@ -1,74 +1,130 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -77,84 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat index aec9973..9b42019 100644 --- a/android/gradlew.bat +++ b/android/gradlew.bat @@ -1,4 +1,22 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -8,26 +26,30 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -35,54 +57,36 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/android/settings.gradle b/android/settings.gradle index 1a4ffcb..2ad4375 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false + id "com.android.application" version "8.7.1" apply false id "org.jetbrains.kotlin.android" version "1.9.20" apply false } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index bf87ae7..8787b17 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -2,28 +2,47 @@ PODS: - Flutter (1.0.0) - flutter_blue_plus (0.0.1): - Flutter + - ObjectBox (4.0.1) + - objectbox_flutter_libs (0.0.1): + - Flutter + - ObjectBox (= 4.0.1) - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - permission_handler_apple (9.3.0): + - Flutter DEPENDENCIES: - Flutter (from `Flutter`) - flutter_blue_plus (from `.symlinks/plugins/flutter_blue_plus/ios`) + - objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + +SPEC REPOS: + trunk: + - ObjectBox EXTERNAL SOURCES: Flutter: :path: Flutter flutter_blue_plus: :path: ".symlinks/plugins/flutter_blue_plus/ios" + objectbox_flutter_libs: + :path: ".symlinks/plugins/objectbox_flutter_libs/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_blue_plus: 4837da7d00cf5d441fdd6635b3a57f936778ea96 + ObjectBox: 0bc4bb75eea85f6af06b369148b334c2056bbc29 + objectbox_flutter_libs: 2ce0da386c780878687c736b528ceaf371573efb path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.13.0 +COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b8b225f..c68605a 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -138,7 +138,6 @@ 433DDD3DABCB7EF9EEAA70A0 /* Pods-RunnerTests.profile-staging.xcconfig */, C6A878D9068FE7F973231B67 /* Pods-RunnerTests.profile-development.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -223,6 +222,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 7AEE0F6C1047BEE8EA71DFC2 /* [CP] Embed Pods Frameworks */, + 5F268BC34EF8F50D378BA2D7 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -332,6 +332,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 5F268BC34EF8F50D378BA2D7 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 67B1C4E35E270A619D5B70BF /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -497,7 +514,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -506,8 +526,9 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -591,7 +612,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -600,8 +624,9 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -680,7 +705,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -689,8 +717,9 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -775,6 +804,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-dev"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "[DEV] Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -783,7 +813,7 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker.dev"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker.dev"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -867,6 +897,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-dev"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "[DEV] Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -875,7 +906,7 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker.dev"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker.dev"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -958,6 +989,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-dev"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "[DEV] Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -966,7 +998,7 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker.dev"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker.dev"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1052,6 +1084,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-stg"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "[STG] Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -1060,7 +1093,7 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker.stg"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker.stg"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1146,6 +1179,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-stg"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "[STG] Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -1154,7 +1188,7 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker.stg"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker.stg"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1235,6 +1269,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon-stg"; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = T67F6A44RM; ENABLE_BITCODE = NO; FLAVOR_APP_NAME = "[STG] Xiao Pet Tracker"; INFOPLIST_FILE = Runner/Info.plist; @@ -1243,7 +1278,7 @@ "@executable_path/Frameworks", ); PACKAGE_CONFIG = .dart_tool/package_config.json; - PRODUCT_BUNDLE_IDENTIFIER = "com.example.verygoodcore.xiao-pet-tracker.stg"; + PRODUCT_BUNDLE_IDENTIFIER = "sh.avoid.xiao-pet-tracker.stg"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index b2c19ce..917d6a3 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,11 +2,8 @@ - CFBundleLocalizations - - en - es - + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -17,6 +14,11 @@ $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 + CFBundleLocalizations + + en + es + CFBundleName $(FLAVOR_APP_NAME) CFBundlePackageType @@ -29,6 +31,12 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + NSBluetoothAlwaysUsageDescription + This app needs access to Bluetooth to function properly. + NSBluetoothPeripheralUsageDescription + This app needs access to Bluetooth to connect to the xiao microcontroller sensor. + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -46,13 +54,9 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UISupportsDocumentBrowser + UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - NSBluetoothAlwaysUsageDescription - This app needs access to Bluetooth to function properly. diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index f1473aa..c24481c 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -6,9 +6,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:forui/forui.dart'; import 'package:xiao_pet_tracker/app_router/app_router.dart'; import 'package:xiao_pet_tracker/bootstrap.dart'; -import 'package:xiao_pet_tracker/home/view/home_page.dart'; import 'package:xiao_pet_tracker/l10n/l10n.dart'; -import 'package:xiao_pet_tracker/screens/bluetooth_off_screen.dart'; class AppView extends StatefulWidget { const AppView({super.key}); @@ -42,11 +40,11 @@ class _AppState extends State { @override Widget build(BuildContext context) { - Widget screen = _adapterState == BluetoothAdapterState.on - ? const HomePage() - : BluetoothOffScreen( - adapterState: _adapterState, - ); + // Widget screen = _adapterState == BluetoothAdapterState.on + // ? const HomePage() + // : BluetoothOffScreen( + // adapterState: _adapterState, + // ); return MaterialApp.router( title: 'Xiao Pet Tracker', diff --git a/lib/app_router/app_router.dart b/lib/app_router/app_router.dart index 55659fc..8e3a70d 100644 --- a/lib/app_router/app_router.dart +++ b/lib/app_router/app_router.dart @@ -14,17 +14,18 @@ class AppRouter extends RootStackRouter { page: MainRoute.page, path: '/', children: [ - AutoRoute( - page: HomeRoute.page, - ), AutoRoute( page: XiaoConnectorRoute.page, ), AutoRoute( - page: SettingsRoute.page, + page: RecordingsRoute.page, + children: [], ), ], - ) + ), + AutoRoute( + page: RecordingsDetailsRoute.page, + ), ]; } @@ -65,7 +66,7 @@ class MainPage extends StatelessWidget { animationDuration: const Duration(milliseconds: 650), routes: const [ XiaoConnectorRoute(), - SettingsRoute(), + RecordingsRoute(), ], bottomNavigationBuilder: (context, tabsRouter) { return FBottomNavigationBar( @@ -73,12 +74,12 @@ class MainPage extends StatelessWidget { onChange: tabsRouter.setActiveIndex, children: [ FBottomNavigationBarItem( - label: const Text('Home'), - icon: FIcon(FAssets.icons.house), + label: const Text('Xiao Connector'), + icon: FIcon(FAssets.icons.microchip), ), FBottomNavigationBarItem( - label: const Text('Settings'), - icon: FIcon(FAssets.icons.settings), + label: const Text('Recordings'), + icon: FIcon(FAssets.icons.disc3), ), ], ); diff --git a/lib/app_router/app_router.gr.dart b/lib/app_router/app_router.gr.dart index 1423936..6991f16 100644 --- a/lib/app_router/app_router.gr.dart +++ b/lib/app_router/app_router.gr.dart @@ -8,36 +8,21 @@ // coverage:ignore-file // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:auto_route/auto_route.dart' as _i5; -import 'package:xiao_pet_tracker/app_router/app_router.dart' as _i2; -import 'package:xiao_pet_tracker/home/view/home_page.dart' as _i1; -import 'package:xiao_pet_tracker/settings/view/settings_page.dart' as _i3; +import 'package:auto_route/auto_route.dart' as _i6; +import 'package:flutter/material.dart' as _i7; +import 'package:xiao_pet_tracker/app_router/app_router.dart' as _i1; +import 'package:xiao_pet_tracker/recordings/view/recordings_details.dart' + as _i2; +import 'package:xiao_pet_tracker/recordings/view/recordings_page.dart' as _i3; import 'package:xiao_pet_tracker/xiao_connector/view/xiao_connector_page.dart' as _i4; +import 'package:xiao_pet_tracker/xiao_connector/view/xiao_data_page.dart' + as _i5; /// generated route for -/// [_i1.HomePage] -class HomeRoute extends _i5.PageRouteInfo { - const HomeRoute({List<_i5.PageRouteInfo>? children}) - : super( - HomeRoute.name, - initialChildren: children, - ); - - static const String name = 'HomeRoute'; - - static _i5.PageInfo page = _i5.PageInfo( - name, - builder: (data) { - return const _i1.HomePage(); - }, - ); -} - -/// generated route for -/// [_i2.MainPage] -class MainRoute extends _i5.PageRouteInfo { - const MainRoute({List<_i5.PageRouteInfo>? children}) +/// [_i1.MainPage] +class MainRoute extends _i6.PageRouteInfo { + const MainRoute({List<_i6.PageRouteInfo>? children}) : super( MainRoute.name, initialChildren: children, @@ -45,37 +30,90 @@ class MainRoute extends _i5.PageRouteInfo { static const String name = 'MainRoute'; - static _i5.PageInfo page = _i5.PageInfo( + static _i6.PageInfo page = _i6.PageInfo( name, builder: (data) { - return const _i2.MainPage(); + return const _i1.MainPage(); }, ); } /// generated route for -/// [_i3.SettingsPage] -class SettingsRoute extends _i5.PageRouteInfo { - const SettingsRoute({List<_i5.PageRouteInfo>? children}) - : super( - SettingsRoute.name, +/// [_i2.RecordingsDetailsPage] +class RecordingsDetailsRoute + extends _i6.PageRouteInfo { + RecordingsDetailsRoute({ + required String uuid, + required String type, + _i7.Key? key, + List<_i6.PageRouteInfo>? children, + }) : super( + RecordingsDetailsRoute.name, + args: RecordingsDetailsRouteArgs( + uuid: uuid, + type: type, + key: key, + ), initialChildren: children, ); - static const String name = 'SettingsRoute'; + static const String name = 'RecordingsDetailsRoute'; - static _i5.PageInfo page = _i5.PageInfo( + static _i6.PageInfo page = _i6.PageInfo( name, builder: (data) { - return const _i3.SettingsPage(); + final args = data.argsAs(); + return _i2.RecordingsDetailsPage( + uuid: args.uuid, + type: args.type, + key: args.key, + ); + }, + ); +} + +class RecordingsDetailsRouteArgs { + const RecordingsDetailsRouteArgs({ + required this.uuid, + required this.type, + this.key, + }); + + final String uuid; + + final String type; + + final _i7.Key? key; + + @override + String toString() { + return 'RecordingsDetailsRouteArgs{uuid: $uuid, type: $type, key: $key}'; + } +} + +/// generated route for +/// [_i3.RecordingsPage] +class RecordingsRoute extends _i6.PageRouteInfo { + const RecordingsRoute({List<_i6.PageRouteInfo>? children}) + : super( + RecordingsRoute.name, + initialChildren: children, + ); + + static const String name = 'RecordingsRoute'; + + static _i6.PageInfo page = _i6.PageInfo( + name, + builder: (data) { + return const _i3.RecordingsPage(); }, ); } /// generated route for /// [_i4.XiaoConnectorPage] -class XiaoConnectorRoute extends _i5.PageRouteInfo { - const XiaoConnectorRoute({List<_i5.PageRouteInfo>? children}) +class XiaoConnectorRoute extends _i6.PageRouteInfo { + const XiaoConnectorRoute({List<_i6.PageRouteInfo>? children}) : super( XiaoConnectorRoute.name, initialChildren: children, @@ -83,10 +121,29 @@ class XiaoConnectorRoute extends _i5.PageRouteInfo { static const String name = 'XiaoConnectorRoute'; - static _i5.PageInfo page = _i5.PageInfo( + static _i6.PageInfo page = _i6.PageInfo( name, builder: (data) { return const _i4.XiaoConnectorPage(); }, ); } + +/// generated route for +/// [_i5.XiaoDataPage] +class XiaoDataRoute extends _i6.PageRouteInfo { + const XiaoDataRoute({List<_i6.PageRouteInfo>? children}) + : super( + XiaoDataRoute.name, + initialChildren: children, + ); + + static const String name = 'XiaoDataRoute'; + + static _i6.PageInfo page = _i6.PageInfo( + name, + builder: (data) { + return const _i5.XiaoDataPage(); + }, + ); +} diff --git a/lib/counter/counter.dart b/lib/counter/counter.dart deleted file mode 100644 index cc3f0c5..0000000 --- a/lib/counter/counter.dart +++ /dev/null @@ -1,2 +0,0 @@ -export 'cubit/counter_cubit.dart'; -export 'view/counter_page.dart'; diff --git a/lib/counter/cubit/counter_cubit.dart b/lib/counter/cubit/counter_cubit.dart deleted file mode 100644 index 70bd952..0000000 --- a/lib/counter/cubit/counter_cubit.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:bloc/bloc.dart'; - -class CounterCubit extends Cubit { - CounterCubit() : super(0); - - void increment() => emit(state + 1); - void decrement() => emit(state - 1); -} diff --git a/lib/counter/view/counter_page.dart b/lib/counter/view/counter_page.dart deleted file mode 100644 index 781d175..0000000 --- a/lib/counter/view/counter_page.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:xiao_pet_tracker/counter/counter.dart'; -import 'package:xiao_pet_tracker/l10n/l10n.dart'; - -class CounterPage extends StatelessWidget { - const CounterPage({super.key}); - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) => CounterCubit(), - child: const CounterView(), - ); - } -} - -class CounterView extends StatelessWidget { - const CounterView({super.key}); - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Scaffold( - appBar: AppBar(title: Text(l10n.counterAppBarTitle)), - body: const Center(child: CounterText()), - floatingActionButton: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - FloatingActionButton( - onPressed: () => context.read().increment(), - child: const Icon(Icons.add), - ), - const SizedBox(height: 8), - FloatingActionButton( - onPressed: () => context.read().decrement(), - child: const Icon(Icons.remove), - ), - ], - ), - ); - } -} - -class CounterText extends StatelessWidget { - const CounterText({super.key}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final count = context.select((CounterCubit cubit) => cubit.state); - return Text('$count', style: theme.textTheme.displayLarge); - } -} diff --git a/lib/home/view/home_page.dart b/lib/home/view/home_page.dart deleted file mode 100644 index 2a95b95..0000000 --- a/lib/home/view/home_page.dart +++ /dev/null @@ -1,296 +0,0 @@ -import 'dart:async'; - -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_blue_plus_windows/flutter_blue_plus_windows.dart'; - -@RoutePage() -class HomePage extends StatelessWidget { - const HomePage({super.key}); - - @override - Widget build(BuildContext context) { - return const HomeScreen(); - } -} - -class HomeScreen extends StatefulWidget { - const HomeScreen({super.key}); - - @override - State createState() => _HomeScreenState(); -} - -class _HomeScreenState extends State { - List _systemDevices = []; - List _scanResults = []; - bool _isScanning = false; - late StreamSubscription> _scanResultsSubscription; - late StreamSubscription _isScanningSubscription; - - bool _connected = false; - bool _found = false; - - BluetoothDevice? device; - - List _services = []; - - Future scanDevices() async { - if (await FlutterBluePlus.isSupported == false) { - print("Bluetooth not supported by this device"); - return; - } - final scannedDevices = {}; - - const timeout = Duration(seconds: 3); - await FlutterBluePlus.startScan(timeout: timeout); - - final sub = - FlutterBluePlus.scanResults.expand((e) => e).listen(scannedDevices.add); - - // await Future.delayed(timeout); - sub.cancel(); - scannedDevices.forEach(print); - print('Scanned devices: ${scannedDevices.length}'); - } - - @override - void initState() { - super.initState(); - - _scanResultsSubscription = FlutterBluePlus.scanResults.listen((results) { - _scanResults = results; - if (mounted) { - setState(() {}); - } - }, onError: (e) { - print('Scan Results Subscription Error: $e'); - }); - - _isScanningSubscription = FlutterBluePlus.isScanning.listen((state) { - _isScanning = state; - if (mounted) { - setState(() {}); - } - }); - } - - @override - void dispose() { - _scanResultsSubscription.cancel(); - _isScanningSubscription.cancel(); - super.dispose(); - } - - Future onScanPressed() async { - try { - _systemDevices = await FlutterBluePlus.systemDevices([]); - } catch (e) { - print('System Devices Error: $e'); - } - - try { - await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); - } catch (e) { - print('Start Scan Error: $e'); - } - - if (mounted) { - setState(() {}); - } - } - - Future onConnectPressed() async { - try { - _systemDevices = await FlutterBluePlus.systemDevices([]); - } catch (e) { - print('System Devices Error: $e'); - } - - try { - await FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); - } catch (e) { - print('Start Scan Error: $e'); - } - - FlutterBluePlus.scanResults.listen((results) async { - for (ScanResult r in results) { - if (kDebugMode) { - print(r); - } - - if (r.device.advName == "Go Bluetooth") { - await FlutterBluePlus.stopScan(); - device = r.device; - - print("FOUND BLUETOOTH DEVICE"); - - await device?.connect(); - - try { - _services = await device?.discoverServices() ?? []; - } catch (e) { - print("Discover Services Error: $e"); - } - - if (mounted) { - setState(() { - _connected = true; - }); - } - } - } - }); - } - - Future onStopPressed() async { - try { - FlutterBluePlus.stopScan(); - } catch (e) { - print('Stop Scan Error: $e'); - } - } - - Future onRefresh() { - if (_isScanning == false) { - FlutterBluePlus.startScan(timeout: const Duration(seconds: 15)); - } - if (mounted) { - setState(() {}); - } - return Future.delayed(const Duration(milliseconds: 500)); - } - - Future handleDisconnect() async { - await device?.disconnect(); - } - - Future onDisconnectPressed() async { - await handleDisconnect(); - setState(() { - _connected = false; - }); - } - - @override - Future deactivate() async { - super.deactivate(); - await handleDisconnect(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('XIAO Sense'), - ), - body: SingleChildScrollView( - child: Column( - children: [ - // ListView.builder( - // shrinkWrap: true, - // physics: const NeverScrollableScrollPhysics(), - // itemCount: _scanResults.length, - // itemBuilder: (context, index) { - // return ListTile( - // title: Text(_scanResults[index].device.remoteId.toString()), - // subtitle: Text(_scanResults[index].device.advName), - // ); - // }, - // ), - if (_connected) Text('Connected!'), - ElevatedButton( - // enabled: !_isScanning, - onPressed: () async { - // await Future.delayed(const Duration(seconds: 2)); - // onScanPressed(); - if (_connected) { - onDisconnectPressed(); - } else { - onConnectPressed(); - } - }, - // icon: _isScanning - // ? const SizedBox.square( - // dimension: 16, - // child: CircularProgressIndicator(), - // ) - // : null, - child: _isScanning - ? const Text('Please wait') - : _connected - ? const Text('Disconnect') - : const Text('Connect'), - ), - ElevatedButton( - onPressed: () async { - print('Toggle LED'); - print(_services); - final ledService = _services - .where( - (s) => - s.serviceUuid == - Guid('a0b40001-926d-4d61-98df-8c5c62ee53b3'), - ) - .first - .characteristics - .where((c) => - c.characteristicUuid == - Guid('a0b40002-926d-4d61-98df-8c5c62ee53b3')) - .first; - - ledService.write([ - 0, - 1, - 0, - ]); - // await Future.delayed(Duration(seconds: 2)); - List ledServiceValue = await ledService.read(); - print("READ LED SERVICE: $ledServiceValue"); - }, - child: Text('Toggle LED'), - ), - ElevatedButton( - onPressed: () async { - print('Toggle LED'); - print(_services); - final ledService = _services - .where( - (s) => - s.serviceUuid == - Guid('4c534d36-4453-3354-5253-657276696365'), - ) - .first - .characteristics - .where( - (c) => - c.characteristicUuid == - Guid('61636365-6c65-7261-7469-6f6e44617461'), - ) - .first; - - final senseSubscription = - ledService.onValueReceived.listen((value) { - print("UPDATE: ${String.fromCharCodes(value)}"); - }); - - device!.cancelWhenDisconnected(senseSubscription); - - await ledService.setNotifyValue(true); - - // await ledService.write([1, 2, 3]); - // await Future.delayed(Duration(seconds: 2)); - // List ledServiceValue = await ledService.read(); - // print("READ Sense SERVICE: $ledServiceValue"); - // print( - // "READ Sense SERVICE: ${String.fromCharCodes(ledServiceValue)}"); - }, - child: Text('Read Sense'), - ) - ], - ), - ), - ); - } -} diff --git a/lib/home/view/view.dart b/lib/home/view/view.dart deleted file mode 100644 index e4ff269..0000000 --- a/lib/home/view/view.dart +++ /dev/null @@ -1 +0,0 @@ -export 'home_page.dart'; diff --git a/lib/objectbox-model.json b/lib/objectbox-model.json index f43b2c5..5a674f5 100644 --- a/lib/objectbox-model.json +++ b/lib/objectbox-model.json @@ -4,80 +4,120 @@ "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", "entities": [ { - "id": "2:23371849066855038", - "lastPropertyId": "10:4052938287274443905", + "id": "1:8883777039160106085", + "lastPropertyId": "28:5600095333294587898", "name": "CapturePoint", "properties": [ { - "id": "1:9209282349575641428", + "id": "1:7031141970833098443", "name": "id", "type": 6, "flags": 1 }, { - "id": "2:8441629944202144182", - "name": "rotationX", - "type": 9 - }, - { - "id": "3:3805193742978753321", - "name": "rotationY", - "type": 9 - }, - { - "id": "4:7632102454091387966", - "name": "rotationZ", - "type": 9 - }, - { - "id": "5:1175247580397683930", - "name": "accelerationX", - "type": 9 - }, - { - "id": "6:133552602314920473", - "name": "accelerationY", - "type": 9 - }, - { - "id": "7:7272664476171529007", - "name": "accelerationZ", - "type": 9 - }, - { - "id": "9:5697556233714735057", + "id": "2:9089711773119462722", "name": "type", "type": 9 }, { - "id": "10:4052938287274443905", - "name": "millisecondsSinceEpoch", + "id": "12:8018734828546727200", + "name": "uuid", + "type": 9 + }, + { + "id": "21:5571567596004635004", + "name": "rotationX", "type": 6 + }, + { + "id": "22:128950109858162458", + "name": "rotationY", + "type": 6 + }, + { + "id": "23:572745053585623035", + "name": "rotationZ", + "type": 6 + }, + { + "id": "24:174662964807733060", + "name": "accelerationX", + "type": 6 + }, + { + "id": "25:2607558191101966289", + "name": "accelerationY", + "type": 6 + }, + { + "id": "26:8078744267935466967", + "name": "accelerationZ", + "type": 6 + }, + { + "id": "27:2235290443011784871", + "name": "millisecondsSinceEpochReceived", + "type": 6 + }, + { + "id": "28:5600095333294587898", + "name": "millisecondsSinceEpochSend", + "type": 6 + } + ], + "relations": [] + }, + { + "id": "2:1642885530071590702", + "lastPropertyId": "3:4134744801341910087", + "name": "CaptureBox", + "properties": [ + { + "id": "1:6772033063452157797", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:6118609604439630810", + "name": "type", + "type": 9 + }, + { + "id": "3:4134744801341910087", + "name": "uuid", + "type": 9 } ], "relations": [] } ], - "lastEntityId": "2:23371849066855038", + "lastEntityId": "2:1642885530071590702", "lastIndexId": "0:0", "lastRelationId": "0:0", "lastSequenceId": "0:0", "modelVersion": 5, "modelVersionParserMinimum": 5, - "retiredEntityUids": [ - 6206931177901930822 - ], + "retiredEntityUids": [], "retiredIndexUids": [], "retiredPropertyUids": [ - 696937191447548336, - 7375635633551465262, - 7972299062087181706, - 5044874535120106939, - 6473009355337784814, - 8948425706967252950, - 5134040367670771080, - 4879888592430478383, - 2890906455219707986 + 5928562934131159642, + 5591187076567204697, + 595530508372810754, + 376663189826498742, + 2249450669900993772, + 3338488084769147616, + 6118432389289072923, + 652072338599943564, + 4974899424217069697, + 6174239378189558256, + 890878501153569237, + 1635504337236403097, + 3739937748546759727, + 1440167199258268261, + 90858017595682556, + 8864904344725714676, + 6079624305294726236 ], "retiredRelationUids": [], "version": 1 diff --git a/lib/objectbox.dart b/lib/objectbox.dart index 219aae7..55daafa 100644 --- a/lib/objectbox.dart +++ b/lib/objectbox.dart @@ -12,7 +12,7 @@ class ObjectBox { static Future create() async { final docsDir = await getApplicationDocumentsDirectory(); final store = - await openStore(directory: p.join(docsDir.path, 'obx-pet-tracker')); + await openStore(directory: p.join(docsDir.path, 'obx-pet-tracker-2')); return ObjectBox._create(store); } } diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart index b0e6031..637da20 100644 --- a/lib/objectbox.g.dart +++ b/lib/objectbox.g.dart @@ -14,61 +14,96 @@ import 'package:objectbox/internal.dart' import 'package:objectbox/objectbox.dart' as obx; import 'package:objectbox_flutter_libs/objectbox_flutter_libs.dart'; +import 'xiao_connector/models/capture_box.dart'; import 'xiao_connector/models/capture_point.dart'; export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file final _entities = [ obx_int.ModelEntity( - id: const obx_int.IdUid(2, 23371849066855038), + id: const obx_int.IdUid(1, 8883777039160106085), name: 'CapturePoint', - lastPropertyId: const obx_int.IdUid(10, 4052938287274443905), + lastPropertyId: const obx_int.IdUid(28, 5600095333294587898), flags: 0, properties: [ obx_int.ModelProperty( - id: const obx_int.IdUid(1, 9209282349575641428), + id: const obx_int.IdUid(1, 7031141970833098443), name: 'id', type: 6, flags: 1), obx_int.ModelProperty( - id: const obx_int.IdUid(2, 8441629944202144182), - name: 'rotationX', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(3, 3805193742978753321), - name: 'rotationY', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(4, 7632102454091387966), - name: 'rotationZ', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(5, 1175247580397683930), - name: 'accelerationX', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(6, 133552602314920473), - name: 'accelerationY', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(7, 7272664476171529007), - name: 'accelerationZ', - type: 9, - flags: 0), - obx_int.ModelProperty( - id: const obx_int.IdUid(9, 5697556233714735057), + id: const obx_int.IdUid(2, 9089711773119462722), name: 'type', type: 9, flags: 0), obx_int.ModelProperty( - id: const obx_int.IdUid(10, 4052938287274443905), - name: 'millisecondsSinceEpoch', + id: const obx_int.IdUid(12, 8018734828546727200), + name: 'uuid', + type: 9, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(21, 5571567596004635004), + name: 'rotationX', type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(22, 128950109858162458), + name: 'rotationY', + type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(23, 572745053585623035), + name: 'rotationZ', + type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(24, 174662964807733060), + name: 'accelerationX', + type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(25, 2607558191101966289), + name: 'accelerationY', + type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(26, 8078744267935466967), + name: 'accelerationZ', + type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(27, 2235290443011784871), + name: 'millisecondsSinceEpochReceived', + type: 6, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(28, 5600095333294587898), + name: 'millisecondsSinceEpochSend', + type: 6, + flags: 0) + ], + relations: [], + backlinks: []), + obx_int.ModelEntity( + id: const obx_int.IdUid(2, 1642885530071590702), + name: 'CaptureBox', + lastPropertyId: const obx_int.IdUid(3, 4134744801341910087), + flags: 0, + properties: [ + obx_int.ModelProperty( + id: const obx_int.IdUid(1, 6772033063452157797), + name: 'id', + type: 6, + flags: 1), + obx_int.ModelProperty( + id: const obx_int.IdUid(2, 6118609604439630810), + name: 'type', + type: 9, + flags: 0), + obx_int.ModelProperty( + id: const obx_int.IdUid(3, 4134744801341910087), + name: 'uuid', + type: 9, flags: 0) ], relations: [], @@ -110,22 +145,30 @@ Future openStore( obx_int.ModelDefinition getObjectBoxModel() { final model = obx_int.ModelInfo( entities: _entities, - lastEntityId: const obx_int.IdUid(2, 23371849066855038), + lastEntityId: const obx_int.IdUid(2, 1642885530071590702), lastIndexId: const obx_int.IdUid(0, 0), lastRelationId: const obx_int.IdUid(0, 0), lastSequenceId: const obx_int.IdUid(0, 0), - retiredEntityUids: const [6206931177901930822], + retiredEntityUids: const [], retiredIndexUids: const [], retiredPropertyUids: const [ - 696937191447548336, - 7375635633551465262, - 7972299062087181706, - 5044874535120106939, - 6473009355337784814, - 8948425706967252950, - 5134040367670771080, - 4879888592430478383, - 2890906455219707986 + 5928562934131159642, + 5591187076567204697, + 595530508372810754, + 376663189826498742, + 2249450669900993772, + 3338488084769147616, + 6118432389289072923, + 652072338599943564, + 4974899424217069697, + 6174239378189558256, + 890878501153569237, + 1635504337236403097, + 3739937748546759727, + 1440167199258268261, + 90858017595682556, + 8864904344725714676, + 6079624305294726236 ], retiredRelationUids: const [], modelVersion: 5, @@ -142,23 +185,20 @@ obx_int.ModelDefinition getObjectBoxModel() { object.id = id; }, objectToFB: (CapturePoint object, fb.Builder fbb) { - final rotationXOffset = fbb.writeString(object.rotationX); - final rotationYOffset = fbb.writeString(object.rotationY); - final rotationZOffset = fbb.writeString(object.rotationZ); - final accelerationXOffset = fbb.writeString(object.accelerationX); - final accelerationYOffset = fbb.writeString(object.accelerationY); - final accelerationZOffset = fbb.writeString(object.accelerationZ); final typeOffset = fbb.writeString(object.type); - fbb.startTable(11); + final uuidOffset = fbb.writeString(object.uuid); + fbb.startTable(29); fbb.addInt64(0, object.id); - fbb.addOffset(1, rotationXOffset); - fbb.addOffset(2, rotationYOffset); - fbb.addOffset(3, rotationZOffset); - fbb.addOffset(4, accelerationXOffset); - fbb.addOffset(5, accelerationYOffset); - fbb.addOffset(6, accelerationZOffset); - fbb.addOffset(8, typeOffset); - fbb.addInt64(9, object.millisecondsSinceEpoch); + fbb.addOffset(1, typeOffset); + fbb.addOffset(11, uuidOffset); + fbb.addInt64(20, object.rotationX); + fbb.addInt64(21, object.rotationY); + fbb.addInt64(22, object.rotationZ); + fbb.addInt64(23, object.accelerationX); + fbb.addInt64(24, object.accelerationY); + fbb.addInt64(25, object.accelerationZ); + fbb.addInt64(26, object.millisecondsSinceEpochReceived); + fbb.addInt64(27, object.millisecondsSinceEpochSend); fbb.finish(fbb.endTable()); return object.id; }, @@ -166,33 +206,67 @@ obx_int.ModelDefinition getObjectBoxModel() { final buffer = fb.BufferContext(fbData); final rootOffset = buffer.derefObject(0); final typeParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 20, ''); - final rotationXParam = const fb.StringReader(asciiOptimization: true) .vTableGet(buffer, rootOffset, 6, ''); - final rotationYParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 8, ''); - final rotationZParam = const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 10, ''); + final uuidParam = const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 26, ''); + final rotationXParam = + const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0); + final rotationYParam = + const fb.Int64Reader().vTableGet(buffer, rootOffset, 46, 0); + final rotationZParam = + const fb.Int64Reader().vTableGet(buffer, rootOffset, 48, 0); final accelerationXParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 12, ''); + const fb.Int64Reader().vTableGet(buffer, rootOffset, 50, 0); final accelerationYParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 14, ''); + const fb.Int64Reader().vTableGet(buffer, rootOffset, 52, 0); final accelerationZParam = - const fb.StringReader(asciiOptimization: true) - .vTableGet(buffer, rootOffset, 16, ''); - final millisecondsSinceEpochParam = - const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 22); + const fb.Int64Reader().vTableGet(buffer, rootOffset, 54, 0); + final millisecondsSinceEpochReceivedParam = + const fb.Int64Reader().vTableGet(buffer, rootOffset, 56, 0); + final millisecondsSinceEpochSendParam = + const fb.Int64Reader().vTableGet(buffer, rootOffset, 58, 0); final object = CapturePoint( type: typeParam, + uuid: uuidParam, rotationX: rotationXParam, rotationY: rotationYParam, rotationZ: rotationZParam, accelerationX: accelerationXParam, accelerationY: accelerationYParam, accelerationZ: accelerationZParam, - millisecondsSinceEpoch: millisecondsSinceEpochParam) + millisecondsSinceEpochReceived: + millisecondsSinceEpochReceivedParam, + millisecondsSinceEpochSend: millisecondsSinceEpochSendParam) + ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); + + return object; + }), + CaptureBox: obx_int.EntityDefinition( + model: _entities[1], + toOneRelations: (CaptureBox object) => [], + toManyRelations: (CaptureBox object) => {}, + getId: (CaptureBox object) => object.id, + setId: (CaptureBox object, int id) { + object.id = id; + }, + objectToFB: (CaptureBox object, fb.Builder fbb) { + final typeOffset = fbb.writeString(object.type); + final uuidOffset = fbb.writeString(object.uuid); + fbb.startTable(4); + fbb.addInt64(0, object.id); + fbb.addOffset(1, typeOffset); + fbb.addOffset(2, uuidOffset); + fbb.finish(fbb.endTable()); + return object.id; + }, + objectFromFB: (obx.Store store, ByteData fbData) { + final buffer = fb.BufferContext(fbData); + final rootOffset = buffer.derefObject(0); + final typeParam = const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 6, ''); + final uuidParam = const fb.StringReader(asciiOptimization: true) + .vTableGet(buffer, rootOffset, 8, ''); + final object = CaptureBox(type: typeParam, uuid: uuidParam) ..id = const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0); return object; @@ -208,35 +282,58 @@ class CapturePoint_ { static final id = obx.QueryIntegerProperty(_entities[0].properties[0]); + /// See [CapturePoint.type]. + static final type = + obx.QueryStringProperty(_entities[0].properties[1]); + + /// See [CapturePoint.uuid]. + static final uuid = + obx.QueryStringProperty(_entities[0].properties[2]); + /// See [CapturePoint.rotationX]. static final rotationX = - obx.QueryStringProperty(_entities[0].properties[1]); + obx.QueryIntegerProperty(_entities[0].properties[3]); /// See [CapturePoint.rotationY]. static final rotationY = - obx.QueryStringProperty(_entities[0].properties[2]); + obx.QueryIntegerProperty(_entities[0].properties[4]); /// See [CapturePoint.rotationZ]. static final rotationZ = - obx.QueryStringProperty(_entities[0].properties[3]); + obx.QueryIntegerProperty(_entities[0].properties[5]); /// See [CapturePoint.accelerationX]. static final accelerationX = - obx.QueryStringProperty(_entities[0].properties[4]); + obx.QueryIntegerProperty(_entities[0].properties[6]); /// See [CapturePoint.accelerationY]. static final accelerationY = - obx.QueryStringProperty(_entities[0].properties[5]); + obx.QueryIntegerProperty(_entities[0].properties[7]); /// See [CapturePoint.accelerationZ]. static final accelerationZ = - obx.QueryStringProperty(_entities[0].properties[6]); - - /// See [CapturePoint.type]. - static final type = - obx.QueryStringProperty(_entities[0].properties[7]); - - /// See [CapturePoint.millisecondsSinceEpoch]. - static final millisecondsSinceEpoch = obx.QueryIntegerProperty(_entities[0].properties[8]); + + /// See [CapturePoint.millisecondsSinceEpochReceived]. + static final millisecondsSinceEpochReceived = + obx.QueryIntegerProperty(_entities[0].properties[9]); + + /// See [CapturePoint.millisecondsSinceEpochSend]. + static final millisecondsSinceEpochSend = + obx.QueryIntegerProperty(_entities[0].properties[10]); +} + +/// [CaptureBox] entity fields to define ObjectBox queries. +class CaptureBox_ { + /// See [CaptureBox.id]. + static final id = + obx.QueryIntegerProperty(_entities[1].properties[0]); + + /// See [CaptureBox.type]. + static final type = + obx.QueryStringProperty(_entities[1].properties[1]); + + /// See [CaptureBox.uuid]. + static final uuid = + obx.QueryStringProperty(_entities[1].properties[2]); } diff --git a/lib/home/home.dart b/lib/recordings/recordings.dart similarity index 100% rename from lib/home/home.dart rename to lib/recordings/recordings.dart diff --git a/lib/recordings/view/recordings_details.dart b/lib/recordings/view/recordings_details.dart new file mode 100644 index 0000000..b02453c --- /dev/null +++ b/lib/recordings/view/recordings_details.dart @@ -0,0 +1,208 @@ +import 'dart:io'; + +import 'package:auto_route/auto_route.dart'; +import 'package:csv/csv.dart'; +import 'package:ditredi/ditredi.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:ditredi/ditredi.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:vector_math/vector_math_64.dart'; +import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart'; +import 'package:xiao_pet_tracker/xiao_connector/models/capture_point.dart'; + +@RoutePage() +class RecordingsDetailsPage extends StatefulWidget { + const RecordingsDetailsPage({ + required this.uuid, + required this.type, + super.key, + }); + + final String uuid; + final String type; + + @override + State createState() => _RecordingsDetailsPageState(); +} + +class _RecordingsDetailsPageState extends State { + final _controllerRotation = DiTreDiController(); + final _controllerAcceleration = DiTreDiController(); + List _capturePoints = []; + + @override + void initState() { + _capturePoints = + context.read().getCapturePointsOfUuid(widget.uuid); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Details: ${widget.type}'), + ), + body: SingleChildScrollView( + child: Column( + children: [ + const SizedBox( + height: 16, + ), + Text( + 'Capture Start Time: ${DateTime.fromMillisecondsSinceEpoch(_capturePoints.first.millisecondsSinceEpochSend)}', + ), + Text( + 'Capture End Time: ${DateTime.fromMillisecondsSinceEpoch(_capturePoints.first.millisecondsSinceEpochSend)}', + ), + const SizedBox( + height: 8, + ), + Text('Amount of captured points: ${_capturePoints.length}'), + const SizedBox( + height: 16, + ), + ElevatedButton( + onPressed: () async { + // await Permission.storage.request(); + + final Directory? downloadsDir = (Platform.isIOS) + ? await getApplicationDocumentsDirectory() + : await getDownloadsDirectory(); + + File f = File( + downloadsDir!.path + "/${widget.type}_${widget.uuid}.csv"); + + List> rows = []; + + List row = []; + row.add('sendTimeStamp'); + row.add('receivedTimeStamp'); + row.add('accelerationX'); + row.add('accelerationY'); + row.add('accelerationZ'); + row.add('rotationX'); + row.add('rotationY'); + row.add('rotationZ'); + + rows.add(row); + + for (var i = 0; i < _capturePoints.length; i++) { + List row = []; + row.add(_capturePoints[i].millisecondsSinceEpochSend); + row.add(_capturePoints[i].millisecondsSinceEpochReceived); + row.add(_capturePoints[i].accelerationX); + row.add(_capturePoints[i].accelerationY); + row.add(_capturePoints[i].accelerationZ); + row.add(_capturePoints[i].rotationX); + row.add(_capturePoints[i].rotationY); + row.add(_capturePoints[i].rotationZ); + rows.add(row); + } + + String csv = const ListToCsvConverter().convert(rows); + + f.writeAsString(csv); + }, + child: const Text('Export to CSV'), + ), + const SizedBox( + height: 16, + ), + const Text('Raw Rotation Data'), + Padding( + padding: const EdgeInsets.all(8), + child: Container( + height: 400, + // width: 400, + // color: Color.fromARGB(255, 0, 0, 0), + decoration: BoxDecoration( + color: const Color.fromARGB(255, 0, 0, 0), + border: + Border.all(color: const Color.fromARGB(255, 0, 0, 0))), + child: DiTreDiDraggable( + controller: _controllerRotation, + child: DiTreDi( + figures: [ + Cube3D( + 0, + Vector3(0, 0, 0), + color: const Color.fromARGB(0, 128, 0, 0), + ), + ..._capturePoints.map( + (p) => Point3D( + Vector3( + p.rotationX.toDouble(), + p.rotationY.toDouble(), + p.rotationZ.toDouble(), + ), + width: 2, + color: const Color.fromARGB(255, 255, 255, 255), + ), + ) + ], + controller: _controllerRotation, + ), + ), + ), + ), + const Text('Raw Acceleration Data'), + Padding( + padding: const EdgeInsets.all(8), + child: Container( + height: 400, + // width: 400, + // color: Color.fromARGB(255, 0, 0, 0), + decoration: BoxDecoration( + color: const Color.fromARGB(255, 0, 0, 0), + border: + Border.all(color: const Color.fromARGB(255, 0, 0, 0))), + child: DiTreDiDraggable( + controller: _controllerAcceleration, + child: DiTreDi( + figures: [ + Cube3D( + 0, + Vector3(0, 0, 0), + color: const Color.fromARGB(0, 128, 0, 0), + ), + ..._capturePoints.map( + (p) => Point3D( + Vector3( + p.accelerationX.toDouble(), + p.accelerationY.toDouble(), + p.accelerationZ.toDouble(), + ), + width: 2, + color: const Color.fromARGB(255, 255, 255, 255), + ), + ) + ], + controller: _controllerAcceleration, + ), + ), + ), + ), + const SizedBox( + height: 100, + ), + // ListView.builder( + // shrinkWrap: true, + // physics: const NeverScrollableScrollPhysics(), + // itemCount: _capturePoints.length, + // itemBuilder: (context, i) { + // return ListTile( + // title: Text('data'), + // ); + // }, + // ), + ], + ), + ), + ); + } +} diff --git a/lib/recordings/view/recordings_page.dart b/lib/recordings/view/recordings_page.dart new file mode 100644 index 0000000..490f1fa --- /dev/null +++ b/lib/recordings/view/recordings_page.dart @@ -0,0 +1,70 @@ +import 'package:auto_route/auto_route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:forui/forui.dart'; +import 'package:xiao_pet_tracker/app_router/app_router.gr.dart'; +import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart'; +import 'package:xiao_pet_tracker/xiao_connector/models/capture_box.dart'; +import 'package:xiao_pet_tracker/xiao_connector/view/xiao_connector_page.dart'; + +@RoutePage() +class RecordingsPage extends StatelessWidget { + const RecordingsPage({super.key}); + + @override + Widget build(BuildContext context) { + return const RecordingsView(); + } +} + +class RecordingsView extends StatefulWidget { + const RecordingsView({super.key}); + + @override + State createState() => _SettingsViewState(); +} + +class _SettingsViewState extends State { + List _captureBoxes = []; + + @override + void initState() { + _captureBoxes = context.read().getAllCaptureBoxes(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Recordings'), + ), + body: RefreshIndicator( + onRefresh: _pullRefresh, + child: ListView.builder( + itemCount: _captureBoxes.length, + itemBuilder: (context, i) { + return ListTile( + leading: FIcon(FAssets.icons.pawPrint), + title: Text('Collection Type: ${_captureBoxes[i].type}'), + subtitle: Text('Uuid: ${_captureBoxes[i].uuid}'), + onTap: () { + AutoRouter.of(context).push( + RecordingsDetailsRoute( + type: _captureBoxes[i].type, + uuid: _captureBoxes[i].uuid, + ), + ); + }, + ); + }, + ), + ), + ); + } + + Future _pullRefresh() async { + _captureBoxes = context.read().getAllCaptureBoxes(); + setState(() {}); + } +} diff --git a/lib/recordings/view/view.dart b/lib/recordings/view/view.dart new file mode 100644 index 0000000..cd54113 --- /dev/null +++ b/lib/recordings/view/view.dart @@ -0,0 +1 @@ +export 'recordings_page.dart'; diff --git a/lib/screens/bluetooth_off_screen.dart b/lib/screens/bluetooth_off_screen.dart deleted file mode 100644 index d5b081b..0000000 --- a/lib/screens/bluetooth_off_screen.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_blue_plus_windows/flutter_blue_plus_windows.dart'; - -class BluetoothOffScreen extends StatelessWidget { - const BluetoothOffScreen({ - super.key, - this.adapterState, - }); - - final BluetoothAdapterState? adapterState; - - Widget buildBluetoothOffIcon(BuildContext context) { - return const Icon( - Icons.bluetooth_disabled, - size: 200, - color: Colors.white54, - ); - } - - Widget buildTitle(BuildContext context) { - String? state = adapterState?.toString().split(".").last; - return Text( - 'Bluetooth Adapter is ${state != null ? state : 'not available'}', - style: Theme.of(context) - .primaryTextTheme - .titleSmall - ?.copyWith(color: Colors.white), - ); - } - - Widget buildTurnOnButton(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(20), - child: ElevatedButton( - onPressed: () async { - try { - if (Platform.isAndroid) { - await FlutterBluePlus.turnOn(); - } - } catch (e) { - print("Error Turning On: $e"); - } - }, - child: const Text('TURN ON'), - ), - ); - } - - @override - Widget build(BuildContext context) { - return ScaffoldMessenger( - child: Scaffold( - backgroundColor: Colors.lightBlue, - body: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - buildBluetoothOffIcon(context), - buildTitle(context), - if (Platform.isAndroid) buildTurnOnButton(context), - ], - ), - ), - )); - } -} diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart deleted file mode 100644 index 00ffcf9..0000000 --- a/lib/settings/settings.dart +++ /dev/null @@ -1 +0,0 @@ -export 'view/view.dart'; diff --git a/lib/settings/view/settings_page.dart b/lib/settings/view/settings_page.dart deleted file mode 100644 index af122cd..0000000 --- a/lib/settings/view/settings_page.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:auto_route/auto_route.dart'; -import 'package:flutter/material.dart'; - -@RoutePage() -class SettingsPage extends StatelessWidget { - const SettingsPage({super.key}); - - @override - Widget build(BuildContext context) { - return const SettingsView(); - } -} - -class SettingsView extends StatelessWidget { - const SettingsView({super.key}); - - @override - Widget build(BuildContext context) { - return const Center( - child: Text('Settings'), - ); - } -} diff --git a/lib/settings/view/view.dart b/lib/settings/view/view.dart deleted file mode 100644 index 10ba3ac..0000000 --- a/lib/settings/view/view.dart +++ /dev/null @@ -1 +0,0 @@ -export 'settings_page.dart'; diff --git a/lib/variables.dart b/lib/variables.dart deleted file mode 100644 index 4abe3c9..0000000 --- a/lib/variables.dart +++ /dev/null @@ -1 +0,0 @@ -const bleName = "Go Bluetooth"; diff --git a/lib/xiao_connector/constants/constants.dart b/lib/xiao_connector/constants/constants.dart index 565fa08..e3f345e 100644 --- a/lib/xiao_connector/constants/constants.dart +++ b/lib/xiao_connector/constants/constants.dart @@ -1,6 +1,2 @@ const uuidLSM6DS3TRService = '4c534d36-4453-3354-5253-657276696365'; const uuidAccelerationData = '61636365-6c65-7261-7469-6f6e44617461'; - - - //tempSenseService = [16]byte{0x74, 0x65, 0x6D, 0x70, 0x53, 0x65, 0x6E, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65} - //temperatureSense = [16]byte{0x74, 0x65, 0x6D, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x6E, 0x73, 0x65} diff --git a/lib/xiao_connector/cubit/xiao_connector_cubit.dart b/lib/xiao_connector/cubit/xiao_connector_cubit.dart index 2128bed..fa7f099 100644 --- a/lib/xiao_connector/cubit/xiao_connector_cubit.dart +++ b/lib/xiao_connector/cubit/xiao_connector_cubit.dart @@ -4,10 +4,13 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_blue_plus_windows/flutter_blue_plus_windows.dart'; -import 'package:objectbox/objectbox.dart'; +import 'package:uuid/uuid.dart'; import 'package:xiao_pet_tracker/bootstrap.dart'; import 'package:xiao_pet_tracker/objectbox.dart'; +import 'package:xiao_pet_tracker/objectbox.g.dart'; +import 'package:xiao_pet_tracker/xiao_connector/models/capture_box.dart'; import 'package:xiao_pet_tracker/xiao_connector/models/capture_point.dart'; +import 'package:xiao_pet_tracker/xiao_connector/utils/utils.dart'; part 'xiao_connector_state.dart'; @@ -15,36 +18,38 @@ class XiaoConnectorCubit extends Cubit { XiaoConnectorCubit() : super(const XiaoConnectorState()); late BluetoothDevice device; - List _systemDevices = []; List _services = []; + late BluetoothCharacteristic _senseService; late StreamSubscription> dataCapturingSubscription; final ObjectBox _objectBox = getIt(); late final Box _capturePointsBox; + late final Box _captureBoxes; - bool gotRotationX = false; - bool gotRotationY = false; - bool gotRotationZ = false; + bool gotRotation = false; bool gotAcceleration = false; + bool gotTimeStamp = false; - late String _rotX; - late String _rotY; - late String _rotZ; - late String _accel; + int _rotX = 0; + int _rotY = 0; + int _rotZ = 0; + int _accX = 0; + int _accY = 0; + int _accZ = 0; + int _millisecondsSinceEpochSend = 0; + String _captureType = ''; + String _uuid = ''; + + bool isRecording = false; Future init() async { _capturePointsBox = _objectBox.store.box(); + _captureBoxes = _objectBox.store.box(); } Future connect() async { emit(state.copyWith(status: XiaoConnectorStatus.loading)); - try { - _systemDevices = await FlutterBluePlus.systemDevices([]); - } catch (e) { - emit(state.copyWith(status: XiaoConnectorStatus.failure)); - throw Exception('System Devices Error: $e'); - } try { // scan for devices for 15 seconds @@ -95,8 +100,46 @@ class XiaoConnectorCubit extends Cubit { ); } - Future startCapturing({required String captureType}) async { + Future setCapturingOn() async { final senseService = _services + .where( + (s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'), + ) + .first + .characteristics + .where( + (c) => + c.characteristicUuid == + Guid('63617074-7572-696E-6753-657276696365'), + ) + .first; + + await senseService.write([1]); + } + + Uint8List int64BigEndianBytes(int value) => + Uint8List(8)..buffer.asByteData().setInt64(0, value); + + Future setTime(int millisSinceEpoch) async { + final senseService = _services + .where( + (s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'), + ) + .first + .characteristics + .where( + (c) => + c.characteristicUuid == + Guid('756E6978-5469-6D65-5374-616D70527374'), + ) + .first; + + final List bytes = int64BigEndianBytes(millisSinceEpoch); + await senseService.write(bytes); + } + + Future startCapturing() async { + _senseService = _services .where( (s) => s.serviceUuid == Guid('4c534d36-4453-3354-5253-657276696365'), ) @@ -109,57 +152,70 @@ class XiaoConnectorCubit extends Cubit { ) .first; - dataCapturingSubscription = senseService.onValueReceived.listen((value) { - debugPrint('UPDATE: $value'); - debugPrint('UPDATE: ${String.fromCharCodes(value)}'); - - final valueAsString = String.fromCharCodes(value); - - if (valueAsString.startsWith('rX=')) { - gotRotationX = true; - _rotX = valueAsString.replaceAll('rX=', ''); + dataCapturingSubscription = _senseService.onValueReceived.listen((value) { + // rotation + if (value.last == 1) { + _rotX = fromBytesToInt32(value[0], value[1], value[2], value[3]); + _rotY = fromBytesToInt32(value[4], value[5], value[6], value[7]); + _rotZ = fromBytesToInt32(value[8], value[9], value[10], value[11]); + gotRotation = true; } - if (valueAsString.startsWith('rY=')) { - gotRotationY = true; - _rotY = valueAsString.replaceAll('rY=', ''); - } - - if (valueAsString.startsWith('rZ=')) { - gotRotationZ = true; - _rotZ = valueAsString.replaceAll('rZ=', ''); - } - - if (valueAsString.contains(',')) { + // acceleration + if (value.last == 2) { + _accX = fromBytesToInt32(value[0], value[1], value[2], value[3]); + _accY = fromBytesToInt32(value[4], value[5], value[6], value[7]); + _accZ = fromBytesToInt32(value[8], value[9], value[10], value[11]); gotAcceleration = true; - _accel = valueAsString; } - if (gotAcceleration && gotRotationX && gotRotationY && gotRotationZ) { - final accelerations = _accel.split(','); + if (value.last == 3) { + final timeStamp = fromBytesToInt64( + value[0], + value[1], + value[2], + value[3], + value[4], + value[5], + value[6], + value[7], + ); + _millisecondsSinceEpochSend = timeStamp; + gotTimeStamp = true; + } + + if (gotAcceleration && gotRotation && gotTimeStamp) { final capturePoint = CapturePoint( - type: captureType, + type: _captureType, + uuid: _uuid, rotationX: _rotX, rotationY: _rotY, rotationZ: _rotZ, - accelerationX: accelerations[0], - accelerationY: accelerations[1], - accelerationZ: accelerations[2], - millisecondsSinceEpoch: DateTime.now().toUtc().millisecondsSinceEpoch, + accelerationX: _accX, + accelerationY: _accY, + accelerationZ: _accZ, + millisecondsSinceEpochReceived: + DateTime.now().toUtc().millisecondsSinceEpoch, + millisecondsSinceEpochSend: _millisecondsSinceEpochSend, ); emit(state.copyWith(lastCapturedPoint: capturePoint)); - debugPrint("UPDATED POINT!"); - // _writeToObjectBox(capturePoint: capturePoint); - gotRotationX = false; - gotRotationY = false; - gotRotationZ = false; + + if (isRecording) { + _writeToObjectBox(capturePoint: capturePoint); + } + gotAcceleration = false; + gotRotation = false; + gotTimeStamp = false; } }); + await setCapturingOn(); + + await setTime(DateTime.now().millisecondsSinceEpoch); device.cancelWhenDisconnected(dataCapturingSubscription); - await senseService.setNotifyValue(true); + await _senseService.setNotifyValue(true); emit(state.copyWith(status: XiaoConnectorStatus.capturing)); } @@ -167,28 +223,39 @@ class XiaoConnectorCubit extends Cubit { Future stopCapturing() async { await dataCapturingSubscription.cancel(); + await _senseService.setNotifyValue(false); + emit(state.copyWith(status: XiaoConnectorStatus.connected)); } + void toggleRecording({required String captureType}) { + isRecording = !isRecording; + if (isRecording) { + _captureType = captureType; + _uuid = const Uuid().v4(); + _captureBoxes.put( + CaptureBox( + type: _captureType, + uuid: _uuid, + ), + ); + } + } + void _writeToObjectBox({ required CapturePoint capturePoint, }) { _capturePointsBox.put(capturePoint); } - void readFromObjectBox() { - final points = _capturePointsBox.getAll(); - for (var i = 0; i < points.length; i++) { - final point = points[i]; - debugPrint('Type: ${point.type}'); - debugPrint('Id: ${point.id}'); - debugPrint('RotX: ${point.rotationX}'); - debugPrint('RotY: ${point.rotationY}'); - debugPrint('RotZ: ${point.rotationZ}'); - debugPrint('AccelX: ${point.accelerationX}'); - debugPrint('AccelY: ${point.accelerationY}'); - debugPrint('AccelZ: ${point.accelerationZ}'); - debugPrint('Date: ${point.millisecondsSinceEpoch}'); - } + List getCapturePointsOfUuid(String uuid) { + final query = + _capturePointsBox.query(CapturePoint_.uuid.equals(uuid)).build(); + + final points = query.find(); + return points; } + + List getAllCaptureBoxes() => _captureBoxes.getAll(); + List getAllCapturePoints() => _capturePointsBox.getAll(); } diff --git a/lib/xiao_connector/models/capture_box.dart b/lib/xiao_connector/models/capture_box.dart new file mode 100644 index 0000000..596c73f --- /dev/null +++ b/lib/xiao_connector/models/capture_box.dart @@ -0,0 +1,14 @@ +import 'package:objectbox/objectbox.dart'; + +@Entity() +class CaptureBox { + CaptureBox({ + required this.type, + required this.uuid, + }); + @Id() + int id = 0; + + String type; + String uuid; +} diff --git a/lib/xiao_connector/models/capture_point.dart b/lib/xiao_connector/models/capture_point.dart index 85a8892..284fbb7 100644 --- a/lib/xiao_connector/models/capture_point.dart +++ b/lib/xiao_connector/models/capture_point.dart @@ -4,25 +4,30 @@ import 'package:objectbox/objectbox.dart'; class CapturePoint { CapturePoint({ required this.type, + required this.uuid, required this.rotationX, required this.rotationY, required this.rotationZ, required this.accelerationX, required this.accelerationY, required this.accelerationZ, - this.millisecondsSinceEpoch, + required this.millisecondsSinceEpochReceived, + required this.millisecondsSinceEpochSend, }); @Id() int id = 0; String type; - String rotationX; - String rotationY; - String rotationZ; - String accelerationX; - String accelerationY; - String accelerationZ; + String uuid; + + int rotationX; + int rotationY; + int rotationZ; + int accelerationX; + int accelerationY; + int accelerationZ; // @Property(type: PropertyType.date) - int? millisecondsSinceEpoch; + int millisecondsSinceEpochReceived; + int millisecondsSinceEpochSend; } diff --git a/lib/xiao_connector/utils/utils.dart b/lib/xiao_connector/utils/utils.dart new file mode 100644 index 0000000..6a0bb98 --- /dev/null +++ b/lib/xiao_connector/utils/utils.dart @@ -0,0 +1,32 @@ +import 'dart:typed_data'; + +int fromBytesToInt32(int b3, int b2, int b1, int b0) { + final int8List = Int8List(4) + ..[3] = b3 + ..[2] = b2 + ..[1] = b1 + ..[0] = b0; + return int8List.buffer.asByteData().getInt32(0); +} + +int fromBytesToInt64( + int b7, + int b6, + int b5, + int b4, + int b3, + int b2, + int b1, + int b0, +) { + final int8List = Int8List(8) + ..[7] = b7 + ..[6] = b6 + ..[5] = b5 + ..[4] = b4 + ..[3] = b3 + ..[2] = b2 + ..[1] = b1 + ..[0] = b0; + return int8List.buffer.asByteData().getInt64(0); +} diff --git a/lib/xiao_connector/view/capture_view.dart b/lib/xiao_connector/view/capture_view.dart index 048a98c..77ed137 100644 --- a/lib/xiao_connector/view/capture_view.dart +++ b/lib/xiao_connector/view/capture_view.dart @@ -1,46 +1,193 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:xiao_pet_tracker/app_router/app_router.dart'; import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart'; -class CaptureView extends StatelessWidget { +class CaptureView extends StatefulWidget { const CaptureView({super.key}); + @override + State createState() => _CaptureViewState(); +} + +class _CaptureViewState extends State { + late TextEditingController _controller; + bool _isTextFieldFocused = false; + + @override + void initState() { + super.initState(); + _controller = TextEditingController(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { + // final count = context.select((XiaoConnectorCubit cubit) => cubit.state); final lastCapturePoint = context .select((XiaoConnectorCubit cubit) => cubit.state.lastCapturedPoint); return Scaffold( appBar: AppBar( - title: const Text('Capturing'), + title: const Text('Live Feed'), ), body: SingleChildScrollView( child: Center( child: Column( children: [ + const SizedBox( + height: 8, + ), ElevatedButton( onPressed: () { context.read().stopCapturing(); }, - child: const Text('Stop Capturing'), + child: const Text('Close Live Feed'), + ), + const SizedBox( + height: 12, ), const Text( 'Last Captured Point', style: TextStyle(fontWeight: FontWeight.bold), ), + const Divider( + indent: 80, + endIndent: 80, + ), + const Text( + 'Received Time', + style: TextStyle(fontWeight: FontWeight.bold), + ), Text( - '${DateTime.fromMillisecondsSinceEpoch(lastCapturePoint?.millisecondsSinceEpoch ?? 0)}'), - const Divider(), + '${DateTime.fromMillisecondsSinceEpoch( + lastCapturePoint?.millisecondsSinceEpochReceived ?? 0, + )}', + ), + const Text( + 'Send Time', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const Text('(time the controller send the data)'), + Text( + '${DateTime.fromMillisecondsSinceEpoch( + lastCapturePoint?.millisecondsSinceEpochSend ?? 0, + )}', + ), + const SizedBox( + height: 12, + ), + ElevatedButton( + onPressed: () { + // context.read().startCapturing( + // captureType: 'test', + // ); + final timeToSend = DateTime.now().millisecondsSinceEpoch; + context.read().setTime(timeToSend); + }, + child: const Text('Send new Timestamp'), + ), + const SizedBox( + height: 12, + ), + const Divider( + indent: 80, + endIndent: 80, + ), Text('Acceleration X: ${lastCapturePoint?.accelerationX}'), Text('Acceleration Y: ${lastCapturePoint?.accelerationY}'), Text('Acceleration Z: ${lastCapturePoint?.accelerationZ}'), - const Divider(), + const Divider( + indent: 80, + endIndent: 80, + ), Text('Rotation X: ${lastCapturePoint?.rotationX}'), Text('Rotation Y: ${lastCapturePoint?.rotationY}'), Text('Rotation Z: ${lastCapturePoint?.rotationZ}'), + const SizedBox( + height: 16, + ), + Padding( + padding: const EdgeInsets.only(left: 32, right: 32, top: 16), + child: FocusScope( + child: Focus( + onFocusChange: (focus) { + _isTextFieldFocused = focus; + setState(() {}); + }, + child: TextField( + controller: _controller, + decoration: const InputDecoration( + label: Text('Capture Name'), + ), + ), + ), + ), + ), + const SizedBox( + height: 16, + ) ], ), ), ), + floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat, + floatingActionButton: _isTextFieldFocused + ? null + : FloatingActionButton.large( + child: context.read().isRecording + ? const Stack( + children: [ + Align( + child: Icon(Icons.pause), + ), + Align( + child: SizedBox.expand( + child: Padding( + padding: EdgeInsets.all(12), + child: CircularProgressIndicator(), + ), + ), + ), + ], + ) + : const Icon(Icons.play_arrow), + onPressed: () { + if (_controller.value.text == '') { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text( + 'Please enter Capture Name', + ), + content: const Text( + 'You need to enter a capture name before you can start capturing data.', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text( + 'Ok', + ), + ), + ], + ); + }, + ); + } else { + context.read().toggleRecording( + captureType: _controller.value.text, + ); + } + }, + ), ); } } diff --git a/lib/xiao_connector/view/connected_view.dart b/lib/xiao_connector/view/connected_view.dart new file mode 100644 index 0000000..598dc4a --- /dev/null +++ b/lib/xiao_connector/view/connected_view.dart @@ -0,0 +1,41 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart'; + +class ConnectedView extends StatelessWidget { + const ConnectedView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Xiao Connector'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + ''' +You are connected to the Xiao Sense. +Now you can open the live feed.''', + textAlign: TextAlign.center, + ), + const SizedBox( + height: 8, + ), + ElevatedButton( + onPressed: () { + context.read().startCapturing(); + // context.read().setCapturingOn(); + }, + child: const Text('Open Live Feed'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/xiao_connector/view/initial_view.dart b/lib/xiao_connector/view/initial_view.dart new file mode 100644 index 0000000..48c1787 --- /dev/null +++ b/lib/xiao_connector/view/initial_view.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart'; + +class InitialView extends StatelessWidget { + const InitialView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Xiao Connector'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + ''' +You are currently not connected to the Xiao Sense. +Click on `Connect` to try to connect to the device.''', + textAlign: TextAlign.center, + ), + const SizedBox( + height: 8, + ), + ElevatedButton( + onPressed: () { + context.read().connect(); + }, + child: const Text('Connect'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/xiao_connector/view/loading_view.dart b/lib/xiao_connector/view/loading_view.dart new file mode 100644 index 0000000..5aeeecf --- /dev/null +++ b/lib/xiao_connector/view/loading_view.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +class LoadingView extends StatelessWidget { + const LoadingView({super.key}); + + @override + Widget build(BuildContext context) { + return const Center( + child: CircularProgressIndicator(), + ); + } +} diff --git a/lib/xiao_connector/view/xiao_connector_page.dart b/lib/xiao_connector/view/xiao_connector_page.dart index 3507f9e..108e7d3 100644 --- a/lib/xiao_connector/view/xiao_connector_page.dart +++ b/lib/xiao_connector/view/xiao_connector_page.dart @@ -3,7 +3,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:xiao_pet_tracker/xiao_connector/cubit/xiao_connector_cubit.dart'; import 'package:xiao_pet_tracker/xiao_connector/view/capture_view.dart'; +import 'package:xiao_pet_tracker/xiao_connector/view/connected_view.dart'; import 'package:xiao_pet_tracker/xiao_connector/view/failure_view.dart'; +import 'package:xiao_pet_tracker/xiao_connector/view/initial_view.dart'; +import 'package:xiao_pet_tracker/xiao_connector/view/loading_view.dart'; @RoutePage() class XiaoConnectorPage extends StatelessWidget { @@ -24,31 +27,9 @@ class XiaoConnectorView extends StatelessWidget { body: BlocBuilder( builder: (context, state) { return switch (state.status) { - XiaoConnectorStatus.initial => ElevatedButton( - onPressed: () { - context.read().connect(); - }, - child: const Text('Connect'), - ), - XiaoConnectorStatus.loading => const CircularProgressIndicator(), - XiaoConnectorStatus.connected => Column( - children: [ - ElevatedButton( - onPressed: () { - context.read().startCapturing( - captureType: 'test', - ); - }, - child: const Text('Start Capturing'), - ), - ElevatedButton( - onPressed: () { - context.read().readFromObjectBox(); - }, - child: const Text('Get Points'), - ), - ], - ), + XiaoConnectorStatus.initial => const InitialView(), + XiaoConnectorStatus.loading => const LoadingView(), + XiaoConnectorStatus.connected => const ConnectedView(), XiaoConnectorStatus.capturing => const CaptureView(), XiaoConnectorStatus.failure => const FailureView(), }; diff --git a/pubspec.lock b/pubspec.lock index 547835b..87ef506 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -202,10 +202,10 @@ packages: dependency: transitive description: name: coverage - sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268" + sha256: "4b03e11f6d5b8f6e5bb5e9f7889a56fe6c5cbe942da5378ea4d4d7f73ef9dfe5" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" crypto: dependency: transitive description: @@ -214,6 +214,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.6" + csv: + dependency: "direct main" + description: + name: csv + sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c + url: "https://pub.dev" + source: hosted + version: "6.0.0" dart_style: dependency: transitive description: @@ -303,18 +311,18 @@ packages: dependency: transitive description: name: flutter_blue_plus - sha256: "22912f71d140ff5c15735d4b2f0425e0a5f61ce6444a29360e7fca05eeebbad7" + sha256: "45ccee4a838f321c301b6130fbf3de28f07a2d6334a69eb5e13b6f83683080e0" url: "https://pub.dev" source: hosted - version: "1.33.5" + version: "1.33.6" flutter_blue_plus_windows: dependency: "direct main" description: name: flutter_blue_plus_windows - sha256: "7ec7ff439e37d00640a5e74dfcdea61bc338c430a8a7b9e29e9a1468108d2a14" + sha256: "8e84f894da4e41e776776f0e706cc35a237862a4d883033464c793b9d38de116" url: "https://pub.dev" source: hosted - version: "1.24.15" + version: "1.24.20" flutter_localizations: dependency: "direct main" description: flutter @@ -324,10 +332,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "1b7723a814d84fb65869ea7115cdb3ee7c3be5a27a755c1ec60e049f6b9fcbb2" + sha256: "578bd8c508144fdaffd4f77b8ef2d8c523602275cd697cc3db284dbd762ef4ce" url: "https://pub.dev" source: hosted - version: "2.0.11" + version: "2.0.14" flutter_test: dependency: "direct dev" description: flutter @@ -606,10 +614,10 @@ packages: dependency: transitive description: name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "1.1.0" path_provider: dependency: "direct main" description: @@ -839,6 +847,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -935,30 +951,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: name: vector_graphics - sha256: "0b9149c6ddb013818075b072b9ddc1b89a5122fff1275d4648d297086b46c4f0" + sha256: "773c9522d66d523e1c7b25dfb95cc91c26a1e17b107039cfe147285e92de7878" url: "https://pub.dev" source: hosted - version: "1.1.12" + version: "1.1.14" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + sha256: "2430b973a4ca3c4dbc9999b62b8c719a160100dcbae5c819bae0cacce32c9cdb" url: "https://pub.dev" source: hosted - version: "1.1.11+1" + version: "1.1.12" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: f3b9b6e4591c11394d4be4806c63e72d3a41778547b2c1e2a8a04fadcfd7d173 + sha256: ab9ff38fc771e9ee1139320adbe3d18a60327370c218c60752068ebee4b49ab1 url: "https://pub.dev" source: hosted - version: "1.1.12" + version: "1.1.15" vector_math: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 0db0366..069bd13 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: xiao_pet_tracker description: A Very Good Project created by Very Good CLI. -version: 1.0.0+1 +version: 1.0.0+3 publish_to: none environment: @@ -10,6 +10,7 @@ dependencies: auto_route: ^9.2.2 beacon_distance: ^0.0.3 bloc: ^8.1.4 + csv: ^6.0.0 ditredi: ^2.0.2 equatable: ^2.0.5 flutter: @@ -27,6 +28,7 @@ dependencies: path: ^1.9.0 path_provider: ^2.1.5 permission_handler: ^11.3.1 + uuid: ^4.5.1 dev_dependencies: auto_route_generator: ^9.0.0 diff --git a/xiao_controller_code/go.mod b/xiao_controller_code/go.mod index 2fb494d..e596203 100644 --- a/xiao_controller_code/go.mod +++ b/xiao_controller_code/go.mod @@ -1,4 +1,4 @@ -module blinky +module xiao-pet-tracker go 1.23.1 diff --git a/xiao_controller_code/main.go b/xiao_controller_code/main.go index 5f7e6c5..ccc7a4e 100644 --- a/xiao_controller_code/main.go +++ b/xiao_controller_code/main.go @@ -4,38 +4,31 @@ import ( "encoding/binary" "fmt" "machine" - "math/rand" "time" "tinygo.org/x/bluetooth" "tinygo.org/x/drivers/lsm6ds3tr" ) -// TinyGo Drivers Docs: -// https://github.com/tinygo-org/drivers - -// TinyGo Bluetooth Docs: -// https://github.com/tinygo-org/bluetooth - var adapter = bluetooth.DefaultAdapter -var ledColor = [3]byte{0x00, 0x00, 0xff} -var leds = [3]machine.Pin{machine.LED_RED, machine.LED_GREEN, machine.LED_BLUE} -var hasColorChange = true +var isBleConnected bool = false -var senseAccelerationData string = "-0.000 -0.000 -0.000" - -var senseRotationData string = "-0.000 -0.000 -0.000" - -var bleConnected bool = false +var isCapturing bool = false var ( LSM6DS3TRService = [16]byte{0x4C, 0x53, 0x4D, 0x36, 0x44, 0x53, 0x33, 0x54, 0x52, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65} accelerationData = [16]byte{0x61, 0x63, 0x63, 0x65, 0x6C, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x44, 0x61, 0x74, 0x61} - tempSenseService = [16]byte{0x74, 0x65, 0x6D, 0x70, 0x53, 0x65, 0x6E, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65} - temperatureSense = [16]byte{0x74, 0x65, 0x6D, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x6E, 0x73, 0x65} + unixTimeStampRst = [16]byte{0x75, 0x6E, 0x69, 0x78, 0x54, 0x69, 0x6D, 0x65, 0x53, 0x74, 0x61, 0x6D, 0x70, 0x52, 0x73, 0x74} + capturingService = [16]byte{0x63, 0x61, 0x70, 0x74, 0x75, 0x72, 0x69, 0x6E, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65} + //tempSenseService = [16]byte{0x74, 0x65, 0x6D, 0x70, 0x53, 0x65, 0x6E, 0x73, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65} + //temperatureSense = [16]byte{0x74, 0x65, 0x6D, 0x70, 0x65, 0x72, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x6E, 0x73, 0x65} ) +var unixTimeStamp time.Time = time.Now() + +const sleepDuration time.Duration = time.Millisecond * 100 + func main() { // Configure LSM6DS3TR machine.I2C0.Configure(machine.I2CConfig{}) @@ -48,10 +41,6 @@ func main() { time.Sleep(time.Second) } } - // - // for _, led := range leds { - // led.Configure(machine.PinConfig{Mode: machine.PinOutput}) - // } // Configure Bluetooth must("enable BLE stack", adapter.Enable()) @@ -68,23 +57,10 @@ func main() { adapter.SetConnectHandler(func(device bluetooth.Device, connected bool) { if connected { - println("connected, not advertising...") - // leds[0].Low() - // leds[1].High() - // ledColor[0] = 0 - // ledColor[1] = 1 - // ledColor[2] = 0 - // hasColorChange = true - bleConnected = true + isBleConnected = true } else { - println("disconnected, advertising...") - // leds[0].High() - // leds[1].Low() - // ledColor[0] = 0 - // ledColor[1] = 0 - // ledColor[2] = 1 - // hasColorChange = true - bleConnected = false + isBleConnected = false + isCapturing = false } }) // @@ -98,8 +74,34 @@ func main() { { Handle: &senseCharacteristic, UUID: bluetooth.NewUUID(accelerationData), - Value: []byte(senseAccelerationData), - Flags: bluetooth.CharacteristicNotifyPermission | bluetooth.CharacteristicReadPermission | bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission, + // can only send a max amount of 20 bytes in one packet + //Value: []byte{}, + Flags: bluetooth.CharacteristicNotifyPermission | bluetooth.CharacteristicReadPermission | bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission, + }, + { + UUID: bluetooth.NewUUID(unixTimeStampRst), + Flags: bluetooth.CharacteristicNotifyPermission | bluetooth.CharacteristicReadPermission | bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission, + WriteEvent: func(client bluetooth.Connection, offset int, value []byte) { + if len(value) != 8 { + return + } + millisFromEpoch := binary.BigEndian.Uint64(value) + unixTimeStamp = time.Unix(0, int64(millisFromEpoch)*int64(time.Millisecond)) + }, + }, + { + UUID: bluetooth.NewUUID(capturingService), + Flags: bluetooth.CharacteristicNotifyPermission | bluetooth.CharacteristicReadPermission | bluetooth.CharacteristicWritePermission | bluetooth.CharacteristicWriteWithoutResponsePermission, + WriteEvent: func(client bluetooth.Connection, offset int, value []byte) { + if len(value) != 1 { + return + } + if value[0] == 1 { + isCapturing = true + } else { + isCapturing = false + } + }, }, }, })) @@ -119,55 +121,66 @@ func main() { // Main Loop for { - time.Sleep(100 * time.Millisecond) - - // for !hasColorChange { - // for i, led := range leds { - // led.Set(ledColor[i] == 0) - // } - // time.Sleep(10 * time.Millisecond) - // } - // hasColorChange = false - // Only read and update sensor data // with an active bluetooth connection - if bleConnected { + // and when `isCapturing` is set to true + if isBleConnected { X, Y, Z, _ := accel.ReadRotation() - rotX := fmt.Sprintf("rX=%f", float32(X)/100000000) - rotY := fmt.Sprintf("rY=%f", float32(Y)/100000000) - rotZ := fmt.Sprintf("rZ=%f", float32(Z)/100000000) - x, y, z, _ := accel.ReadAcceleration() - accel := fmt.Sprintf("%.3f,%.3f,%.3f", float32(x)/1000000, float32(y)/1000000, float32(z)/1000000) - //senseAccelerationData = dataTest - //tempTest, _ := accel.ReadTemperature() - //tempTestBytes := []byte{} - //tempData = fmt.Sprintf("%i", tempTest) + arrRot := valuesToByteArray(X, Y, Z, int8(1)) + senseCharacteristic.Write(arrRot) - //senseCharacteristic.Write([]byte(dataRotation)) - //senseCharacteristic.Write([]byte("-0.0,-1.2,-5.6")) - senseCharacteristic.Write([]byte(rotX)) - senseCharacteristic.Write([]byte(rotY)) - senseCharacteristic.Write([]byte(rotZ)) - senseCharacteristic.Write([]byte(accel)) + arrAcc := valuesToByteArray(x, y, z, int8(2)) + senseCharacteristic.Write(arrAcc) - // bs := make([]byte, 4) - // binary.LittleEndian.PutUint32(bs, X) + arrTime := timeStampToByteArray(unixTimeStamp.UnixMilli(), int8(3)) + senseCharacteristic.Write(arrTime) + } - //binary.LittleEndian.AppendUint32(tempTestBytes, uint32(tempTest)) - //tempCharacteristic.Write(tempTestBytes) + fmt.Println("TIME: ", unixTimeStamp) + + time.Sleep(sleepDuration) + if isCapturing { + unixTimeStamp = unixTimeStamp.Add(sleepDuration) } } } +func valuesToByteArray(x int32, y int32, z int32, p int8) []byte { + arr := make([]byte, 13) + arr[0] = byte(x) + arr[1] = byte(x >> 8) + arr[2] = byte(x >> 16) + arr[3] = byte(x >> 24) + arr[4] = byte(y) + arr[5] = byte(y >> 8) + arr[6] = byte(y >> 16) + arr[7] = byte(y >> 24) + arr[8] = byte(z) + arr[9] = byte(z >> 8) + arr[10] = byte(z >> 16) + arr[11] = byte(z >> 24) + arr[12] = byte(p) + return arr +} + +func timeStampToByteArray(value int64, p int8) []byte { + arr := make([]byte, 9) + arr[0] = byte(value) + arr[1] = byte(value >> 8) + arr[2] = byte(value >> 16) + arr[3] = byte(value >> 24) + arr[4] = byte(value >> 32) + arr[5] = byte(value >> 40) + arr[6] = byte(value >> 48) + arr[7] = byte(value >> 56) + arr[8] = byte(p) + return arr +} + func must(action string, err error) { if err != nil { panic("failed to " + action + ": " + err.Error()) } } - -// Returns an int >= min, < max -func randomInt(min, max int) uint8 { - return uint8(min + rand.Intn(max-min)) -} diff --git a/xiao_controller_code/test.go b/xiao_controller_code/test.go new file mode 100644 index 0000000..b644d76 --- /dev/null +++ b/xiao_controller_code/test.go @@ -0,0 +1,106 @@ +package main + +// import ( +// "encoding/binary" +// "fmt" +// "unsafe" +// ) + +// func main() { +// const x int32 = 2017403647 +// const y int32 = 1010101010 +// const z int32 = 2020202020 + +// // bytearr := valuesToByteArrayTest(x, y, z, 1) +// // fmt.Println("TEST: ", bytearr) + +// // slice := binary.LittleEndian.Uint32(bytearr[0:4]) +// // fmt.Println("SLICE:", slice) + +// // slice2 := binary.LittleEndian.Uint32(bytearr[4:8]) +// // fmt.Println("SLICE2:", slice2) + +// bs := make([]byte, 4) +// binary.LittleEndian.PutUint32(bs, uint32(y)) +// fmt.Println(bs) + +// // buf := make([]byte, 4) +// // arr[0] = byte(x >> 8) + +// // buf[0], buf[1], buf[2] = uint8(7676767>>16), uint8(id>>8), uint8(id) + +// bytearr := valuesToByteArray3(x, y, z, 1) +// fmt.Println("TEST: ", bytearr) + +// slice := binary.LittleEndian.Uint32(bytearr[0:4]) +// fmt.Println("SLICE:", slice) + +// slice2 := binary.LittleEndian.Uint32(bytearr[4:8]) +// fmt.Println("SLICE2:", slice2) + +// } + +// func valuesToByteArrayOld(x int32, y int32, z int32, p int8) []byte { +// arr := make([]byte, 13) +// for i := 0; i < 12; i++ { +// var test unsafe.Pointer +// var test2 unsafe.Pointer +// var test3 unsafe.Pointer +// if i < 4 { +// test = unsafe.Pointer(&x) +// } else if i < 8 { +// test2 = unsafe.Pointer(&y) +// } else if i < 12 { +// test3 = unsafe.Pointer(&z) +// } +// byt := *(*uint8)(unsafe.Pointer(uintptr(test) + uintptr(i))) +// arr[i] = byt +// } +// arr[12] = byte(p) +// return arr +// } + +// func valuesToByteArray1(x int32, y int32, z int32, p int8) []byte { +// arr := make([]byte, 13) +// arr[0] = byte(x) +// arr[1] = byte(x >> 8) +// arr[2] = byte(x >> 16) +// arr[3] = byte(x >> 24) +// arr[4] = byte(y) +// arr[5] = byte(y >> 8) +// arr[6] = byte(y >> 16) +// arr[7] = byte(y >> 24) +// arr[8] = byte(z) +// arr[9] = byte(z >> 8) +// arr[10] = byte(z >> 16) +// arr[11] = byte(z >> 24) +// arr[12] = byte(p) +// return arr +// } + +// func valuesToByteArray2(x int32, y int32, z int32, p int8) []byte { +// buf := make([]byte, 13) +// binary.LittleEndian.PutUint32(buf[0:], uint32(x)) +// binary.LittleEndian.PutUint32(buf[4:], uint32(y)) +// binary.LittleEndian.PutUint32(buf[8:], uint32(z)) +// buf[12] = byte(p) +// return buf +// } + +// func valuesToByteArray3(x int32, y int32, z int32, p int8) []byte { +// arr := make([]byte, 13) +// for i := 0; i < 12; i++ { +// var test unsafe.Pointer +// if i < 4 { +// test = unsafe.Pointer(&x) +// } else if i < 8 { +// test = unsafe.Pointer(&y) +// } else if i < 12 { +// test = unsafe.Pointer(&z) +// } +// byt := *(*uint8)(unsafe.Pointer(uintptr(test) + uintptr(i))) +// arr[i] = byt +// } +// arr[12] = byte(p) +// return arr +// }