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
+// }