├── .gitignore ├── esp32 ├── .gitignore ├── preferences.h ├── config.h ├── .vscode │ ├── arduino.json │ └── settings.json ├── local_fonts.h ├── scheduler.h ├── keyval.h ├── lcd.h ├── theme.h ├── splash.h ├── esp32.ino ├── registers.h ├── lcd.cpp ├── ble.h └── .clang-format ├── android-app ├── app │ ├── .gitignore │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── values │ │ │ │ │ ├── arrays.xml │ │ │ │ │ ├── dimens.xml │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── themes.xml │ │ │ │ │ └── strings.xml │ │ │ │ ├── drawable │ │ │ │ │ ├── bg.jpg │ │ │ │ │ ├── catface.png │ │ │ │ │ ├── roundabout.bmp │ │ │ │ │ ├── ic_home_black_24dp.xml │ │ │ │ │ ├── ic_dashboard_black_24dp.xml │ │ │ │ │ ├── ic_notifications_black_24dp.xml │ │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ ├── ic_launcher.webp │ │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ ├── ic_launcher.xml │ │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── menu │ │ │ │ │ └── bottom_nav_menu.xml │ │ │ │ ├── xml │ │ │ │ │ ├── backup_rules.xml │ │ │ │ │ ├── data_extraction_rules.xml │ │ │ │ │ └── root_preferences.xml │ │ │ │ ├── navigation │ │ │ │ │ └── mobile_navigation.xml │ │ │ │ ├── values-night │ │ │ │ │ └── themes.xml │ │ │ │ ├── layout │ │ │ │ │ ├── activity_ble_selection.xml │ │ │ │ │ ├── activity_main.xml │ │ │ │ │ ├── device_row_item.xml │ │ │ │ │ └── fragment_home.xml │ │ │ │ └── drawable-v24 │ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── maisonsmd │ │ │ │ │ └── catdrive │ │ │ │ │ ├── lib │ │ │ │ │ ├── BleCharacteristics.kt │ │ │ │ │ ├── NavigationNotification.kt │ │ │ │ │ ├── BleWriteQueue.kt │ │ │ │ │ ├── Intents.kt │ │ │ │ │ ├── NavigationData.kt │ │ │ │ │ ├── ParserHelper.kt │ │ │ │ │ ├── IntrospectionUtils.kt │ │ │ │ │ ├── BitmapHelper.kt │ │ │ │ │ ├── GMapsNotification.kt │ │ │ │ │ └── CustomSerializers.kt │ │ │ │ │ ├── ui │ │ │ │ │ ├── ActivityViewModel.kt │ │ │ │ │ ├── home │ │ │ │ │ │ └── HomeFragment.kt │ │ │ │ │ ├── BleDeviceSelectionActivity.kt │ │ │ │ │ └── settings │ │ │ │ │ │ └── SettingsFragment.kt │ │ │ │ │ ├── GoogleMapNotificationListener.kt │ │ │ │ │ ├── utils │ │ │ │ │ ├── ServiceManager.kt │ │ │ │ │ └── PermissionCheck.kt │ │ │ │ │ ├── service │ │ │ │ │ └── NavigationListener.kt │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ └── debug │ │ │ ├── ic_launcher-playstore.png │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ └── drawable │ │ │ └── ic_launcher_background.xml │ ├── proguard-rules.pro │ └── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── build.gradle ├── settings.gradle ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /esp32/.gitignore: -------------------------------------------------------------------------------- 1 | build -------------------------------------------------------------------------------- /android-app/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-app/app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/drawable/bg.jpg -------------------------------------------------------------------------------- /android-app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable/catface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/drawable/catface.png -------------------------------------------------------------------------------- /android-app/app/src/debug/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/ic_launcher-playstore.png -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable/roundabout.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/drawable/roundabout.bmp -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maisonsmd/esp32-google-maps/HEAD/android-app/app/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /esp32/preferences.h: -------------------------------------------------------------------------------- 1 | #ifndef PREFERENCES_H 2 | #define PREFERENCES_H 3 | 4 | namespace Pref { 5 | bool lightTheme = false; 6 | int brightness = 40; 7 | int speedLimit = 60; 8 | } // namespace Pref 9 | 10 | #endif // PREFERENCES_H -------------------------------------------------------------------------------- /android-app/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | -------------------------------------------------------------------------------- /android-app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /android-app/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '8.10.0' apply false 4 | id 'com.android.library' version '8.10.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.20' apply false 6 | } -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-app/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /esp32/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H 2 | #define CONFIG_H 3 | 4 | #define SCHEDULER_SOURCE millis() 5 | 6 | #define PIN_MISO 5 7 | #define PIN_MOSI 6 8 | #define PIN_SCLK 7 9 | #define PIN_LCD_CS 14 10 | #define PIN_LCD_DC 15 11 | #define PIN_LCD_RST 21 12 | #define PIN_BACKLIGHT 22 13 | 14 | #define HORIZONTAL 15 | 16 | #endif -------------------------------------------------------------------------------- /esp32/.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": "COM17", 3 | "board": "esp32:esp32:esp32c6", 4 | "programmer": "esptool", 5 | "sketch": "esp32.ino", 6 | "output": "build", 7 | "configuration": "JTAGAdapter=default,CDCOnBoot=cdc,PartitionScheme=no_ota,CPUFreq=160,FlashMode=qio,FlashFreq=80,FlashSize=4M,UploadSpeed=921600,DebugLevel=warn,EraseFlash=none,ZigbeeMode=default" 8 | } -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /android-app/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | rootProject.name = "CatDrive" 16 | include ':app' 17 | -------------------------------------------------------------------------------- /esp32/local_fonts.h: -------------------------------------------------------------------------------- 1 | #ifndef LOCAL_FONTS_H 2 | #define LOCAL_FONTS_H 3 | 4 | #include "lvgl.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | lv_font_t* get_montserrat_24(); 10 | lv_font_t* get_montserrat_bold_32(); 11 | lv_font_t* get_montserrat_number_bold_48(); 12 | lv_font_t* get_montserrat_semibold_24(); 13 | lv_font_t* get_montserrat_semibold_28(); 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | 18 | #endif // LOCAL_FONTS_H -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/menu/bottom_nav_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /android-app/app/src/main/java/com/maisonsmd/catdrive/lib/BleCharacteristics.kt: -------------------------------------------------------------------------------- 1 | package com.maisonsmd.catdrive.lib 2 | 3 | class BleCharacteristics { 4 | companion object { 5 | const val SERVICE_UUID = "ec91d7ab-e87c-48d5-adfa-cc4b2951298a" 6 | const val CHA_SETTINGS = "9d37a346-63d3-4df6-8eee-f0242949f59f" 7 | const val CHA_NAV = "0b11deef-1563-447f-aece-d3dfeb1c1f20" 8 | const val CHA_NAV_TBT_ICON = "d4d8fcca-16b2-4b8e-8ed5-90137c44a8ad" 9 | const val CHA_GPS_SPEED = "98b6073a-5cf3-4e73-b6d3-f8e05fa018a9" 10 | } 11 | } -------------------------------------------------------------------------------- /android-app/app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /android-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | misc.xml 22 | deploymentTargetDropDown.xml 23 | render.experimental.xml 24 | 25 | # Keystore files 26 | *.jks 27 | *.keystore 28 | 29 | # Google Services (e.g. APIs or Firebase) 30 | google-services.json 31 | 32 | # Android Profiling 33 | *.hprof -------------------------------------------------------------------------------- /android-app/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /esp32/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[cpp]": { 3 | "editor.formatOnSave": true 4 | }, 5 | "[c]": { 6 | "editor.formatOnSave": false 7 | }, 8 | "files.exclude": { 9 | "**/.git": true, 10 | "**/.svn": true, 11 | "**/.hg": true, 12 | "**/CVS": true, 13 | "**/.DS_Store": true, 14 | "**/Thumbs.db": true, 15 | "build": true, 16 | }, 17 | "files.associations": { 18 | "array": "cpp", 19 | "string_view": "cpp", 20 | "initializer_list": "cpp", 21 | "deque": "cpp", 22 | "list": "cpp", 23 | "string": "cpp", 24 | "unordered_map": "cpp", 25 | "unordered_set": "cpp", 26 | "vector": "cpp", 27 | "*.tcc": "cpp" 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /android-app/app/src/main/java/com/maisonsmd/catdrive/ui/ActivityViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.maisonsmd.catdrive.ui 2 | 3 | import android.bluetooth.BluetoothDevice 4 | import androidx.lifecycle.MutableLiveData 5 | import androidx.lifecycle.ViewModel 6 | import com.maisonsmd.catdrive.lib.NavigationData 7 | 8 | class ActivityViewModel : ViewModel() { 9 | val permissionUpdatedTimestamp = MutableLiveData().apply { value = 0 } 10 | val navigationData = MutableLiveData().apply { value = NavigationData() } 11 | val speed = MutableLiveData().apply { value = 0 } 12 | val connectedDevice = MutableLiveData().apply { value = null } 13 | val serviceRunInBackground = MutableLiveData().apply { value = false } 14 | } -------------------------------------------------------------------------------- /android-app/app/src/main/res/navigation/mobile_navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 17 | -------------------------------------------------------------------------------- /android-app/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /esp32/scheduler.h: -------------------------------------------------------------------------------- 1 | #ifndef SCHEDULER_H 2 | #define SCHEDULER_H 3 | 4 | #include "config.h" 5 | 6 | #define SCHEDULER_GUARD(__current__, __var__) \ 7 | if (__var__ > __current__) \ 8 | __var__ = __current__ 9 | #define SCHEDULER_CREATE(__name__) static uint32_t __name__ = 0 10 | 11 | #define TOKENPASTE(x, y) x##y 12 | #define TOKENPASTE2(x, y) TOKENPASTE(x, y) 13 | #define DO_EVERY(duration) \ 14 | SCHEDULER_CREATE(TOKENPASTE2(scheduler_, __LINE__)); \ 15 | if (onSchedule(SCHEDULER_SOURCE, TOKENPASTE2(scheduler_, __LINE__), duration)) 16 | 17 | // this function already guard timekeeper variable 18 | static bool onSchedule(const uint32_t& current, uint32_t& var, const uint32_t& interval) { 19 | SCHEDULER_GUARD(current, var); 20 | if (current < var + interval) 21 | return false; 22 | 23 | var = current; 24 | return true; 25 | } 26 | 27 | #endif -------------------------------------------------------------------------------- /android-app/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /android-app/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /android-app/app/src/main/java/com/maisonsmd/catdrive/lib/NavigationNotification.kt: -------------------------------------------------------------------------------- 1 | package com.maisonsmd.catdrive.lib 2 | 3 | import android.app.Notification 4 | import android.content.Context 5 | import android.service.notification.StatusBarNotification 6 | 7 | open class NavigationNotification(context: Context, statusBarNotification: StatusBarNotification) { 8 | protected val mNotification: Notification = statusBarNotification.notification 9 | protected val mContext = context 10 | protected var mAppSourceContext: Context = mContext.createPackageContext( 11 | statusBarNotification.packageName, 12 | Context.CONTEXT_IGNORE_SECURITY 13 | ) 14 | 15 | private var mNavigationData: NavigationData = NavigationData() 16 | var navigationData 17 | get() = mNavigationData 18 | set(value) { 19 | if (value == mNavigationData) 20 | return 21 | mNavigationData = value 22 | } 23 | 24 | init { 25 | mNavigationData.postTime = NavigationTimestamp(statusBarNotification.postTime) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Son H. Mai (Mason) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /android-app/app/src/main/java/com/maisonsmd/catdrive/lib/BleWriteQueue.kt: -------------------------------------------------------------------------------- 1 | package com.maisonsmd.catdrive.lib 2 | 3 | class BleWriteQueue { 4 | data class QueueItem(val uuid: String, val data: ByteArray, val overwrite: Boolean = true) { 5 | override fun equals(other: Any?): Boolean { 6 | if (this === other) return true 7 | if (javaClass != other?.javaClass) return false 8 | 9 | other as QueueItem 10 | 11 | if (uuid != other.uuid) return false 12 | if (!data.contentEquals(other.data)) return false 13 | 14 | return true 15 | } 16 | 17 | override fun hashCode(): Int { 18 | var result = uuid.hashCode() 19 | result = 31 * result + data.contentHashCode() 20 | return result 21 | } 22 | } 23 | 24 | private var mQueue: MutableList = mutableListOf() 25 | 26 | fun add(newItem: QueueItem) { 27 | if (newItem.overwrite && mQueue.find { it.uuid == newItem.uuid } != null) { 28 | mQueue.removeAll { it.uuid == newItem.uuid } 29 | } 30 | 31 | mQueue.add(newItem) 32 | } 33 | 34 | fun pop(): QueueItem { 35 | return mQueue.removeAt(0) 36 | } 37 | 38 | val size get() = mQueue.size 39 | 40 | fun clear() { 41 | mQueue.clear() 42 | } 43 | } -------------------------------------------------------------------------------- /android-app/app/src/main/java/com/maisonsmd/catdrive/lib/Intents.kt: -------------------------------------------------------------------------------- 1 | package com.maisonsmd.catdrive.lib 2 | 3 | import com.maisonsmd.catdrive.BuildConfig 4 | 5 | class Intents { 6 | companion object { 7 | private const val APP_ID = BuildConfig.APPLICATION_ID 8 | 9 | const val ENABLE_SERVICES = "${APP_ID}.intent.ENABLE_SERVICES" 10 | const val DISABLE_SERVICES = "${APP_ID}.intent.DISABLE_SERVICES" 11 | const val BIND_LOCAL_SERVICE = "${APP_ID}.intent.LOCAL_BIND" 12 | const val BACKGROUND_SERVICE_STATUS = "${APP_ID}.intent.SERVICE_RUNNING" 13 | 14 | const val DISCONNECT_DEVICE = "${APP_ID}.intent.DISCONNECT_DEVICE" 15 | const val CONNECT_DEVICE = "${APP_ID}.intent.CONNECT_DEVICE" 16 | const val CONNECTION_UPDATE = "${APP_ID}.intent.CONNECTION_UPDATE" 17 | 18 | const val NAVIGATION_UPDATE = "${APP_ID}.intent.NAVIGATION_UPDATE" 19 | const val GPS_UPDATE = "${APP_ID}.intent.GPS_UPDATE" 20 | 21 | const val OPEN_NOTIFICATION_LISTENER_SETTINGS = 22 | "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS" 23 | 24 | const val ACTION_GATT_CONNECTED = "com.maisonsmd.bluetooth.le.ACTION_GATT_CONNECTED" 25 | const val ACTION_GATT_DISCONNECTED = "com.maisonsmd.bluetooth.le.ACTION_GATT_DISCONNECTED" 26 | const val ACTION_GATT_SERVICES_DISCOVERED = 27 | "com.maisonsmd.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED" 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /esp32/keyval.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct KeyValue { 4 | String key; 5 | String value; 6 | }; 7 | 8 | struct KvParseResult { 9 | std::vector pairs; 10 | 11 | String getOrDefault(const String& key, const String& valueIfNull = "") const { 12 | const auto it = std::find_if(pairs.begin(), pairs.end(), [&key](const KeyValue& kv) { return kv.key == key; }); 13 | 14 | if (it != pairs.end()) 15 | return it->value; 16 | 17 | return valueIfNull; 18 | } 19 | 20 | bool contains(const String& key) const { 21 | return std::find_if(pairs.begin(), pairs.end(), [&key](const KeyValue& kv) { return kv.key == key; }) != pairs.end(); 22 | } 23 | }; 24 | 25 | std::vector splitString(const String& input, const String& delimiter) { 26 | std::vector result; 27 | 28 | int start = 0; 29 | int end = input.indexOf(delimiter); 30 | 31 | while (end != -1) { 32 | result.push_back(input.substring(start, end)); 33 | start = end + delimiter.length(); 34 | end = input.indexOf(delimiter, start); 35 | } 36 | 37 | result.push_back(input.substring(start)); 38 | 39 | return result; 40 | } 41 | 42 | KvParseResult kvParseMultiline(const String& input) { 43 | KvParseResult result{}; 44 | 45 | const auto lines = splitString(input, "\n"); 46 | 47 | for (const auto& line : lines) { 48 | const auto parts = splitString(line, "="); 49 | 50 | if (parts.size() < 2) 51 | continue; 52 | 53 | result.pairs.push_back({parts[0], parts[1]}); 54 | } 55 | 56 | return result; 57 | } -------------------------------------------------------------------------------- /android-app/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.defaults.buildfeatures.buildconfig=true 25 | android.nonFinalResIds=false -------------------------------------------------------------------------------- /android-app/app/src/main/res/layout/activity_ble_selection.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |