A Cross Platform Tail Company gear control App
- Supports Android and IOS
- Firmware Updates
- The same actions/moves from Crumpet
- Triggers for walking, shaking and other gestures
- Custom Actions
- Custom Sound Effects
- Joystick for manually moving gear
- Dark Mode (Based on system settings)
- Color Themes
- Background mode on IOS
- Tail Blog
- Tablet support
- Sound Actions
Small or large, feel free to leave suggestions for new features, or changes to existing features.
Note
As long as the suggestion is not related to a specific day or event
- @darkgrue for helping me with gear firmware behavior & developing the firmware the Gear uses
- @MasterTailer for providing useful feedback and suggestions, and creating the gear this app controls
- @ToeiRei for inspiring me to use more privacy-preserving infrastructure like plausible, and for managing & assisting in the translation of this app
- @leinir for creating the Crumpet Android app
- The Tail Company Telegram Channel for motivating me over time.
Tip
Follow the instructions here to set up a Flutter environment
- 8GB of ram
- Dual Core CPU
- 80gb of unused storage (Required for Android Studio & XCode, Sources for IOS & Android apps, etc)
- 2018 or newer Apple Mac (For IOS) Older macs with OpenCore-Legacy Patcher may work
- Windows 11 Or MacOS Sonoma (Have not tried developing on linux)
- Android Studio
- Java
17
(For Android) - XCode
15
(For IOS) with IOS & CLI Tools installed - CocoaPods (For IOS)
- Flutter SDK
3.22.0
- Bash (Windows & Linux) or ZSH (MacOS)
- Git
- Ruby for FastLane
To update EN localization strings, the file translation_string_definitions.dart
needs to be updated.
String message() => Intl.message('Displayed Message', name: 'message', desc: 'A description of the string and where it is used');
The Displayed Message
is the string that appears in the UI.
The name
is the variable name. This must match the variable name used such as message()
but without the ()
.
The desc
is a description of the string for use by translators.
When translation_string_definitions.dart
is updated, the job localization_strings_update.yml
updates the generated localization file messages_en.arb
which makes the strings available to Weblate.
When non EN translations are updated in Weblate, A pull request will automatically open with the changes. This may take a few minutes.
Tip
A pre-made build script exists at scripts/build,sh
Important
These commands must be run before building or running.
# Install and enable required tools
dart pub global activate intl_translation
flutter pub get # Downloads Dependencies
dart run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/Frontend/translation_string_definitions.dart lib/l10n/*.arb
flutter pub run build_runner build --delete-conflicting-outputs # Generates .g files
Note
To generate the base EN localization file, run
dart pub global activate intl_translation
dart run intl_translation:extract_to_arb --locale=en --output-file='./lib/l10n/messages_en.arb' ./lib/Frontend/translation_string_definitions.dart
To build localization files, run
dart pub global activate intl_translation
dart run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/Frontend/translation_string_definitions.dart lib/l10n/*.arb
To build generated .g
files, run
flutter pub run build_runner build --delete-conflicting-outputs
Tip
If you get a flutter version error, run
flutter Upgrade
if you get an error similar to Error: Couldn't resolve the package 'flutter_gen' in 'package:flutter_gen/gen_l10n/app_localizations.dart'
run
flutter pub get
if you get an error during riverpod generator such as RangeError (index): Invalid value: Not in inclusive range 0..20491: 77535
try
flutter clean
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
flutter pub get # fixes app_localizations error
The xcode project is configured to to use automatic code signing for develop builds. make sure to open xcode and sign in with your developer account. Check the Signing and Capabilities page of Runner to verify the signing configuration is correct. Release builds are set up to rely on Fastlane to provide the signing certificate.
cd ios
rm Podfile.lock # Handles a CocoaPods error about version management
cd ..
flutter build ipa --debug
Tip
MacOS may display multiple permission prompts such as File Access, KeyChain Access, Device Access (iphone) & Controlling XCode. Accept them for the build to complete. These only need to be accepted once
If you receive an error that IOS is not installed in XCode during build.
- Go to XCode (In Top menu bar) -> Settings
- Click Platforms
- Click on IOS Simulator
- Click the small
-
icon near the bottom of the settings window - Click Delete
If CocoaPods returns a version error, delete ios/Podfile.lock
If Permissions are not working, revert any changes you may have done to ios/Podfile
Android looks for a key.properties file. If this file is missing the apk is not signed. A keystore jks is also expected at the location specified in the keystore.properties file. The Sentry plugin will run on release builds and expects the sentry env variables to be set
# Build APK
flutter build apk --debug
# build AppBundle
flutter build appbundle --debug
App packages can be found in build/app/output
Place the new icon in Assets then update image_path
in the icons_launcher
section in pubspec.yml
dart pub global activate icons_launcher # downloads the utility
dart pub global run icons_launcher:create
Make any changes to the 'flutter_native_splash' section in pubspec.yml
dart run flutter_native_splash:create
Tip
Once this command is run, you may need to add the following to styles.xml to have transparent status and navigation bars on the splash screen on android.
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
To run tests, simply run the command
flutter test
In order to filter sentry logs from testing and production, the 'Environment' is guessed
- debug: if the app was built in debug mode or if the app is in an emulator
- staging: if the app is on testflight or in firebase test lab
- production: everything else
Fastlane is a tool to automatically upload apps to the Apple App Store and Google Play Store. Inside the IOS and Android folders is a fastlane folder. Inside is the FastFile which contains the upload config. Secrets are JSON files passed through repository secrets. The script fastlane.sh selects the fastlane folder to use and begins the upload.
Some of these values aren't actually secret and can be shared. Specifically the sentry ones
Name | Example Value | How to get | Uses |
---|---|---|---|
SENTRY_AUTH_TOKEN | sntrys_eyJpYXQiOjE3MTYyNTky... | Go to Sentry -> Settings -> Auth Token | Authenticate with sentry to upload symbols |
SENTRY_ORG | Sentry | Listed at the top left of sentry when logged in | Which org to upload symbols to |
SENTRY_PROJECT | tail_app | Whatever the project is named in sentry | Which project to upload symbols to |
SENTRY_URL | https://sentry.io/ | The url to the sentry instance | Which instance to upload symbols to |
SENTRY_DSN | https://sdfghjssdh.ingest.de.sentry.io/ | The dsn for the sentry project | Which instance to upload errors to |
FASTLANE_GITHUB | JeqGFIV1yb7emBFLkBk/dA== | echo -n your_github_username:your_personal_access_token | base64 -w 0 | Store certificates for fastlane match |
APPLE | {"key_id": "D383SF739", "issuer_id": "6053b7fe-68a8-4acb-89be-165aa6465141", "key": "-----BEGIN PRIVATE KEY-----MIGTAgEAMB----END PRIVATE KEY-----", "in_house": false } | Json file of apple credentials https://docs.fastlane.tools/app-store-connect-api/ | Authenticate with Apple to upload to TestFlight |
FASTLANE_PATCH_PASSWORD | hunter2 | Make a password | Encrypt match certificates |
ANDROID_KEY_PROPERTIES | storePassword=hunter2 keyPassword=hunter2 keyAlias=upload storeFile=key.jks |
generate an android signing certificate and fill out key.example.properties | sign apks |
ANDROID_KEY_JKS | sdfsfasdFSDgjklsgklsjdfASGHSDLGHJFSD= | cat AndroidKeystoreCodel1417.jks | base64 -w 0 | base64 form of the jks file |
GOOGLE_SECRETS | {"type": "service_account", | Json file of google credentials https://docs.fastlane.tools/actions/upload_to_play_store/ | Authenticate to google to upload builds |
CODECOV_TOKEN | jlgsfjklsdhjklvsdfjklsdfjklsdfjkjklnsdf | generated by codecov | Code coverage metrics |
A github app which allows Sentry to authenticate with Github and this repo. It allows Source Code stack trace linking and Creating issues from the Sentry UI.
A Webhook to notify Weblate that code was pushed to this repo.
A SSH key is installed in my account which allows Weblate to push translation changes to the repo.
The Codecov integration is set up to track test coverage. The job testing.yml runs the unit tests for every push and pull request, and uploads the coverage report to codecov. These reports are visible on pull requests as comments.
- Gear console
- Manual OTA Updates
- Advanced state control for gear
- Access to app logs
Crime
Secret
To enter the in-app Developer Mode, follow these instructions
- Go to
More
- Long press
Source Code
button, enter the following code π¦ππ¦¦π¦
To Turn off Developer Mode
- go to More -> Settings -> Developer Mode
- Turn off
showDebugging
These services are self-hosted in a mini-pc on my tv stand in my apartment
Services are hosted in a Proxmox based machine using unprivileged LXC containers. These containers are based on Ubuntu Server and have unattended upgrades enabled. This machine doesn't use port forwarding, but instead uses Cloudflare Tunnel. Services go down daily at 6:00 AM UTC for offline backups. This Can take up to an hour.
This app supports updating some config values remotely.
These values are located in dynamic_config.json
.
The file is included in builds and updated after app launch, so changes may not appear immediately.
Json Key | Description |
---|---|
sentryProfiles | sets options.profilesSampleRate for sentry |
sentryTraces | sets options.tracesSampleRate for sentry |
Apple App Store connect Codecov Code Coverage
UUIDs were generated using https://www.uuidgenerator.net/version4