1.2.3
This commit is contained in:
commit
3f5f894e86
49
CHANGELOG.md
Normal file
49
CHANGELOG.md
Normal file
@ -0,0 +1,49 @@
|
||||
# 1.2.3
|
||||
|
||||
- Replaces deprecated native lifecycle observer methods (by [@Crdzbird](https://github.com/Crdzbird) in [#6](https://github.com/Rexios80/wear_plus/pull/6))
|
||||
|
||||
# 1.2.2
|
||||
|
||||
- Fixes build issue
|
||||
|
||||
# 1.2.1
|
||||
|
||||
- Updates proguard rules for R8
|
||||
|
||||
# 1.2.0
|
||||
|
||||
- First release of `wear_plus`. Replace any references to the `wear` plugin with `wear_plus` to switch.
|
||||
- `InheritedShape` is now private. This would be a breaking change if this wasn't the first release of a new plugin.
|
||||
|
||||
# 1.1.0
|
||||
|
||||
- Fix issue with non-windows builds breaking.
|
||||
|
||||
# 1.0.0
|
||||
|
||||
- Null Safety Migration (Finally!)
|
||||
- Thanks to Rexios and Peter Ullrich.
|
||||
- Min Dart 2.12 / Flutter 2.5
|
||||
- Updated native component versions:
|
||||
- Gradle 6.5
|
||||
- Android Gradle Plugin 4.1.0
|
||||
- Android compileSdkVersion 31
|
||||
- AndroidX Wear 1.2.0
|
||||
- Kotlin 1.5.10
|
||||
- Removed `jcenter()` repo requirement.
|
||||
|
||||
# 0.1.1
|
||||
|
||||
- Fix Kotlin/Android compileOnly dep on com.google.android.wearable:wearable.
|
||||
|
||||
# 0.1.0
|
||||
|
||||
- Updated to AndroidX and Android embedding v2.
|
||||
- Renamed `Shape` is now `WearShape`.
|
||||
- Renamed `Mode` is now `WearMode`.
|
||||
- Deprecated `InheritedShape`.
|
||||
_Add `WatchShape` instead and use `WatchShape.of(context)` to get the shape value._
|
||||
|
||||
# 0.0.1
|
||||
|
||||
- Initial release
|
27
LICENSE
Normal file
27
LICENSE
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Flutter Wear Plugin
|
||||
|
||||
A plugin that offers Flutter support for Wear OS by Google (Android Wear).
|
||||
|
||||
__To use this plugin you must set your `minSdkVersion` to `23`.__
|
||||
|
||||
|
||||
# Tutorial
|
||||
|
||||
https://medium.com/flutter-community/flutter-building-wearos-app-fedf0f06d1b4
|
||||
|
||||
|
||||
# Widgets
|
||||
|
||||
There currently three widgets provided by the plugin:
|
||||
|
||||
* WatchShape: determines whether the watch is square or round.
|
||||
* AmbientMode: builder that provides what mode the watch is in. The widget will rebuild whenever the watch changes mode.
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
If you are creating a standalone watch app, add the following to your manifest:
|
||||
|
||||
```xml
|
||||
<application>
|
||||
<meta-data
|
||||
android:name="com.google.android.wearable.standalone"
|
||||
android:value="true"
|
||||
/>
|
||||
</application>
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```dart
|
||||
class WatchScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return WatchShape(
|
||||
builder: (BuildContext context, WearShape shape, Widget? child) {
|
||||
return AmbientMode(
|
||||
builder: (context, mode, child) {
|
||||
return mode == Mode.active ? ActiveWatchFace() : AmbientWatchFace();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
1
analysis_options.yaml
Normal file
1
analysis_options.yaml
Normal file
@ -0,0 +1 @@
|
||||
include: package:rexios_lints/flutter/package_extra.yaml
|
56
android/build.gradle
Normal file
56
android/build.gradle
Normal file
@ -0,0 +1,56 @@
|
||||
group 'dev.rexios.wear_plus'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.7.20'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
//apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
lintOptions {
|
||||
disable 'InvalidPackage'
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '11'
|
||||
}
|
||||
namespace "dev.rexios.wear_plus"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation 'androidx.wear:wear:1.3.0'
|
||||
implementation 'com.google.android.support:wearable:2.9.0'
|
||||
compileOnly 'com.google.android.wearable:wearable:2.9.0'
|
||||
}
|
2
android/gradle.properties
Normal file
2
android/gradle.properties
Normal file
@ -0,0 +1,2 @@
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -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-6.5-all.zip
|
3
android/proguard-rules.pro
vendored
Normal file
3
android/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
-keep class dev.rexios.wear_plus.** { *; }
|
||||
-dontwarn com.google.android.wearable.compat.WearableActivityController$AmbientCallback
|
||||
-dontwarn com.google.android.wearable.compat.WearableActivityController
|
1
android/settings.gradle
Normal file
1
android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'wear'
|
11
android/src/main/AndroidManifest.xml
Normal file
11
android/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.rexios.wear_plus"
|
||||
>
|
||||
|
||||
<!-- Flags the app as a Wear app -->
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
||||
<!-- Required for ambient mode support -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
</manifest>
|
147
android/src/main/kotlin/dev/rexios/wear_plus/WearPlugin.kt
Normal file
147
android/src/main/kotlin/dev/rexios/wear_plus/WearPlugin.kt
Normal file
@ -0,0 +1,147 @@
|
||||
package dev.rexios.wear_plus
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.google.android.wearable.compat.WearableActivityController
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
|
||||
import io.flutter.embedding.engine.plugins.lifecycle.HiddenLifecycleReference
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.plugin.common.MethodChannel.Result
|
||||
|
||||
|
||||
class WearPlugin : FlutterPlugin, ActivityAware, MethodCallHandler, LifecycleEventObserver {
|
||||
private var mAmbientCallback = WearableAmbientCallback()
|
||||
private var mMethodChannel: MethodChannel? = null
|
||||
private var mActivityBinding: ActivityPluginBinding? = null
|
||||
private var mAmbientController: WearableActivityController? = null
|
||||
|
||||
companion object {
|
||||
const val TAG = "WearPlugin"
|
||||
const val BURN_IN_PROTECTION = WearableActivityController.EXTRA_BURN_IN_PROTECTION
|
||||
const val LOW_BIT_AMBIENT = WearableActivityController.EXTRA_LOWBIT_AMBIENT
|
||||
}
|
||||
|
||||
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
mMethodChannel = MethodChannel(binding.binaryMessenger, "wear")
|
||||
mMethodChannel!!.setMethodCallHandler(this)
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
|
||||
mMethodChannel?.setMethodCallHandler(this)
|
||||
mMethodChannel = null
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
attachAmbientController(binding)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivityForConfigChanges() {
|
||||
detachAmbientController()
|
||||
}
|
||||
|
||||
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
|
||||
attachAmbientController(binding)
|
||||
}
|
||||
|
||||
override fun onDetachedFromActivity() {
|
||||
detachAmbientController()
|
||||
}
|
||||
|
||||
private fun attachAmbientController(binding: ActivityPluginBinding) {
|
||||
mActivityBinding = binding
|
||||
mAmbientController = WearableActivityController(TAG, binding.activity, mAmbientCallback)
|
||||
mAmbientController?.setAmbientEnabled()
|
||||
val reference = (binding.lifecycle as HiddenLifecycleReference)
|
||||
reference.lifecycle.addObserver(this)
|
||||
}
|
||||
|
||||
private fun detachAmbientController() {
|
||||
mActivityBinding?.let {
|
||||
val reference = (it.lifecycle as HiddenLifecycleReference)
|
||||
reference.lifecycle.removeObserver(this)
|
||||
}
|
||||
mActivityBinding = null
|
||||
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: Result) {
|
||||
when (call.method) {
|
||||
"getShape" -> {
|
||||
val activity = mActivityBinding?.activity
|
||||
when {
|
||||
activity == null -> {
|
||||
result.error("no-activity", "No android activity available.", null)
|
||||
}
|
||||
activity.resources.configuration.isScreenRound -> {
|
||||
result.success("round")
|
||||
}
|
||||
else -> {
|
||||
result.success("square")
|
||||
}
|
||||
}
|
||||
}
|
||||
"isAmbient" -> {
|
||||
result.success(mAmbientController?.isAmbient ?: false)
|
||||
}
|
||||
"setAutoResumeEnabled" -> {
|
||||
val enabled = call.argument<Boolean>("enabled")
|
||||
if (mAmbientController == null || enabled == null) {
|
||||
result.error("not-ready", "Ambient mode controller not ready", null)
|
||||
} else {
|
||||
mAmbientController!!.setAutoResumeEnabled(enabled)
|
||||
result.success(null)
|
||||
}
|
||||
}
|
||||
"setAmbientOffloadEnabled" -> {
|
||||
val enabled = call.argument<Boolean>("enabled")
|
||||
if (mAmbientController == null || enabled == null) {
|
||||
result.error("not-ready", "Ambient mode controller not ready", null)
|
||||
} else {
|
||||
mAmbientController!!.setAmbientOffloadEnabled(enabled)
|
||||
result.success(null)
|
||||
}
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
inner class WearableAmbientCallback : WearableActivityController.AmbientCallback() {
|
||||
override fun onEnterAmbient(ambientDetails: Bundle) {
|
||||
val burnInProtection = ambientDetails.getBoolean(BURN_IN_PROTECTION, false)
|
||||
val lowBitAmbient = ambientDetails.getBoolean(LOW_BIT_AMBIENT, false)
|
||||
mMethodChannel?.invokeMethod("onEnterAmbient", mapOf(
|
||||
"burnInProtection" to burnInProtection,
|
||||
"lowBitAmbient" to lowBitAmbient
|
||||
))
|
||||
}
|
||||
|
||||
override fun onExitAmbient() {
|
||||
mMethodChannel?.invokeMethod("onExitAmbient", null)
|
||||
}
|
||||
|
||||
override fun onUpdateAmbient() {
|
||||
mMethodChannel?.invokeMethod("onUpdateAmbient", null)
|
||||
}
|
||||
|
||||
override fun onInvalidateAmbientOffload() {
|
||||
mMethodChannel?.invokeMethod("onInvalidateAmbientOffload", null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_CREATE -> mAmbientController?.onCreate()
|
||||
Lifecycle.Event.ON_RESUME -> mAmbientController?.onResume()
|
||||
Lifecycle.Event.ON_PAUSE -> mAmbientController?.onPause()
|
||||
Lifecycle.Event.ON_STOP -> mAmbientController?.onStop()
|
||||
Lifecycle.Event.ON_DESTROY -> mAmbientController?.onDestroy()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
129
lib/src/ambient_widget.dart
Normal file
129
lib/src/ambient_widget.dart
Normal file
@ -0,0 +1,129 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:wear_plus/src/wear.dart';
|
||||
|
||||
/// Ambient modes for a Wear device
|
||||
enum WearMode {
|
||||
/// The screen is active
|
||||
active,
|
||||
|
||||
/// The screen is in ambient mode
|
||||
ambient,
|
||||
}
|
||||
|
||||
/// Builds a child for [AmbientMode]
|
||||
typedef AmbientModeWidgetBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
WearMode mode,
|
||||
Widget? child,
|
||||
);
|
||||
|
||||
/// Widget that listens for when a Wear device enters full power or ambient mode,
|
||||
/// and provides this in a builder. It optionally takes an [onUpdate] function that's
|
||||
/// called every time the wear device triggers an ambient update request.
|
||||
@immutable
|
||||
class AmbientMode extends StatefulWidget {
|
||||
/// Constructor
|
||||
const AmbientMode({
|
||||
super.key,
|
||||
required this.builder,
|
||||
this.child,
|
||||
this.onUpdate,
|
||||
});
|
||||
|
||||
/// Built when the mode changes
|
||||
final AmbientModeWidgetBuilder builder;
|
||||
|
||||
/// Optional child that will not get rebuilt when the mode changes
|
||||
final Widget? child;
|
||||
|
||||
/// Called each time the the wear device triggers an ambient update request.
|
||||
final VoidCallback? onUpdate;
|
||||
|
||||
/// Get current [WearMode].
|
||||
static WearMode wearModeOf(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<_InheritedAmbientMode>()!
|
||||
.mode;
|
||||
}
|
||||
|
||||
/// Get current [AmbientDetails].
|
||||
static AmbientDetails ambientDetailsOf(BuildContext context) {
|
||||
return context
|
||||
.dependOnInheritedWidgetOfExactType<_InheritedAmbientMode>()!
|
||||
.details;
|
||||
}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _AmbientModeState();
|
||||
}
|
||||
|
||||
class _AmbientModeState extends State<AmbientMode> with AmbientCallback {
|
||||
var _ambientMode = WearMode.active;
|
||||
final _ambientDetails = const AmbientDetails(false, false);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Wear.instance.registerAmbientCallback(this);
|
||||
_initState();
|
||||
}
|
||||
|
||||
void _initState() async {
|
||||
final isAmbient = await Wear.instance.isAmbient();
|
||||
_updateMode(isAmbient);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
Wear.instance.unregisterAmbientCallback(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _InheritedAmbientMode(
|
||||
mode: _ambientMode,
|
||||
details: _ambientDetails,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return widget.builder(context, _ambientMode, widget.child);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateMode(bool isAmbient) {
|
||||
if (!mounted) return;
|
||||
setState(
|
||||
() => _ambientMode = isAmbient ? WearMode.ambient : WearMode.active,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onEnterAmbient(AmbientDetails ambientDetails) => _updateMode(true);
|
||||
|
||||
@override
|
||||
void onExitAmbient() => _updateMode(false);
|
||||
|
||||
@override
|
||||
void onUpdateAmbient() {
|
||||
_updateMode(true);
|
||||
widget.onUpdate?.call();
|
||||
}
|
||||
}
|
||||
|
||||
class _InheritedAmbientMode extends InheritedWidget {
|
||||
const _InheritedAmbientMode({
|
||||
required this.mode,
|
||||
required this.details,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final WearMode mode;
|
||||
final AmbientDetails details;
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(_InheritedAmbientMode old) {
|
||||
return mode != old.mode || details != old.details;
|
||||
}
|
||||
}
|
94
lib/src/shape_widget.dart
Normal file
94
lib/src/shape_widget.dart
Normal file
@ -0,0 +1,94 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:wear_plus/src/wear.dart';
|
||||
|
||||
/// Shape of a Wear device
|
||||
enum WearShape {
|
||||
/// The display is square
|
||||
square,
|
||||
|
||||
/// The display is round
|
||||
round,
|
||||
}
|
||||
|
||||
/// Builds a child for a [WatchShape]
|
||||
typedef WatchShapeBuilder = Widget Function(
|
||||
BuildContext context,
|
||||
WearShape shape,
|
||||
Widget? child,
|
||||
);
|
||||
|
||||
/// Builder widget for watch shapes
|
||||
@immutable
|
||||
class WatchShape extends StatefulWidget {
|
||||
/// Constructor
|
||||
const WatchShape({
|
||||
super.key,
|
||||
required this.builder,
|
||||
this.child,
|
||||
});
|
||||
|
||||
/// Built when the shape changes
|
||||
final WatchShapeBuilder builder;
|
||||
|
||||
/// Optional child that will not get rebuilt when the shape changes
|
||||
final Widget? child;
|
||||
|
||||
/// Call [WatchShape.of(context)] to retrieve the shape further down
|
||||
/// in the widget hierarchy.
|
||||
static WearShape of(BuildContext context) {
|
||||
return _InheritedShape.of(context).shape;
|
||||
}
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _WatchShapeState();
|
||||
}
|
||||
|
||||
class _WatchShapeState extends State<WatchShape> {
|
||||
// Default to round until the platform returns the shape
|
||||
// round being the most common form factor for WearOS
|
||||
var _shape = WearShape.round;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initState();
|
||||
}
|
||||
|
||||
void _initState() async {
|
||||
final shape = await Wear.instance.getShape();
|
||||
if (!mounted) return;
|
||||
setState(
|
||||
() => _shape = (shape == 'round' ? WearShape.round : WearShape.square),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return _InheritedShape(
|
||||
shape: _shape,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return widget.builder(context, _shape, widget.child);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InheritedShape extends InheritedWidget {
|
||||
/// Constructor
|
||||
const _InheritedShape({
|
||||
required this.shape,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
final WearShape shape;
|
||||
|
||||
static _InheritedShape of(BuildContext context) {
|
||||
return context.dependOnInheritedWidgetOfExactType<_InheritedShape>()!;
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(_InheritedShape oldWidget) =>
|
||||
shape != oldWidget.shape;
|
||||
}
|
152
lib/src/wear.dart
Normal file
152
lib/src/wear.dart
Normal file
@ -0,0 +1,152 @@
|
||||
import 'package:flutter/foundation.dart' show debugPrint;
|
||||
import 'package:flutter/services.dart'
|
||||
show MethodChannel, MethodCall, PlatformException;
|
||||
|
||||
/// Provides access to Wearable features
|
||||
class Wear {
|
||||
static const _channel = MethodChannel('wear');
|
||||
|
||||
/// Get the [Wear] instance
|
||||
factory Wear() => instance;
|
||||
|
||||
/// Access to the singleton instance
|
||||
static final instance = Wear._();
|
||||
|
||||
Wear._() {
|
||||
_channel.setMethodCallHandler(_onMethodCallHandler);
|
||||
}
|
||||
|
||||
final _ambientCallbacks = <AmbientCallback>[];
|
||||
|
||||
/// Register callback for ambient notifications
|
||||
void registerAmbientCallback(AmbientCallback callback) {
|
||||
_ambientCallbacks.add(callback);
|
||||
}
|
||||
|
||||
/// Unregister callback for ambient notifications
|
||||
void unregisterAmbientCallback(AmbientCallback callback) {
|
||||
_ambientCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
Future<dynamic> _onMethodCallHandler(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case 'onEnterAmbient':
|
||||
final args = (call.arguments as Map).cast<String, bool>();
|
||||
final details =
|
||||
AmbientDetails(args['burnInProtection']!, args['lowBitAmbient']!);
|
||||
_notifyAmbientCallbacks((callback) => callback.onEnterAmbient(details));
|
||||
case 'onExitAmbient':
|
||||
_notifyAmbientCallbacks((callback) => callback.onExitAmbient());
|
||||
case 'onUpdateAmbient':
|
||||
_notifyAmbientCallbacks((callback) => callback.onUpdateAmbient());
|
||||
case 'onInvalidateAmbientOffload':
|
||||
_notifyAmbientCallbacks(
|
||||
(callback) => callback.onInvalidateAmbientOffload(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _notifyAmbientCallbacks(Function(AmbientCallback callback) fn) {
|
||||
final callbacks = List<AmbientCallback>.from(_ambientCallbacks);
|
||||
for (final callback in callbacks) {
|
||||
try {
|
||||
fn(callback);
|
||||
} catch (e, st) {
|
||||
debugPrint('Failed callback: $callback\n$e\n$st');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches the shape of the watch face
|
||||
Future<String> getShape() async {
|
||||
try {
|
||||
return (await _channel.invokeMethod<String>('getShape'))!;
|
||||
} on PlatformException catch (e, st) {
|
||||
// Default to round
|
||||
debugPrint('Error calling getShape: $e\n$st');
|
||||
return 'round';
|
||||
}
|
||||
}
|
||||
|
||||
/// Tells the application if we are currently in ambient mode
|
||||
Future<bool> isAmbient() async {
|
||||
try {
|
||||
return (await _channel.invokeMethod<bool>('isAmbient'))!;
|
||||
} on PlatformException catch (e, st) {
|
||||
debugPrint('Error calling isAmbient: $e\n$st');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether this activity's task should be moved to the front when
|
||||
/// the system exits ambient mode.
|
||||
///
|
||||
/// If true, the activity's task may be moved to the front if it was the
|
||||
/// last activity to be running when ambient started, depending on how
|
||||
/// much time the system spent in ambient mode.
|
||||
///
|
||||
Future<void> setAutoResumeEnabled(bool enabled) async {
|
||||
try {
|
||||
await _channel.invokeMethod<String>(
|
||||
'setAutoResumeEnabled',
|
||||
{'enabled': enabled},
|
||||
);
|
||||
} on PlatformException catch (e, st) {
|
||||
debugPrint('Error calling setAutoResumeEnabled: $e\n$st');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets whether this activity is currently in a state that supports ambient offload mode.
|
||||
Future<void> setAmbientOffloadEnabled(bool enabled) async {
|
||||
try {
|
||||
await _channel.invokeMethod<String>(
|
||||
'setAmbientOffloadEnabled',
|
||||
{'enabled': enabled},
|
||||
);
|
||||
} on PlatformException catch (e, st) {
|
||||
debugPrint('Error calling setAmbientOffloadEnabled: $e\n$st');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides details of current ambient mode configuration.
|
||||
class AmbientDetails {
|
||||
/// Constructor
|
||||
const AmbientDetails(this.burnInProtection, this.lowBitAmbient);
|
||||
|
||||
/// Used to indicate whether burn-in protection is required.
|
||||
///
|
||||
/// When this property is set to true, views must be shifted around
|
||||
/// periodically in ambient mode. To ensure that content isn't
|
||||
/// shifted off the screen, avoid placing content within 10 pixels
|
||||
/// of the edge of the screen. Activities should also avoid solid
|
||||
/// white areas to prevent pixel burn-in. Both of these
|
||||
/// requirements only apply in ambient mode, and only when
|
||||
/// this property is set to true.
|
||||
final bool burnInProtection;
|
||||
|
||||
/// Used to indicate whether the device has low-bit ambient mode.
|
||||
///
|
||||
/// When this property is set to true, the screen supports fewer bits
|
||||
/// for each color in ambient mode. In this case, activities should
|
||||
/// disable anti-aliasing in ambient mode.
|
||||
final bool lowBitAmbient;
|
||||
}
|
||||
|
||||
/// Callback to receive ambient mode state changes.
|
||||
mixin AmbientCallback {
|
||||
/// Called when an activity is entering ambient mode.
|
||||
void onEnterAmbient(AmbientDetails ambientDetails) {}
|
||||
|
||||
/// Called when an activity should exit ambient mode.
|
||||
void onExitAmbient() {}
|
||||
|
||||
/// Called when the system is updating the display for ambient mode.
|
||||
void onUpdateAmbient() {}
|
||||
|
||||
/// Called to inform an activity that whatever decomposition it has sent to
|
||||
/// Sidekick is no longer valid and should be re-sent before enabling ambient offload.
|
||||
void onInvalidateAmbientOffload() {}
|
||||
}
|
3
lib/wear_plus.dart
Normal file
3
lib/wear_plus.dart
Normal file
@ -0,0 +1,3 @@
|
||||
export 'src/ambient_widget.dart';
|
||||
export 'src/shape_widget.dart';
|
||||
export 'src/wear.dart';
|
22
pubspec.yaml
Normal file
22
pubspec.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
name: wear_plus
|
||||
description: An actively maintained plugin that offers Flutter support for Wear OS by Google
|
||||
version: 1.2.3
|
||||
homepage: https://github.com/Rexios80/wear_plus
|
||||
|
||||
environment:
|
||||
sdk: ^3.0.0
|
||||
flutter: ">=2.5.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
rexios_lints: ^10.1.0
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
package: dev.rexios.wear_plus
|
||||
pluginClass: WearPlugin
|
Loading…
x
Reference in New Issue
Block a user