diff --git a/wear_companion/.gitignore b/wear_companion/.gitignore new file mode 100644 index 000000000..3820a95c6 --- /dev/null +++ b/wear_companion/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/wear_companion/.metadata b/wear_companion/.metadata new file mode 100644 index 000000000..76fd1d085 --- /dev/null +++ b/wear_companion/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "05db9689081f091050f01aed79f04dce0c750154" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 05db9689081f091050f01aed79f04dce0c750154 + base_revision: 05db9689081f091050f01aed79f04dce0c750154 + - platform: android + create_revision: 05db9689081f091050f01aed79f04dce0c750154 + base_revision: 05db9689081f091050f01aed79f04dce0c750154 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/wear_companion/README.md b/wear_companion/README.md new file mode 100644 index 000000000..fba3feaf9 --- /dev/null +++ b/wear_companion/README.md @@ -0,0 +1,16 @@ +# wear_companion + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/wear_companion/analysis_options.yaml b/wear_companion/analysis_options.yaml new file mode 100644 index 000000000..0d2902135 --- /dev/null +++ b/wear_companion/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/wear_companion/android/.gitignore b/wear_companion/android/.gitignore new file mode 100644 index 000000000..be3943c96 --- /dev/null +++ b/wear_companion/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/wear_companion/android/app/build.gradle.kts b/wear_companion/android/app/build.gradle.kts new file mode 100644 index 000000000..2a5d40751 --- /dev/null +++ b/wear_companion/android/app/build.gradle.kts @@ -0,0 +1,43 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.maxdelissen.mobileraker.wear_companion" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + applicationId = "com.maxdelissen.mobileraker.wear_companion" + + // Set the minSdk to 30 to only support wearos 3.0 and higher. 2.0 is obsolete and more difficult to maintain. + minSdk = 30 + targetSdk = 33 + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/wear_companion/android/app/src/debug/AndroidManifest.xml b/wear_companion/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..399f6981d --- /dev/null +++ b/wear_companion/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/wear_companion/android/app/src/main/AndroidManifest.xml b/wear_companion/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..894099479 --- /dev/null +++ b/wear_companion/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wear_companion/android/app/src/main/kotlin/com/maxdelissen/mobileraker/wear_companion/MainActivity.kt b/wear_companion/android/app/src/main/kotlin/com/maxdelissen/mobileraker/wear_companion/MainActivity.kt new file mode 100644 index 000000000..7b3263cef --- /dev/null +++ b/wear_companion/android/app/src/main/kotlin/com/maxdelissen/mobileraker/wear_companion/MainActivity.kt @@ -0,0 +1,5 @@ +package com.maxdelissen.mobileraker.wear_companion + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/wear_companion/android/app/src/main/res/drawable-v21/launch_background.xml b/wear_companion/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/wear_companion/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/wear_companion/android/app/src/main/res/drawable/launch_background.xml b/wear_companion/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 000000000..304732f88 --- /dev/null +++ b/wear_companion/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/wear_companion/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/wear_companion/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..db77bb4b7 Binary files /dev/null and b/wear_companion/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/wear_companion/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/wear_companion/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..17987b79b Binary files /dev/null and b/wear_companion/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/wear_companion/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/wear_companion/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..09d439148 Binary files /dev/null and b/wear_companion/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/wear_companion/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/wear_companion/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..d5f1c8d34 Binary files /dev/null and b/wear_companion/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/wear_companion/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/wear_companion/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/wear_companion/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/wear_companion/android/app/src/main/res/values-night/styles.xml b/wear_companion/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..06952be74 --- /dev/null +++ b/wear_companion/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/wear_companion/android/app/src/main/res/values/styles.xml b/wear_companion/android/app/src/main/res/values/styles.xml new file mode 100644 index 000000000..cb1ef8805 --- /dev/null +++ b/wear_companion/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/wear_companion/android/app/src/profile/AndroidManifest.xml b/wear_companion/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..399f6981d --- /dev/null +++ b/wear_companion/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/wear_companion/android/build.gradle.kts b/wear_companion/android/build.gradle.kts new file mode 100644 index 000000000..dbee657bb --- /dev/null +++ b/wear_companion/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/wear_companion/android/gradle.properties b/wear_companion/android/gradle.properties new file mode 100644 index 000000000..f018a6181 --- /dev/null +++ b/wear_companion/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/wear_companion/android/gradle/wrapper/gradle-wrapper.properties b/wear_companion/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..ac3b47926 --- /dev/null +++ b/wear_companion/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip diff --git a/wear_companion/android/settings.gradle.kts b/wear_companion/android/settings.gradle.kts new file mode 100644 index 000000000..fb605bc84 --- /dev/null +++ b/wear_companion/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.9.1" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/wear_companion/lib/main.dart b/wear_companion/lib/main.dart new file mode 100644 index 000000000..1fbdbdcc2 --- /dev/null +++ b/wear_companion/lib/main.dart @@ -0,0 +1,79 @@ +// lib/main.dart +import 'package:flutter/material.dart'; + +/// Minimal, watch-friendly "Hello World". +/// - Uses a dark background and white text for readability on most watch faces. +/// - Uses MediaQuery padding to avoid bezel / system insets on round screens. +/// - Keeps font sizes conservative so text doesn't get clipped on small faces. +void main() => runApp(const WearHelloApp()); + +class WearHelloApp extends StatelessWidget { + const WearHelloApp({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + // Using a dark theme globally keeps colors consistent and makes it easy + // to change later (e.g., switch to ambient-mode-specific style). + return MaterialApp( + title: 'Mobileraker (Wear)', + debugShowCheckedModeBanner: false, + theme: ThemeData( + // Choose dark so default text/icon colors contrast with a dark background. + brightness: Brightness.dark, + scaffoldBackgroundColor: Colors.black, // explicit black background + textTheme: const TextTheme( + // headline6 is used below; set a comfortable font size for watches. + titleLarge: TextStyle(fontSize: 18.0, color: Colors.white), + bodySmall: TextStyle(fontSize: 12.0, color: Colors.white70), + ), + ), + home: const HelloScreen(), + ); + } +} + +class HelloScreen extends StatelessWidget { + const HelloScreen({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + // Use MediaQuery padding to respect round devices' inset (bezel). + final padding = MediaQuery.of(context).viewPadding; + final horizontalInset = (padding.left + padding.right) / 2; + final textStyle = Theme.of(context).textTheme.titleLarge; + + return Scaffold( + // Keep the Scaffold simple; black background is set in theme. + body: Padding( + // horizontal inset keeps contents away from curved edges. + padding: EdgeInsets.fromLTRB( + 8.0 + horizontalInset, + 8.0 + padding.top, + 8.0 + horizontalInset, + 8.0 + padding.bottom, + ), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Short, centered headline. Shortening helps on narrow round faces. + Text( + 'Hello, Mobileraker!', + style: textStyle, + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 8), + Text( + 'Watch companion running!', + style: Theme.of(context).textTheme.bodySmall, + textAlign: TextAlign.center, + ), + ], + ), + ), + ), + ); + } +} diff --git a/wear_companion/pubspec.yaml b/wear_companion/pubspec.yaml new file mode 100644 index 000000000..bd75d10c4 --- /dev/null +++ b/wear_companion/pubspec.yaml @@ -0,0 +1,89 @@ +name: wear_companion +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.9.0 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/wear_companion/test/widget_test.dart b/wear_companion/test/widget_test.dart new file mode 100644 index 000000000..f98cec26e --- /dev/null +++ b/wear_companion/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:wear_companion/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +}