From 075e3ac4132c53afded554fba35c5eb329e9288c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20Fr=C3=B6ling?= Date: Fri, 20 Jan 2023 17:46:05 +0100 Subject: [PATCH 01/28] [UPDATE] - add parameter auto enter to setAutoPipMode --- .../kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt | 3 ++- lib/simple_pip.dart | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt index 391c000..279da8d 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt @@ -132,9 +132,10 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val aspectRatio = call.argument>("aspectRatio") val seamlessResize = call.argument("seamlessResize") + val autoEnter = call.argument("autoEnter") val params = PictureInPictureParams.Builder() .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setAutoEnterEnabled(true) + .setAutoEnterEnabled(autoEnter) .setSeamlessResizeEnabled(seamlessResize!!) .setActions(actions) diff --git a/lib/simple_pip.dart b/lib/simple_pip.dart index 39bda3a..ed3b8d8 100644 --- a/lib/simple_pip.dart +++ b/lib/simple_pip.dart @@ -60,11 +60,12 @@ class SimplePip { /// Android 12 (Android S, API level 31) or newer required. Future setAutoPipMode({ aspectRatio = const [16, 9], - seamlessResize = false, + bool seamlessResize = false, + bool autoEnter = true, }) async { Map params = { 'aspectRatio': aspectRatio, - 'autoEnter': true, + 'autoEnter': autoEnter, 'seamlessResize': seamlessResize, }; final bool? setSuccessfully = From 020fa5dda77567e9868d8cd684d5fd80695e522d Mon Sep 17 00:00:00 2001 From: engine-ydsong-uxplus Date: Tue, 9 Jan 2024 17:10:12 +0900 Subject: [PATCH 02/28] feat: Android 14 Supported - Reflects changes to broadcast usage in Android 14 --- android/build.gradle | 6 +++-- .../simple_pip_mode/SimplePipModePlugin.kt | 26 +++++++++++-------- example/android/build.gradle | 6 ++--- .../gradle/wrapper/gradle-wrapper.properties | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 1d01687..9a134bc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,14 +2,14 @@ group 'cl.puntito.simple_pip_mode' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -46,5 +46,7 @@ android { } dependencies { + implementation "androidx.core:core:1.10.1" + implementation 'androidx.annotation:annotation:1.7.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt index 391c000..10d525c 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt @@ -12,6 +12,8 @@ import android.os.Build import android.util.Rational import androidx.annotation.NonNull import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.RECEIVER_EXPORTED import cl.puntito.simple_pip_mode.Constants.EXTRA_ACTION_TYPE import cl.puntito.simple_pip_mode.Constants.SIMPLE_PIP_ACTION import cl.puntito.simple_pip_mode.actions.PipAction @@ -62,7 +64,8 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } } }.also { broadcastReceiver = it } - context.registerReceiver(broadcastReceiver, IntentFilter(SIMPLE_PIP_ACTION)) + + ContextCompat.registerReceiver(context, broadcastReceiver, IntentFilter(SIMPLE_PIP_ACTION), RECEIVER_EXPORTED) } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { @@ -76,7 +79,7 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { result.success("Android ${Build.VERSION.RELEASE}") } else if (call.method == "isPipAvailable") { result.success( - activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) + activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) ) } else if (call.method == "isPipActivated") { result.success(activity.isInPictureInPictureMode) @@ -87,18 +90,18 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { val autoEnter = call.argument("autoEnter") val seamlessResize = call.argument("seamlessResize") var params = PictureInPictureParams.Builder() - .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setActions(actions) + .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) + .setActions(actions) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { params = params.setAutoEnterEnabled(autoEnter!!) - .setSeamlessResizeEnabled(seamlessResize!!) + .setSeamlessResizeEnabled(seamlessResize!!) } this.params = params result.success( - activity.enterPictureInPictureMode(params.build()) + activity.enterPictureInPictureMode(params.build()) ) } else if (call.method == "setPipLayout") { val success = call.argument("layout")?.let { @@ -114,7 +117,7 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } else if (call.method == "setIsPlaying") { call.argument("isPlaying")?.let { isPlaying -> if (actionsLayout.actions.contains(PipAction.PLAY) || - actionsLayout.actions.contains(PipAction.PAUSE)) { + actionsLayout.actions.contains(PipAction.PAUSE)) { var i = actionsLayout.actions.indexOf(PipAction.PLAY) if (i == -1) { i = actionsLayout.actions.indexOf(PipAction.PAUSE) @@ -131,12 +134,13 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } else if (call.method == "setAutoPipMode") { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val aspectRatio = call.argument>("aspectRatio") + val autoEnter = call.argument("autoEnter") val seamlessResize = call.argument("seamlessResize") val params = PictureInPictureParams.Builder() - .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setAutoEnterEnabled(true) - .setSeamlessResizeEnabled(seamlessResize!!) - .setActions(actions) + .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) + .setAutoEnterEnabled(autoEnter!!) + .setSeamlessResizeEnabled(seamlessResize!!) + .setActions(actions) this.params = params diff --git a/example/android/build.gradle b/example/android/build.gradle index 4256f91..b5c2ad0 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..6b66533 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip From 5e589ed2620db051a99c4b2471ff01593edfca75 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 19:22:54 -0300 Subject: [PATCH 03/28] Setup fvm and updated projects to compile with flutter 3.24 --- .fvmrc | 3 + .gitignore | 5 +- android/build.gradle | 2 +- example/README.md | 13 -- example/android/app/build.gradle | 1 - example/android/build.gradle | 6 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/pubspec.lock | 220 ++++++++++++++++++ pubspec.yaml | 2 +- 9 files changed, 233 insertions(+), 21 deletions(-) create mode 100644 .fvmrc create mode 100644 example/pubspec.lock diff --git a/.fvmrc b/.fvmrc new file mode 100644 index 0000000..906bbb3 --- /dev/null +++ b/.fvmrc @@ -0,0 +1,3 @@ +{ + "flutter": "3.24.3" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9be145f..aab2f03 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,7 @@ # 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/ +.vscode/ # Flutter/Dart/Pub related # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. @@ -27,3 +27,6 @@ .dart_tool/ .packages build/ + +# FVM Version Cache +.fvm/ \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 1d01687..74cdc9c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,7 +41,7 @@ android { } defaultConfig { - minSdkVersion 16 + minSdkVersion 21 } } diff --git a/example/README.md b/example/README.md index 24d5770..242d7ee 100644 --- a/example/README.md +++ b/example/README.md @@ -1,16 +1,3 @@ # simple_pip_mode_example Demonstrates how to use the simple_pip_mode plugin. - -## 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://flutter.dev/docs/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index a9ebb50..40fc572 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -42,7 +42,6 @@ android { } defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "cl.puntito.simple_pip_mode_example" minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion diff --git a/example/android/build.gradle b/example/android/build.gradle index 4256f91..b5c2ad0 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.2.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58a..cfe88f6 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip diff --git a/example/pubspec.lock b/example/pubspec.lock new file mode 100644 index 0000000..d643ea7 --- /dev/null +++ b/example/pubspec.lock @@ -0,0 +1,220 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.dev" + source: hosted + version: "1.0.8" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + url: "https://pub.dev" + source: hosted + version: "10.0.5" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + url: "https://pub.dev" + source: hosted + version: "3.0.5" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + url: "https://pub.dev" + source: hosted + version: "1.0.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + url: "https://pub.dev" + source: hosted + version: "1.15.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + simple_pip_mode: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "0.8.0" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + url: "https://pub.dev" + source: hosted + version: "14.2.5" +sdks: + dart: ">=3.3.0 <=3.5.3" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 0157b98..a604cd3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.8.0 repository: https://github.com/PuntitOwO/simple_pip_mode_flutter environment: - sdk: ">=2.16.0 <3.0.0" + sdk: ">=2.16.0 <=3.5.3" flutter: ">=2.5.0" dependencies: From 6e6e53ceb6f4449b062f47e7b4bb7f98a4bae1cf Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 20:40:19 -0300 Subject: [PATCH 04/28] refactor: pass flutter analyze without issues found --- .../simple_pip_mode/SimplePipModePlugin.kt | 2 +- example/lib/main.dart | 25 ++++++++++-------- lib/actions/pip_actions_layout.dart | 26 +++++++++---------- test/pip_widget_test.dart | 4 +-- test/simple_pip_test.dart | 4 +-- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt index 10d525c..f490caf 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt @@ -106,7 +106,7 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } else if (call.method == "setPipLayout") { val success = call.argument("layout")?.let { try { - actionsLayout = PipActionsLayout.valueOf(it.uppercase()) + actionsLayout = PipActionsLayout.valueOf(it.uppercase().replace("_", "")) actions = actionsLayout.remoteActions(context) true } catch(e: Exception) { diff --git a/example/lib/main.dart b/example/lib/main.dart index 3095df0..f326894 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:simple_pip_mode/actions/pip_action.dart'; import 'package:simple_pip_mode/actions/pip_actions_layout.dart'; @@ -78,7 +79,7 @@ class _ExampleAppState extends State { // builder is null so child is used when not in pip mode pipLayout: pipActionsLayout, onPipAction: (action) { - print("PIP ACTION TAP: " + action.name); + if (kDebugMode) print("PIP ACTION TAP: " + action.name); switch (action) { case PipAction.play: // example: videoPlayerController.play(); @@ -205,10 +206,10 @@ class _ExampleAppState extends State { ), pipActionsLayout != PipActionsLayout.none ? Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 8), - child: Row( + children: [ + Padding( + padding: const EdgeInsets.only(top: 8), + child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ const Text("Simulated player: "), @@ -221,17 +222,19 @@ class _ExampleAppState extends State { actionResponse = ""; }); }, - icon: Icon( - isPlaying ? Icons.pause : Icons.play_arrow)) + icon: Icon(isPlaying + ? Icons.pause + : Icons.play_arrow)) ], ), - ), + ), const Padding( padding: EdgeInsets.all(16), - child: Text("Obs.: Tap the simulated player button to see the PIP actions be updated on PIP mode, when you tap PIP actions on PIP mode it will reflect here too"), + child: Text( + "Obs.: Tap the simulated player button to see the PIP actions be updated on PIP mode, when you tap PIP actions on PIP mode it will reflect here too"), ) - ], - ) + ], + ) : Container(), ], ), diff --git a/lib/actions/pip_actions_layout.dart b/lib/actions/pip_actions_layout.dart index f4f0810..c74a6bf 100644 --- a/lib/actions/pip_actions_layout.dart +++ b/lib/actions/pip_actions_layout.dart @@ -1,20 +1,20 @@ /// PIP Actions Layout preset -/// -/// This layouts are defined on a ENUM inside Android src, where is +/// +/// This layouts are defined on a ENUM inside Android src, where is /// specified the actions each layout should show. -/// -/// TODO: Implement generic layouts on runtime, so plugin users can -/// create theirs own layouts without needing to update this preset -/// -/// +/// +/// +/// /// [none] do not show any actions on PIP mode /// [media] shows `previous`, `pause/play`, `next` actions (on this specific order) -/// [media_only_pause] shows only `pause/play` action -/// [media_live] shows `live` and `pause/play` actions (on this specific order) -/// +/// [mediaOnlyPause] shows only `pause/play` action +/// [mediaLive] shows `live` and `pause/play` actions (on this specific order) + enum PipActionsLayout { none, media, - media_only_pause, - media_live -} \ No newline at end of file + mediaOnlyPause, + mediaLive, +} + +// TODO(PuntitOwO): Implement generic layouts on runtime, so plugin users can create theirs own layouts without needing to update this preset \ No newline at end of file diff --git a/test/pip_widget_test.dart b/test/pip_widget_test.dart index ab4a4fc..9934871 100644 --- a/test/pip_widget_test.dart +++ b/test/pip_widget_test.dart @@ -25,7 +25,7 @@ void main() { final PipWidgetState state = tester.state(find.byType(PipWidget)); SimplePip pip = state.pip; - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { switch (methodCall.method) { case 'enterPipMode': @@ -76,7 +76,7 @@ void main() { final PipWidgetState state = tester.state(find.byType(PipWidget)); SimplePip pip = state.pip; - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { switch (methodCall.method) { case 'enterPipMode': diff --git a/test/simple_pip_test.dart b/test/simple_pip_test.dart index 17ce4aa..1749f44 100644 --- a/test/simple_pip_test.dart +++ b/test/simple_pip_test.dart @@ -18,7 +18,7 @@ void main() { setUp(() { enterCallbackCalled = false; exitCallbackCalled = false; - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { switch (methodCall.method) { case 'isPipAvailable': @@ -46,7 +46,7 @@ void main() { }); tearDown(() { - TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, null); }); From 9f40c8d6bca8f30840a1ef7cea39e7cd293830d6 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 20:51:34 -0300 Subject: [PATCH 05/28] chore: update pubspec, readme and changelog --- CHANGELOG.md | 8 +++++++- README.md | 39 ++++++++++++++++++++++++++++----------- pubspec.yaml | 2 +- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe35ea3..e9a09bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ +## 0.9.0 + +* Plugin updated to support Android 14 by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) +* Actions code refactor to pass flutter static analysis. +* README updated. + ## 0.8.0 -* Pip Actions implemented by [Erick Daros](https://github.com/erickdaros): [Issue #5](https://github.com/PuntitOwO/simple_pip_mode_flutter/issues/5). +* Pip Actions implemented by [Erick Daros](https://github.com/erickdaros): [PR #6](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/6). ## 0.7.1 diff --git a/README.md b/README.md index 7f95ea7..bd85554 100644 --- a/README.md +++ b/README.md @@ -22,16 +22,31 @@ In the `dependencies:` section of your `pubspec.yaml`, add the following line: simple_pip_mode: ``` -# Usage +# Table of contents + +- [Features](#features) +- [Installation](#installation) +- [Table of contents](#table-of-contents) +- [Usage](#usage) + - [Update manifest](#update-manifest) + - [Verify pip support](#verify-pip-support) + - [Entering pip mode](#entering-pip-mode) + - [Setting automatic pip mode](#setting-automatic-pip-mode) + - [Enabling callbacks](#enabling-callbacks) + - [Activity wrapper](#activity-wrapper) + - [Kotlin](#kotlin) + - [Java](#java) + - [Callback helper](#callback-helper) + - [Kotlin](#kotlin-1) + - [Java](#java-1) + - [Using callbacks](#using-callbacks) + - [Using the PIP widget](#using-the-pip-widget) + - [Using PIP Actions](#using-pip-actions) +- [Notes](#notes) + - [Multi-platform apps](#multi-platform-apps) +- [Contribute](#contribute) -This section has example code for the following tasks: -* [Update manifest](#update-manifest) -* [Verify PIP support](#verify-pip-support) -* [Entering PIP mode](#entering-pip-mode) -* [Enabling callbacks](#enabling-callbacks) -* [Using callbacks](#using-callbacks) -* [Using the PIP Widget](#using-the-pip-widget) -* [Using PIP Actions](#using-pip-actions) +# Usage ## Update manifest @@ -240,6 +255,8 @@ Calling `SimplePip` methods on a non-Android device will raise a `MissingPluginE # Contribute -Huge thanks to [Erick Daros](https://github.com/erickdaros) for PIP Actions feature. +Huge thanks to: +* [Erick Daros](https://github.com/erickdaros) for PIP Actions feature. +* [song011794](https://github.com/song011794) for updating the plugin to Android 14. -I'm currently working on more features, so issues and pull requests are appreciated! +Issues and pull requests are appreciated! diff --git a/pubspec.yaml b/pubspec.yaml index a604cd3..50a6504 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: simple_pip_mode description: A complete Picture-In-Picutre mode plugin (Android support only) -version: 0.8.0 +version: 0.9.0 repository: https://github.com/PuntitOwO/simple_pip_mode_flutter environment: From f8c13560d03a99521037cd7dbfeefe05591cb05c Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 21:05:47 -0300 Subject: [PATCH 06/28] fix: wrong autoEnter type inference --- .../kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt index f3cb551..b76b6a6 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt @@ -136,10 +136,9 @@ class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { val aspectRatio = call.argument>("aspectRatio") val autoEnter = call.argument("autoEnter") val seamlessResize = call.argument("seamlessResize") - val autoEnter = call.argument("autoEnter") val params = PictureInPictureParams.Builder() .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setAutoEnterEnabled(autoEnter) + .setAutoEnterEnabled(autoEnter!!) .setSeamlessResizeEnabled(seamlessResize!!) .setActions(actions) From 0f1490f15fbecd876deaa3a409c5d3b41f643729 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 21:10:09 -0300 Subject: [PATCH 07/28] chore: update readme and changelog --- CHANGELOG.md | 3 ++- README.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9a09bc..08b14bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## 0.9.0 -* Plugin updated to support Android 14 by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) +* Android 14 support by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) +* Add auto enter parameter to `setAutoPipMode` by [af-ffr](https://github.com/af-ffr): [PR #8](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/8) * Actions code refactor to pass flutter static analysis. * README updated. diff --git a/README.md b/README.md index bd85554..0f7fe2f 100644 --- a/README.md +++ b/README.md @@ -258,5 +258,6 @@ Calling `SimplePip` methods on a non-Android device will raise a `MissingPluginE Huge thanks to: * [Erick Daros](https://github.com/erickdaros) for PIP Actions feature. * [song011794](https://github.com/song011794) for updating the plugin to Android 14. +* [af-ffr](https://github.com/af-ffr) for updating the plugin to add auto enter parameter. Issues and pull requests are appreciated! From 3c9d5b3914bc15c3f3aa16798ff6ded42b239023 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 21:22:18 -0300 Subject: [PATCH 08/28] chore: update dependencies and clean up code --- CHANGELOG.md | 5 ++-- example/pubspec.lock | 12 +++++----- example/pubspec.yaml | 55 ++------------------------------------------ pubspec.yaml | 8 +++---- 4 files changed, 15 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08b14bf..cace9a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ -## 0.9.0 +## 1.0.0 * Android 14 support by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) * Add auto enter parameter to `setAutoPipMode` by [af-ffr](https://github.com/af-ffr): [PR #8](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/8) * Actions code refactor to pass flutter static analysis. -* README updated. +* Dependencies updated. +* Clean up code. ## 0.8.0 diff --git a/example/pubspec.lock b/example/pubspec.lock index d643ea7..6ea943d 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -66,10 +66,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "5.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: lints - sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "5.0.0" matcher: dependency: transitive description: @@ -145,7 +145,7 @@ packages: path: ".." relative: true source: path - version: "0.8.0" + version: "1.0.0" sky_engine: dependency: transitive description: flutter @@ -216,5 +216,5 @@ packages: source: hosted version: "14.2.5" sdks: - dart: ">=3.3.0 <=3.5.3" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 985fc4d..bee86c9 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,19 +1,11 @@ name: simple_pip_mode_example description: Demonstrates how to use the simple_pip_mode plugin. -# 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 environment: - sdk: ">=2.18.0 <3.0.0" + sdk: "^3.0.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 @@ -34,51 +26,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: ^1.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec + flutter_lints: ^5.0.0 # The following section is specific to Flutter. 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/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # 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/custom-fonts/#from-packages diff --git a/pubspec.yaml b/pubspec.yaml index 50a6504..e987b94 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: simple_pip_mode description: A complete Picture-In-Picutre mode plugin (Android support only) -version: 0.9.0 +version: 1.0.0 repository: https://github.com/PuntitOwO/simple_pip_mode_flutter environment: - sdk: ">=2.16.0 <=3.5.3" - flutter: ">=2.5.0" + sdk: "^3.0.0" + flutter: "^3.0.0" dependencies: flutter: @@ -14,7 +14,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + 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 From f2a23cf97a28d881a21a34d249c609431deea811 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 22:39:10 -0300 Subject: [PATCH 09/28] feat: add AspectRatio and usage --- example/lib/main.dart | 325 ++++++++++++++++++++++-------------------- lib/aspect_ratio.dart | 8 ++ lib/simple_pip.dart | 13 +- 3 files changed, 186 insertions(+), 160 deletions(-) create mode 100644 lib/aspect_ratio.dart diff --git a/example/lib/main.dart b/example/lib/main.dart index f326894..9ffdc41 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,19 +1,21 @@ +// ignore_for_file: sort_child_properties_last + import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; +import 'package:flutter/material.dart' hide AspectRatio; import 'package:simple_pip_mode/actions/pip_action.dart'; import 'package:simple_pip_mode/actions/pip_actions_layout.dart'; -import 'dart:async'; +import 'package:simple_pip_mode/aspect_ratio.dart'; import 'package:simple_pip_mode/simple_pip.dart'; // To enter pip mode and receive callbacks import 'package:simple_pip_mode/pip_widget.dart'; // To build pip mode dependent layouts /// Some aspect ratio presets to choose const aspectRatios = [ - [1, 1], - [2, 3], - [3, 2], - [16, 9], - [9, 16], + (1, 1), + (2, 3), + (3, 2), + (16, 9), + (9, 16), ]; void main() { @@ -24,7 +26,7 @@ void main() { /// Example App to show usage of PIP mode class ExampleApp extends StatefulWidget { - const ExampleApp({Key? key}) : super(key: key); + const ExampleApp({super.key}); @override State createState() => _ExampleAppState(); @@ -32,7 +34,7 @@ class ExampleApp extends StatefulWidget { class _ExampleAppState extends State { bool pipAvailable = false; - List aspectRatio = aspectRatios.first; + AspectRatio aspectRatio = aspectRatios.first; bool autoPipAvailable = false; bool autoPipSwitch = false; late SimplePip pip; @@ -60,12 +62,25 @@ class _ExampleAppState extends State { }); } - List> layoutList() { + /// List of available layouts + List> get layoutList { return PipActionsLayout.values .map>( - (PipActionsLayout value) => DropdownMenuItem( - child: Text(value.name), - value: value, + (PipActionsLayout layout) => DropdownMenuItem( + value: layout, + child: Text(layout.name), + ), + ) + .toList(); + } + + /// List of available aspect ratio presets + List> get aspectRatioList { + return aspectRatios + .map>( + (AspectRatio ratio) => DropdownMenuItem( + value: ratio, + child: Text(ratio.name), ), ) .toList(); @@ -78,78 +93,37 @@ class _ExampleAppState extends State { home: PipWidget( // builder is null so child is used when not in pip mode pipLayout: pipActionsLayout, - onPipAction: (action) { - if (kDebugMode) print("PIP ACTION TAP: " + action.name); - switch (action) { - case PipAction.play: - // example: videoPlayerController.play(); - setState(() { - isPlaying = true; - actionResponse = "Playing"; - }); - break; - case PipAction.pause: - // example: videoPlayerController.pause(); - setState(() { - isPlaying = false; - actionResponse = "Paused"; - }); - break; - case PipAction.live: - // example: videoPlayerController.forceLive(); - setState(() { - actionResponse = "Go to live view"; - }); - break; - case PipAction.next: - // example: videoPlayerController.next(); - setState(() { - actionResponse = "Next"; - }); - break; - case PipAction.previous: - // example: videoPlayerController.previous(); - setState(() { - actionResponse = "Previous"; - }); - break; - default: - break; - } - }, + onPipAction: _handlePipAction, child: Scaffold( - appBar: AppBar( - title: const Text('Pip Plugin example app'), - ), + appBar: AppBar(title: const Text('PiP Plugin example app')), body: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(width: double.infinity), - Text('Pip is ${pipAvailable ? '' : 'not '}Available'), - const Text('Pip is not activated'), - DropdownButton>( - value: aspectRatio, - onChanged: (List? newValue) { - if (newValue == null) return; - if (autoPipSwitch) { - pip.setAutoPipMode( - aspectRatio: newValue, - seamlessResize: true, - ); - } - setState(() { - aspectRatio = newValue; - }); - }, - items: aspectRatios - .map>>( - (List value) => DropdownMenuItem>( - child: Text('${value.first} : ${value.last}'), - value: value, - ), - ) - .toList(), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('PiP Available: '), + Icon(pipAvailable ? Icons.check : Icons.close), + ], + ), + const Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('PiP Activated: '), + Icon(Icons.close), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Aspect ratio: '), + DropdownButton( + value: aspectRatio, + onChanged: _handleAspectRatioSelection, + items: aspectRatioList, + ), + ], ), Row( mainAxisAlignment: MainAxisAlignment.center, @@ -157,29 +131,17 @@ class _ExampleAppState extends State { const Text('Auto Enter (Android S): '), Switch( value: autoPipSwitch, - onChanged: autoPipAvailable - ? (newValue) { - setState(() { - autoPipSwitch = newValue; - }); - } - : null, + onChanged: autoPipAvailable ? _handleAutoSwitch : null, ), ], ), IconButton( - onPressed: pipAvailable - ? () => pip.enterPipMode( - aspectRatio: aspectRatio, - ) - : null, + onPressed: pipAvailable ? _handleEnterPip : null, icon: const Icon(Icons.picture_in_picture), ), const Padding( padding: EdgeInsets.symmetric(vertical: 20), - child: Divider( - thickness: 1, - ), + child: Divider(thickness: 1), ), const Text("PIP Actions:"), Padding( @@ -190,84 +152,139 @@ class _ExampleAppState extends State { const Text("Current actions layout: "), DropdownButton( value: pipActionsLayout, - onChanged: (PipActionsLayout? newValue) { - if (newValue == null) return; - pip.setPipActionsLayout(newValue); - pip.setIsPlaying(true); - setState(() { - isPlaying = true; - pipActionsLayout = newValue; - }); - }, - items: layoutList(), + onChanged: _handlePipActionsLayoutSelection, + items: layoutList, ), ], ), ), - pipActionsLayout != PipActionsLayout.none - ? Column( - children: [ - Padding( - padding: const EdgeInsets.only(top: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Text("Simulated player: "), - IconButton( - onPressed: () { - bool newValue = !isPlaying; - pip.setIsPlaying(newValue); - setState(() { - isPlaying = newValue; - actionResponse = ""; - }); - }, - icon: Icon(isPlaying - ? Icons.pause - : Icons.play_arrow)) - ], + if (pipActionsLayout != PipActionsLayout.none) + Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const Text("Simulated player: "), + IconButton( + onPressed: _handleSimulatedPlayerToggle, + isSelected: isPlaying, + icon: Icon(Icons.play_arrow), + selectedIcon: Icon(Icons.pause), ), - ), - const Padding( - padding: EdgeInsets.all(16), - child: Text( - "Obs.: Tap the simulated player button to see the PIP actions be updated on PIP mode, when you tap PIP actions on PIP mode it will reflect here too"), - ) - ], - ) - : Container(), + ], + ), + ), + const Padding( + padding: EdgeInsets.all(16), + child: Text( + "Obs.: " + "Tap the simulated player button to see the PIP " + "actions be updated on PIP mode, when you tap PIP " + "actions on PIP mode it will reflect here too", + ), + ), + ], + ), ], ), ), // pip builder is null so pip child is used when in pip mode pipChild: Scaffold( - appBar: AppBar( - title: const Text('Pip Mode'), - ), + appBar: AppBar(title: const Text('Pip Mode')), body: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ - const SizedBox(width: double.infinity), + const SizedBox(width: double.maxFinite), const Text('Pip activated'), - pipActionsLayout != PipActionsLayout.none - ? IconButton( - onPressed: () { - bool newValue = !isPlaying; - pip.setIsPlaying(newValue); - setState(() { - isPlaying = newValue; - }); - }, - icon: Icon(isPlaying ? Icons.pause : Icons.play_arrow)) - : Container(), - pipActionsLayout != PipActionsLayout.none - ? Text(actionResponse) - : Container(), + if (pipActionsLayout != PipActionsLayout.none) ...[ + Icon(isPlaying ? Icons.pause : Icons.play_arrow), + Text(actionResponse), + ] ], ), ), ), ); } + + void _handleSimulatedPlayerToggle() { + bool newValue = !isPlaying; + pip.setIsPlaying(newValue); + setState(() { + isPlaying = newValue; + actionResponse = ""; + }); + } + + void _handlePipActionsLayoutSelection(PipActionsLayout? newValue) { + if (newValue == null) return; + pip.setPipActionsLayout(newValue); + pip.setIsPlaying(true); + setState(() { + isPlaying = true; + pipActionsLayout = newValue; + }); + } + + _handleEnterPip() => pip.enterPipMode( + aspectRatio: aspectRatio, + autoEnter: autoPipSwitch, + seamlessResize: autoPipSwitch, + ); + + _handleAutoSwitch(newValue) { + pip.setAutoPipMode( + aspectRatio: aspectRatio, + autoEnter: newValue, + seamlessResize: newValue, + ); + setState(() => autoPipSwitch = newValue); + } + + void _handleAspectRatioSelection(AspectRatio? newValue) { + if (newValue == null) return; + pip.setAutoPipMode( + aspectRatio: newValue, + autoEnter: autoPipSwitch, + seamlessResize: autoPipSwitch, + ); + setState(() => aspectRatio = newValue); + } + + _handlePipAction(action) { + if (kDebugMode) print("PIP ACTION TAP: ${action.name}"); + switch (action) { + case PipAction.play: + // example: videoPlayerController.play(); + setState(() { + isPlaying = true; + actionResponse = "Playing"; + }); + break; + case PipAction.pause: + // example: videoPlayerController.pause(); + setState(() { + isPlaying = false; + actionResponse = "Paused"; + }); + break; + case PipAction.live: + // example: videoPlayerController.forceLive(); + setState(() => actionResponse = "Go to live view"); + break; + case PipAction.next: + // example: videoPlayerController.next(); + setState(() => actionResponse = "Next"); + break; + case PipAction.previous: + // example: videoPlayerController.previous(); + setState(() => actionResponse = "Previous"); + break; + default: + break; + } + } } diff --git a/lib/aspect_ratio.dart b/lib/aspect_ratio.dart new file mode 100644 index 0000000..d331a8f --- /dev/null +++ b/lib/aspect_ratio.dart @@ -0,0 +1,8 @@ +typedef AspectRatio = (int, int); + +extension AspectRatioExtension on AspectRatio { + int get width => this.$1; + int get height => this.$2; + String get name => '$width:$height'; + List get asList => [width, height]; +} diff --git a/lib/simple_pip.dart b/lib/simple_pip.dart index ed3b8d8..5a82c75 100644 --- a/lib/simple_pip.dart +++ b/lib/simple_pip.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:simple_pip_mode/actions/pip_action.dart'; import 'package:simple_pip_mode/actions/pip_actions_layout.dart'; +import 'package:simple_pip_mode/aspect_ratio.dart'; /// Main controller class. /// It can verify whether the system supports PIP, @@ -42,12 +43,12 @@ class SimplePip { /// Request entering PIP mode Future enterPipMode({ - aspectRatio = const [16, 9], - autoEnter = false, - seamlessResize = false, + AspectRatio aspectRatio = const (16, 9), + bool autoEnter = false, + bool seamlessResize = false, }) async { Map params = { - 'aspectRatio': aspectRatio, + 'aspectRatio': aspectRatio.asList, 'autoEnter': autoEnter, 'seamlessResize': seamlessResize, }; @@ -59,12 +60,12 @@ class SimplePip { /// Request setting automatic PIP mode. /// Android 12 (Android S, API level 31) or newer required. Future setAutoPipMode({ - aspectRatio = const [16, 9], + AspectRatio aspectRatio = const (16, 9), bool seamlessResize = false, bool autoEnter = true, }) async { Map params = { - 'aspectRatio': aspectRatio, + 'aspectRatio': aspectRatio.asList, 'autoEnter': autoEnter, 'seamlessResize': seamlessResize, }; From 43754fa1a647050fa0680706e5fea4dedd843d24 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 23:07:07 -0300 Subject: [PATCH 10/28] refactor: refactor example code --- example/lib/main.dart | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 9ffdc41..8b960ce 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -168,7 +168,14 @@ class _ExampleAppState extends State { children: [ const Text("Simulated player: "), IconButton( - onPressed: _handleSimulatedPlayerToggle, + onPressed: () { + bool newValue = !isPlaying; + pip.setIsPlaying(newValue); + setState(() { + isPlaying = newValue; + actionResponse = ""; + }); + }, isSelected: isPlaying, icon: Icon(Icons.play_arrow), selectedIcon: Icon(Icons.pause), @@ -198,7 +205,7 @@ class _ExampleAppState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(width: double.maxFinite), - const Text('Pip activated'), + const Text('PiP activated'), if (pipActionsLayout != PipActionsLayout.none) ...[ Icon(isPlaying ? Icons.pause : Icons.play_arrow), Text(actionResponse), @@ -210,15 +217,6 @@ class _ExampleAppState extends State { ); } - void _handleSimulatedPlayerToggle() { - bool newValue = !isPlaying; - pip.setIsPlaying(newValue); - setState(() { - isPlaying = newValue; - actionResponse = ""; - }); - } - void _handlePipActionsLayoutSelection(PipActionsLayout? newValue) { if (newValue == null) return; pip.setPipActionsLayout(newValue); @@ -229,13 +227,13 @@ class _ExampleAppState extends State { }); } - _handleEnterPip() => pip.enterPipMode( + void _handleEnterPip() => pip.enterPipMode( aspectRatio: aspectRatio, autoEnter: autoPipSwitch, seamlessResize: autoPipSwitch, ); - _handleAutoSwitch(newValue) { + void _handleAutoSwitch(newValue) { pip.setAutoPipMode( aspectRatio: aspectRatio, autoEnter: newValue, @@ -254,7 +252,7 @@ class _ExampleAppState extends State { setState(() => aspectRatio = newValue); } - _handlePipAction(action) { + void _handlePipAction(PipAction action) { if (kDebugMode) print("PIP ACTION TAP: ${action.name}"); switch (action) { case PipAction.play: @@ -263,28 +261,21 @@ class _ExampleAppState extends State { isPlaying = true; actionResponse = "Playing"; }); - break; case PipAction.pause: // example: videoPlayerController.pause(); setState(() { isPlaying = false; actionResponse = "Paused"; }); - break; case PipAction.live: // example: videoPlayerController.forceLive(); setState(() => actionResponse = "Go to live view"); - break; case PipAction.next: // example: videoPlayerController.next(); setState(() => actionResponse = "Next"); - break; case PipAction.previous: // example: videoPlayerController.previous(); setState(() => actionResponse = "Previous"); - break; - default: - break; } } } From ac2108f90cfa1ff0984eb1f62086d9868aa181af Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 23:08:09 -0300 Subject: [PATCH 11/28] refactor: PipActionEnum --- lib/actions/pip_action.dart | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/actions/pip_action.dart b/lib/actions/pip_action.dart index 51e1f4f..2244046 100644 --- a/lib/actions/pip_action.dart +++ b/lib/actions/pip_action.dart @@ -1,24 +1,22 @@ /// PIP Action preset -/// -/// This actions are defined on a ENUM inside Android src, where is +/// +/// This actions are defined on a ENUM inside Android src, where is /// specified each action drawable, name, description and a [afterAction] /// that shows after the action is tap. Ex.: play's after action is pause /// and vice-versa. -/// -/// TODO: Create implement generic actions on runtime, so plugin users can -/// create theirs own actions without needing to update this preset -/// -/// +/// +/// /// [play] Play action represented by triangle play icon /// [pause] Pause action represented by double vertical bars pause icon /// [previous] Previous action represented by previous icon /// [next] Next action represented by next icon /// [live] Live action (force player seeker to show latest content) represented by sorround icon -/// enum PipAction { play, pause, previous, next, - live -} \ No newline at end of file + live, +} + +// TODO(PuntitOwO): Create implement generic actions on runtime, so plugin users can create theirs own actions without needing to update this preset From 027ac9f34dbb119337036872efa17bc2bca6b7c6 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 23:08:47 -0300 Subject: [PATCH 12/28] fix: fix documentation --- lib/actions/pip_actions_layout.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/actions/pip_actions_layout.dart b/lib/actions/pip_actions_layout.dart index c74a6bf..c2778bd 100644 --- a/lib/actions/pip_actions_layout.dart +++ b/lib/actions/pip_actions_layout.dart @@ -4,12 +4,10 @@ /// specified the actions each layout should show. /// /// -/// /// [none] do not show any actions on PIP mode /// [media] shows `previous`, `pause/play`, `next` actions (on this specific order) /// [mediaOnlyPause] shows only `pause/play` action /// [mediaLive] shows `live` and `pause/play` actions (on this specific order) - enum PipActionsLayout { none, media, From 6f23fc1d23dd36d1365f903f229e69bb9884994d Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 23:10:40 -0300 Subject: [PATCH 13/28] chore: update changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cace9a1..4e02840 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,10 @@ * Android 14 support by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) * Add auto enter parameter to `setAutoPipMode` by [af-ffr](https://github.com/af-ffr): [PR #8](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/8) +* Add `AspectRatio` record type. * Actions code refactor to pass flutter static analysis. * Dependencies updated. -* Clean up code. +* Refactor example app. ## 0.8.0 From ab156afe3b8d2cbfe504b433798ff34e41470a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20Mar=C3=ADn?= Date: Mon, 30 Sep 2024 23:35:32 -0300 Subject: [PATCH 14/28] chore: update README.md to include videos --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f7fe2f..e636491 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,11 @@ A complete Picture-In-Picture mode plugin for android API level 26+ (Android Ore Provides methods to check feature availability, enter PIP mode, callbacks for mode change and PIP Actions support. -![pip_example](https://user-images.githubusercontent.com/69210614/154329387-bd90ce0b-d563-4173-b2d0-2cbcc62b670c.gif) +[main.webm](https://github.com/user-attachments/assets/8d9dd33d-a008-41af-a663-579c3a9cae7d) + +[auto_enter.webm](https://github.com/user-attachments/assets/9704e36b-351d-440e-b66b-cf6ad1523d92) + +[actions.webm](https://github.com/user-attachments/assets/d171aaf5-2b21-4c49-9269-bd58cb3858b7) # Features From ded6cb4c30056e001ca91454d1f32c384df441bc Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 23:37:27 -0300 Subject: [PATCH 15/28] chore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e02840..f2be73b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Android 14 support by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) * Add auto enter parameter to `setAutoPipMode` by [af-ffr](https://github.com/af-ffr): [PR #8](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/8) * Add `AspectRatio` record type. +* Update README.md to inlcude updated videos of new features. * Actions code refactor to pass flutter static analysis. * Dependencies updated. * Refactor example app. From 5d3debcc1f963fecba9ee3c6c6413af4e989304d Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Mon, 30 Sep 2024 23:44:37 -0300 Subject: [PATCH 16/28] fix: downgrade flutter_lints --- example/pubspec.lock | 10 +++++----- example/pubspec.yaml | 2 +- pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index 6ea943d..a3fc743 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -66,10 +66,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "4.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -103,10 +103,10 @@ packages: dependency: transitive description: name: lints - sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "4.0.0" matcher: dependency: transitive description: @@ -216,5 +216,5 @@ packages: source: hosted version: "14.2.5" sdks: - dart: ">=3.5.0 <4.0.0" + dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index bee86c9..e36d9cc 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -26,7 +26,7 @@ dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 + flutter_lints: ^4.0.0 # The following section is specific to Flutter. flutter: diff --git a/pubspec.yaml b/pubspec.yaml index e987b94..6562c7c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^5.0.0 + flutter_lints: ^4.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec From 044be8588aea2903e4750fd58905da516849a846 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Tue, 1 Oct 2024 00:07:56 -0300 Subject: [PATCH 17/28] fix: remove flutter upper bound --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6562c7c..cec7473 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ repository: https://github.com/PuntitOwO/simple_pip_mode_flutter environment: sdk: "^3.0.0" - flutter: "^3.0.0" + flutter: ">=3.0.0" dependencies: flutter: From 39ad08d97321e1867b857d7392f5c1632b135e83 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Tue, 1 Oct 2024 00:13:28 -0300 Subject: [PATCH 18/28] fix: flutter analysis issues --- example/lib/main.dart | 4 ++-- example/pubspec.lock | 24 ++++++++++++------------ lib/pip_widget.dart | 16 +++++++--------- test/pip_widget_test.dart | 4 ++-- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 8b960ce..7454f07 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -177,8 +177,8 @@ class _ExampleAppState extends State { }); }, isSelected: isPlaying, - icon: Icon(Icons.play_arrow), - selectedIcon: Icon(Icons.pause), + icon: const Icon(Icons.play_arrow), + selectedIcon: const Icon(Icons.pause), ), ], ), diff --git a/example/pubspec.lock b/example/pubspec.lock index a3fc743..d78d912 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -79,18 +79,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: @@ -119,18 +119,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.12.0" path: dependency: transitive description: @@ -195,10 +195,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.0" vector_math: dependency: transitive description: @@ -211,10 +211,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.2.1" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/pip_widget.dart b/lib/pip_widget.dart index a822117..a996b7d 100644 --- a/lib/pip_widget.dart +++ b/lib/pip_widget.dart @@ -28,7 +28,7 @@ class PipWidget extends StatefulWidget { final Widget? pipChild; final PipActionsLayout pipLayout; const PipWidget({ - Key? key, + super.key, this.onPipEntered, this.onPipExited, this.onPipAction, @@ -36,10 +36,9 @@ class PipWidget extends StatefulWidget { this.child, this.pipBuilder, this.pipChild, - this.pipLayout = PipActionsLayout.none + this.pipLayout = PipActionsLayout.none, }) : assert(child != null || builder != null), - assert(pipChild != null || pipBuilder != null), - super(key: key); + assert(pipChild != null || pipBuilder != null); @override PipWidgetState createState() => PipWidgetState(); @@ -56,10 +55,9 @@ class PipWidgetState extends State { void initState() { super.initState(); pip = SimplePip( - onPipEntered: onPipEntered, - onPipExited: onPipExited, - onPipAction: onPipAction - ); + onPipEntered: onPipEntered, + onPipExited: onPipExited, + onPipAction: onPipAction); pip.setPipActionsLayout(widget.pipLayout); } @@ -79,7 +77,7 @@ class PipWidgetState extends State { widget.onPipExited?.call(); } - /// The user taps one PIP action + /// The user taps one PIP action void onPipAction(PipAction action) { widget.onPipAction?.call(action); } diff --git a/test/pip_widget_test.dart b/test/pip_widget_test.dart index 9934871..6c96306 100644 --- a/test/pip_widget_test.dart +++ b/test/pip_widget_test.dart @@ -16,9 +16,9 @@ void main() { MaterialApp( home: PipWidget( builder: (_) => const Text('builder'), - child: const Text('child'), pipBuilder: (_) => const Text('pipbuilder'), pipChild: const Text('pipchild'), + child: const Text('child'), ), ), ); @@ -68,8 +68,8 @@ void main() { await tester.pumpWidget( const MaterialApp( home: PipWidget( - child: Text('child'), pipChild: Text('pipchild'), + child: Text('child'), ), ), ); From cf259374f5a2a68ab8342c9328edf78f68f1c0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kau=C3=AA=20Martins?= Date: Sat, 28 Dec 2024 17:25:37 -0300 Subject: [PATCH 19/28] feat: add media with seek action and fix actions name --- README.md | 7 +- android/build.gradle | 5 +- android/src/main/AndroidManifest.xml | 3 +- .../simple_pip_mode/SimplePipModePlugin.kt | 322 +++++++++--------- .../simple_pip_mode/actions/PipAction.kt | 4 +- .../actions/PipActionsLayout.kt | 5 +- .../drawable/ic_baseline_forward_10_24.xml | 7 + .../res/drawable/ic_baseline_replay_10_24.xml | 7 + android/src/main/res/values/strings.xml | 4 + example/android/app/build.gradle | 2 + .../android/app/src/main/AndroidManifest.xml | 3 +- example/lib/main.dart | 9 +- example/pubspec.lock | 24 +- lib/actions/pip_action.dart | 2 + lib/actions/pip_actions_layout.dart | 2 + 15 files changed, 230 insertions(+), 176 deletions(-) create mode 100644 android/src/main/res/drawable/ic_baseline_forward_10_24.xml create mode 100644 android/src/main/res/drawable/ic_baseline_replay_10_24.xml diff --git a/README.md b/README.md index e636491..e21d3f7 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ You can also pass callbacks directly to `PipWidget`. ## Using PIP Actions To use PIP actions, you need to specify a `pipLayout` preset on your `PipWidget`. -The current available action layout presets are focused on giving support to media reproduction controls. They are `media`, `media_only_pause` and `media_live`. Those are defined on the `[PipActionsLayout]` enum. +The current available action layout presets are focused on giving support to media reproduction controls. They are `media`, `media_only_pause`, `media_live` and `mediaWithSeek10`. Those are defined on the `[PipActionsLayout]` enum. You can also add a `onPipAction` listener to handle actions callbacks from `PipWidget`. This can be defined on `SimplePip(onPipAction: ...)` too. ```dart @@ -232,6 +232,11 @@ class MyWidget extends StatelessWidget { // example: videoPlayerController.next(); case PipAction.previous: // example: videoPlayerController.previous(); + case PipAction.rewind: + // example: videoPlayerController.seek(-10); + case PipAction.forward: + // example: videoPlayerController.seek(10); + setState(() => actionResponse = "Forward") default: break; } diff --git a/android/build.gradle b/android/build.gradle index 50e3144..0494ee7 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -9,7 +9,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath 'com.android.tools.build:gradle:8.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -25,7 +25,8 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 31 + compileSdk 34 + namespace = "cl.puntito.simple_pip_mode" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 7fca53d..a2f47b6 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,2 @@ - + diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt index b76b6a6..80474e9 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt @@ -18,6 +18,7 @@ import cl.puntito.simple_pip_mode.Constants.EXTRA_ACTION_TYPE import cl.puntito.simple_pip_mode.Constants.SIMPLE_PIP_ACTION import cl.puntito.simple_pip_mode.actions.PipAction import cl.puntito.simple_pip_mode.actions.PipActionsLayout +import io.flutter.Log import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding @@ -27,160 +28,177 @@ import io.flutter.plugin.common.MethodChannel.MethodCallHandler /** SimplePipModePlugin */ -class SimplePipModePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { - - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private val CHANNEL = "puntito.simple_pip_mode" - private lateinit var channel: MethodChannel - private lateinit var context: Context - private lateinit var activity: Activity - private var actions: MutableList = mutableListOf() - private var actionsLayout: PipActionsLayout = PipActionsLayout.NONE - - private var callbackHelper = PipCallbackHelper() - private var params: PictureInPictureParams.Builder? = null - private lateinit var broadcastReceiver: BroadcastReceiver - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL) - callbackHelper.setChannel(channel) - channel.setMethodCallHandler(this) - context = flutterPluginBinding.applicationContext - broadcastReceiver = object : BroadcastReceiver() { - @RequiresApi(Build.VERSION_CODES.O) - override fun onReceive(context: Context, intent: Intent) { - if (SIMPLE_PIP_ACTION !== intent.action) { - return - } - intent.getStringExtra(EXTRA_ACTION_TYPE)?.let { - val action = PipAction.valueOf(it) - action.afterAction()?.let { - toggleAction(action) - } - callbackHelper.onPipAction(action) - } - } - }.also { broadcastReceiver = it } - - ContextCompat.registerReceiver(context, broadcastReceiver, IntentFilter(SIMPLE_PIP_ACTION), RECEIVER_EXPORTED) - } - - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - channel.setMethodCallHandler(null) - context.unregisterReceiver(broadcastReceiver) - } - - @RequiresApi(Build.VERSION_CODES.O) - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${Build.VERSION.RELEASE}") - } else if (call.method == "isPipAvailable") { - result.success( - activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) - ) - } else if (call.method == "isPipActivated") { - result.success(activity.isInPictureInPictureMode) - } else if (call.method == "isAutoPipAvailable") { - result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - } else if (call.method == "enterPipMode") { - val aspectRatio = call.argument>("aspectRatio") - val autoEnter = call.argument("autoEnter") - val seamlessResize = call.argument("seamlessResize") - var params = PictureInPictureParams.Builder() - .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setActions(actions) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - params = params.setAutoEnterEnabled(autoEnter!!) - .setSeamlessResizeEnabled(seamlessResize!!) - } - - this.params = params - - result.success( - activity.enterPictureInPictureMode(params.build()) - ) - } else if (call.method == "setPipLayout") { - val success = call.argument("layout")?.let { - try { - actionsLayout = PipActionsLayout.valueOf(it.uppercase().replace("_", "")) - actions = actionsLayout.remoteActions(context) - true - } catch(e: Exception) { - false - } - } ?: false - result.success(success) - } else if (call.method == "setIsPlaying") { - call.argument("isPlaying")?.let { isPlaying -> - if (actionsLayout.actions.contains(PipAction.PLAY) || - actionsLayout.actions.contains(PipAction.PAUSE)) { - var i = actionsLayout.actions.indexOf(PipAction.PLAY) - if (i == -1) { - i = actionsLayout.actions.indexOf(PipAction.PAUSE) - } - if( i >= 0) { - actionsLayout.actions[i] = if(isPlaying) PipAction.PAUSE else PipAction.PLAY - renderPipActions() - result.success(true) - } +class SimplePipModePlugin : FlutterPlugin, MethodCallHandler, ActivityAware { + + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private val CHANNEL = "puntito.simple_pip_mode" + private lateinit var channel: MethodChannel + private lateinit var context: Context + private lateinit var activity: Activity + private var actions: MutableList = mutableListOf() + private var actionsLayout: PipActionsLayout = PipActionsLayout.NONE + + private var callbackHelper = PipCallbackHelper() + private var params: PictureInPictureParams.Builder? = null + private lateinit var broadcastReceiver: BroadcastReceiver + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL) + callbackHelper.setChannel(channel) + channel.setMethodCallHandler(this) + context = flutterPluginBinding.applicationContext + broadcastReceiver = object : BroadcastReceiver() { + @RequiresApi(Build.VERSION_CODES.O) + override fun onReceive(context: Context, intent: Intent) { + if (SIMPLE_PIP_ACTION !== intent.action) { + return + } + intent.getStringExtra(EXTRA_ACTION_TYPE)?.let { + val action = PipAction.valueOf(it) + action.afterAction()?.let { + toggleAction(action) + } + callbackHelper.onPipAction(action) + } + } + }.also { broadcastReceiver = it } + + ContextCompat.registerReceiver( + context, + broadcastReceiver, + IntentFilter(SIMPLE_PIP_ACTION), + RECEIVER_EXPORTED + ) + } + + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) + context.unregisterReceiver(broadcastReceiver) + } + + @RequiresApi(Build.VERSION_CODES.O) + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + if (call.method == "getPlatformVersion") { + result.success("Android ${Build.VERSION.RELEASE}") + } else if (call.method == "isPipAvailable") { + result.success( + activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) + ) + } else if (call.method == "isPipActivated") { + result.success(activity.isInPictureInPictureMode) + } else if (call.method == "isAutoPipAvailable") { + result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + } else if (call.method == "enterPipMode") { + val aspectRatio = call.argument>("aspectRatio") + val autoEnter = call.argument("autoEnter") + val seamlessResize = call.argument("seamlessResize") + var params = PictureInPictureParams.Builder() + .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) + .setActions(actions) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + params = params.setAutoEnterEnabled(autoEnter!!) + .setSeamlessResizeEnabled(seamlessResize!!) + } + + this.params = params + + result.success( + activity.enterPictureInPictureMode(params.build()) + ) + } else if (call.method == "setPipLayout") { + val success = call.argument("layout")?.let { + try { + Log.e("PIP", convertAction(it)) + actionsLayout = PipActionsLayout.valueOf(convertAction(it)) + actions = actionsLayout.remoteActions(context) + true + } catch (e: Exception) { + false + } + } ?: false + result.success(success) + } else if (call.method == "setIsPlaying") { + call.argument("isPlaying")?.let { isPlaying -> + if (actionsLayout.actions.contains(PipAction.PLAY) || + actionsLayout.actions.contains(PipAction.PAUSE) + ) { + var i = actionsLayout.actions.indexOf(PipAction.PLAY) + if (i == -1) { + i = actionsLayout.actions.indexOf(PipAction.PAUSE) + } + if (i >= 0) { + actionsLayout.actions[i] = + if (isPlaying) PipAction.PAUSE else PipAction.PLAY + renderPipActions() + result.success(true) + } + } else { + result.success(false) + } + } ?: result.success(false) + } else if (call.method == "setAutoPipMode") { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val aspectRatio = call.argument>("aspectRatio") + val autoEnter = call.argument("autoEnter") + val seamlessResize = call.argument("seamlessResize") + val params = PictureInPictureParams.Builder() + .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) + .setAutoEnterEnabled(autoEnter!!) + .setSeamlessResizeEnabled(seamlessResize!!) + .setActions(actions) + + this.params = params + + activity.setPictureInPictureParams(params.build()) + + result.success(true) + } else { + result.error( + "NotImplemented", + "System Version less than Android S found", + "Expected Android S or newer." + ) + } } else { - result.success(false) + result.notImplemented() } - } ?: result.success(false) - } else if (call.method == "setAutoPipMode") { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val aspectRatio = call.argument>("aspectRatio") - val autoEnter = call.argument("autoEnter") - val seamlessResize = call.argument("seamlessResize") - val params = PictureInPictureParams.Builder() - .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setAutoEnterEnabled(autoEnter!!) - .setSeamlessResizeEnabled(seamlessResize!!) - .setActions(actions) - - this.params = params - - activity.setPictureInPictureParams(params.build()) - - result.success(true) - } else { - result.error("NotImplemented", "System Version less than Android S found", "Expected Android S or newer.") - } - } else { - result.notImplemented() } - } - - override fun onAttachedToActivity(binding: ActivityPluginBinding) { - activity = binding.activity - } - - override fun onDetachedFromActivityForConfigChanges() { - } - - override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { - activity = binding.activity - } - - override fun onDetachedFromActivity() { - } - - @RequiresApi(Build.VERSION_CODES.O) - private fun toggleAction(action: PipAction) { - actionsLayout.toggleToAfterAction(action) - renderPipActions() - } - - @RequiresApi(Build.VERSION_CODES.O) - private fun renderPipActions() { - actions = PipActionsLayout.remoteActions(context, actionsLayout.actions) - params?.let { - it.setActions(actions).build() - activity.setPictureInPictureParams(it.build()) + + override fun onAttachedToActivity(binding: ActivityPluginBinding) { + activity = binding.activity + } + + override fun onDetachedFromActivityForConfigChanges() { + } + + override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + activity = binding.activity + } + + override fun onDetachedFromActivity() { + } + + @RequiresApi(Build.VERSION_CODES.O) + private fun toggleAction(action: PipAction) { + actionsLayout.toggleToAfterAction(action) + renderPipActions() } - } + + @RequiresApi(Build.VERSION_CODES.O) + private fun renderPipActions() { + actions = PipActionsLayout.remoteActions(context, actionsLayout.actions) + params?.let { + it.setActions(actions).build() + activity.setPictureInPictureParams(it.build()) + } + } + + private fun convertAction(action: String) = action + .replace(Regex("([a-z])([A-Z])"), "$1_$2") + .replace(Regex("([a-z])([0-9])"), "$1_$2") + .uppercase() } diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt index 3b9cf77..d156b4f 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt @@ -21,7 +21,9 @@ enum class PipAction( PAUSE(R.drawable.ic_baseline_pause_24, R.string.pip_action_pause, R.string.pip_action_pause_description, "PLAY"), NEXT(R.drawable.ic_baseline_skip_next_24, R.string.pip_action_next, R.string.pip_action_next_description), PREVIOUS(R.drawable.ic_baseline_skip_previous_24, R.string.pip_action_previous, R.string.pip_action_previous_description), - LIVE(R.drawable.ic_surround_sound_24, R.string.pip_action_live, R.string.pip_action_live_description,); + LIVE(R.drawable.ic_surround_sound_24, R.string.pip_action_live, R.string.pip_action_live_description,), + REWIND(R.drawable.ic_baseline_replay_10_24,R.string.pip_action_rewind_10, R.string.pip_action_rewind_10_description), + FORWARD(R.drawable.ic_baseline_forward_10_24,R.string.pip_action_forward_10, R.string.pip_action_forward_10_description); @RequiresApi(Build.VERSION_CODES.O) fun toRemoteAction(context: Context) : RemoteAction = RemoteAction( diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipActionsLayout.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipActionsLayout.kt index 10e6c39..c59b4be 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipActionsLayout.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipActionsLayout.kt @@ -6,12 +6,13 @@ import android.os.Build import androidx.annotation.RequiresApi enum class PipActionsLayout( - var actions: MutableList + var actions: MutableList, ) { NONE(mutableListOf()), MEDIA(mutableListOf(PipAction.PREVIOUS, PipAction.PAUSE, PipAction.NEXT)), MEDIA_ONLY_PAUSE(mutableListOf(PipAction.PAUSE)), - MEDIA_LIVE(mutableListOf(PipAction.LIVE, PipAction.PAUSE)); + MEDIA_LIVE(mutableListOf(PipAction.LIVE, PipAction.PAUSE)), + MEDIA_WITH_SEEK_10(mutableListOf(PipAction.REWIND,PipAction.PAUSE, PipAction.FORWARD)); @RequiresApi(Build.VERSION_CODES.O) fun remoteActions(context: Context): MutableList = diff --git a/android/src/main/res/drawable/ic_baseline_forward_10_24.xml b/android/src/main/res/drawable/ic_baseline_forward_10_24.xml new file mode 100644 index 0000000..9170369 --- /dev/null +++ b/android/src/main/res/drawable/ic_baseline_forward_10_24.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/android/src/main/res/drawable/ic_baseline_replay_10_24.xml b/android/src/main/res/drawable/ic_baseline_replay_10_24.xml new file mode 100644 index 0000000..18d01da --- /dev/null +++ b/android/src/main/res/drawable/ic_baseline_replay_10_24.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/android/src/main/res/values/strings.xml b/android/src/main/res/values/strings.xml index 6a55567..467ddd3 100644 --- a/android/src/main/res/values/strings.xml +++ b/android/src/main/res/values/strings.xml @@ -11,5 +11,9 @@ Back to previous media Live Go to live view + Rewind + Rewind 10 + Forward 10 + Rewind 10 \ No newline at end of file diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 40fc572..a125f1b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -27,6 +27,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { compileSdkVersion flutter.compileSdkVersion + namespace 'cl.puntito.simple_pip_mode_example' compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -43,6 +44,7 @@ android { defaultConfig { applicationId "cl.puntito.simple_pip_mode_example" + namespace 'cl.puntito.simple_pip_mode_example' minSdkVersion flutter.minSdkVersion targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index dd8d92c..f32ea6e 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + { case PipAction.previous: // example: videoPlayerController.previous(); setState(() => actionResponse = "Previous"); + case PipAction.rewind: + // example: videoPlayerController.seek(-10); + setState(() => actionResponse = "Rewind"); + case PipAction.forward: + // example: videoPlayerController.seek(10); + setState(() => actionResponse = "Forward"); } } } diff --git a/example/pubspec.lock b/example/pubspec.lock index d78d912..a3fc743 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -79,18 +79,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -119,18 +119,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -195,10 +195,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" vector_math: dependency: transitive description: @@ -211,10 +211,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" sdks: dart: ">=3.3.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/lib/actions/pip_action.dart b/lib/actions/pip_action.dart index 2244046..53cb852 100644 --- a/lib/actions/pip_action.dart +++ b/lib/actions/pip_action.dart @@ -17,6 +17,8 @@ enum PipAction { previous, next, live, + rewind, + forward, } // TODO(PuntitOwO): Create implement generic actions on runtime, so plugin users can create theirs own actions without needing to update this preset diff --git a/lib/actions/pip_actions_layout.dart b/lib/actions/pip_actions_layout.dart index c2778bd..2f207fc 100644 --- a/lib/actions/pip_actions_layout.dart +++ b/lib/actions/pip_actions_layout.dart @@ -8,11 +8,13 @@ /// [media] shows `previous`, `pause/play`, `next` actions (on this specific order) /// [mediaOnlyPause] shows only `pause/play` action /// [mediaLive] shows `live` and `pause/play` actions (on this specific order) +/// [mediaWithSeek10] shows `previous`, `pause/play`, `next`, `seek10` actions (on this specific order) enum PipActionsLayout { none, media, mediaOnlyPause, mediaLive, + mediaWithSeek10, } // TODO(PuntitOwO): Implement generic layouts on runtime, so plugin users can create theirs own layouts without needing to update this preset \ No newline at end of file From 66255b2f57791e3e408f96d6ee794ce95304e044 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 11:40:44 -0300 Subject: [PATCH 20/28] chore: run dart format --- lib/actions/pip_actions_layout.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/actions/pip_actions_layout.dart b/lib/actions/pip_actions_layout.dart index 2f207fc..ea0b26d 100644 --- a/lib/actions/pip_actions_layout.dart +++ b/lib/actions/pip_actions_layout.dart @@ -17,4 +17,4 @@ enum PipActionsLayout { mediaWithSeek10, } -// TODO(PuntitOwO): Implement generic layouts on runtime, so plugin users can create theirs own layouts without needing to update this preset \ No newline at end of file +// TODO(PuntitOwO): Implement generic layouts on runtime, so plugin users can create theirs own layouts without needing to update this preset From f48f5a2640771489a5df86559b82bd250cf88122 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 13:12:59 -0300 Subject: [PATCH 21/28] chore: change example app gradle plugin apply method --- android/build.gradle | 6 ++-- example/android/app/build.gradle | 19 ++++-------- example/android/build.gradle | 13 -------- .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 30 ++++++++++++++----- 5 files changed, 32 insertions(+), 38 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 0494ee7..ac6741e 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -29,12 +29,12 @@ android { namespace = "cl.puntito.simple_pip_mode" compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = JavaVersion.VERSION_17 } sourceSets { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index a125f1b..c106f76 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { } } -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - def flutterVersionCode = localProperties.getProperty('flutter.versionCode') if (flutterVersionCode == null) { flutterVersionCode = '1' @@ -21,10 +22,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdkVersion flutter.compileSdkVersion namespace 'cl.puntito.simple_pip_mode_example' @@ -63,7 +60,3 @@ android { flutter { source '../..' } - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/build.gradle b/example/android/build.gradle index b5c2ad0..bc157bd 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,16 +1,3 @@ -buildscript { - ext.kotlin_version = '1.8.0' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { google() diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index cfe88f6..2aaed3a 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 44e62bc..66befea 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,11 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.3.2" apply false + id "org.jetbrains.kotlin.android" version "1.8.0" apply false +} -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" +include ':app' From b367c24a287deb1aef6459d12fc42df65621fb55 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 17:37:24 -0300 Subject: [PATCH 22/28] refactor: change indentation to 4 spaces --- .../simple_pip_mode/PipCallbackHelper.kt | 34 +++++++++---------- .../PipCallbackHelperActivityWrapper.kt | 20 +++++------ 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelper.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelper.kt index d9a99ac..d56538b 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelper.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelper.kt @@ -7,26 +7,26 @@ import io.flutter.embedding.engine.FlutterEngine open class PipCallbackHelper { - private val CHANNEL = "puntito.simple_pip_mode" - private lateinit var channel: MethodChannel + private val CHANNEL = "puntito.simple_pip_mode" + private lateinit var channel: MethodChannel - fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) - } + fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) + } - fun setChannel(channel: MethodChannel) { - this.channel = channel - } + fun setChannel(channel: MethodChannel) { + this.channel = channel + } - fun onPictureInPictureModeChanged(active: Boolean) { - if (active) { - channel.invokeMethod("onPipEntered", null) - } else { - channel.invokeMethod("onPipExited", null) + fun onPictureInPictureModeChanged(active: Boolean) { + if (active) { + channel.invokeMethod("onPipEntered", null) + } else { + channel.invokeMethod("onPipExited", null) + } } - } - fun onPipAction(action: PipAction) { - channel.invokeMethod("onPipAction", action.name.lowercase()) - } + fun onPipAction(action: PipAction) { + channel.invokeMethod("onPipAction", action.name.lowercase()) + } } \ No newline at end of file diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelperActivityWrapper.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelperActivityWrapper.kt index 4bf7feb..6b5ec84 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelperActivityWrapper.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/PipCallbackHelperActivityWrapper.kt @@ -7,16 +7,16 @@ import io.flutter.embedding.engine.FlutterEngine import cl.puntito.simple_pip_mode.PipCallbackHelper -open class PipCallbackHelperActivityWrapper: FlutterActivity() { - private var callbackHelper = PipCallbackHelper() +open class PipCallbackHelperActivityWrapper : FlutterActivity() { + private var callbackHelper = PipCallbackHelper() - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - super.configureFlutterEngine(flutterEngine) - callbackHelper.configureFlutterEngine(flutterEngine) - } + override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + callbackHelper.configureFlutterEngine(flutterEngine) + } - override fun onPictureInPictureModeChanged(active: Boolean, newConfig: Configuration?) { - super.onPictureInPictureModeChanged(active, newConfig) - callbackHelper.onPictureInPictureModeChanged(active) - } + override fun onPictureInPictureModeChanged(active: Boolean, newConfig: Configuration?) { + super.onPictureInPictureModeChanged(active, newConfig) + callbackHelper.onPictureInPictureModeChanged(active) + } } \ No newline at end of file From 4c2c258d5a4b092494a724c99518ecda1ca1bbdf Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 17:38:11 -0300 Subject: [PATCH 23/28] refactor: separated each method implementation of onMethodCall --- .../simple_pip_mode/SimplePipModePlugin.kt | 207 +++++++++++------- 1 file changed, 123 insertions(+), 84 deletions(-) diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt index 80474e9..b93331b 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/SimplePipModePlugin.kt @@ -27,6 +27,18 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler +/** PIP_METHODS enum */ +enum class PIP_METHODS(val methodName: String) { + GET_PLATFORM_VERSION("getPlatformVersion"), + IS_PIP_AVAILABLE("isPipAvailable"), + IS_PIP_ACTIVATED("isPipActivated"), + IS_AUTO_PIP_AVAILABLE("isAutoPipAvailable"), + ENTER_PIP_MODE("enterPipMode"), + SET_PIP_LAYOUT("setPipLayout"), + SET_IS_PLAYING("setIsPlaying"), + SET_AUTO_PIP_MODE("setAutoPipMode"), +} + /** SimplePipModePlugin */ class SimplePipModePlugin : FlutterPlugin, MethodCallHandler, ActivityAware { @@ -81,90 +93,16 @@ class SimplePipModePlugin : FlutterPlugin, MethodCallHandler, ActivityAware { @RequiresApi(Build.VERSION_CODES.O) override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - if (call.method == "getPlatformVersion") { - result.success("Android ${Build.VERSION.RELEASE}") - } else if (call.method == "isPipAvailable") { - result.success( - activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) - ) - } else if (call.method == "isPipActivated") { - result.success(activity.isInPictureInPictureMode) - } else if (call.method == "isAutoPipAvailable") { - result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - } else if (call.method == "enterPipMode") { - val aspectRatio = call.argument>("aspectRatio") - val autoEnter = call.argument("autoEnter") - val seamlessResize = call.argument("seamlessResize") - var params = PictureInPictureParams.Builder() - .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setActions(actions) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - params = params.setAutoEnterEnabled(autoEnter!!) - .setSeamlessResizeEnabled(seamlessResize!!) - } - - this.params = params - - result.success( - activity.enterPictureInPictureMode(params.build()) - ) - } else if (call.method == "setPipLayout") { - val success = call.argument("layout")?.let { - try { - Log.e("PIP", convertAction(it)) - actionsLayout = PipActionsLayout.valueOf(convertAction(it)) - actions = actionsLayout.remoteActions(context) - true - } catch (e: Exception) { - false - } - } ?: false - result.success(success) - } else if (call.method == "setIsPlaying") { - call.argument("isPlaying")?.let { isPlaying -> - if (actionsLayout.actions.contains(PipAction.PLAY) || - actionsLayout.actions.contains(PipAction.PAUSE) - ) { - var i = actionsLayout.actions.indexOf(PipAction.PLAY) - if (i == -1) { - i = actionsLayout.actions.indexOf(PipAction.PAUSE) - } - if (i >= 0) { - actionsLayout.actions[i] = - if (isPlaying) PipAction.PAUSE else PipAction.PLAY - renderPipActions() - result.success(true) - } - } else { - result.success(false) - } - } ?: result.success(false) - } else if (call.method == "setAutoPipMode") { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val aspectRatio = call.argument>("aspectRatio") - val autoEnter = call.argument("autoEnter") - val seamlessResize = call.argument("seamlessResize") - val params = PictureInPictureParams.Builder() - .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) - .setAutoEnterEnabled(autoEnter!!) - .setSeamlessResizeEnabled(seamlessResize!!) - .setActions(actions) - - this.params = params - - activity.setPictureInPictureParams(params.build()) - - result.success(true) - } else { - result.error( - "NotImplemented", - "System Version less than Android S found", - "Expected Android S or newer." - ) - } - } else { - result.notImplemented() + when (call.method) { + PIP_METHODS.GET_PLATFORM_VERSION.methodName -> getPlatformVersion(result) + PIP_METHODS.IS_PIP_AVAILABLE.methodName -> isPipAvailable(result) + PIP_METHODS.IS_PIP_ACTIVATED.methodName -> isPipActivated(result) + PIP_METHODS.IS_AUTO_PIP_AVAILABLE.methodName -> isAutoPipAvailable(result) + PIP_METHODS.ENTER_PIP_MODE.methodName -> enterPipMode(call, result) + PIP_METHODS.SET_PIP_LAYOUT.methodName -> setPipLayout(call, result) + PIP_METHODS.SET_IS_PLAYING.methodName -> setIsPlaying(call, result) + PIP_METHODS.SET_AUTO_PIP_MODE.methodName -> setAutoPipMode(call, result) + else -> result.notImplemented() } } @@ -182,6 +120,107 @@ class SimplePipModePlugin : FlutterPlugin, MethodCallHandler, ActivityAware { override fun onDetachedFromActivity() { } + /* METHOD IMPLEMENTATION */ + + private fun getPlatformVersion(result: MethodChannel.Result) { + result.success("Android ${Build.VERSION.RELEASE}") + } + + private fun isPipAvailable(result: MethodChannel.Result) { + result.success( + activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) + ) + } + + private fun isPipActivated(result: MethodChannel.Result) { + result.success(activity.isInPictureInPictureMode) + } + + private fun isAutoPipAvailable(result: MethodChannel.Result) { + result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + } + + private fun enterPipMode(call: MethodCall, result: MethodChannel.Result) { + val aspectRatio = call.argument>("aspectRatio") + val autoEnter = call.argument("autoEnter") + val seamlessResize = call.argument("seamlessResize") + var params = PictureInPictureParams.Builder() + .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) + .setActions(actions) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + params = params.setAutoEnterEnabled(autoEnter!!) + .setSeamlessResizeEnabled(seamlessResize!!) + } + + this.params = params + + result.success( + activity.enterPictureInPictureMode(params.build()) + ) + } + + private fun setPipLayout(call: MethodCall, result: MethodChannel.Result) { + val success = call.argument("layout")?.let { + try { + Log.i("PIP", "layout = ${convertAction(it)}") + actionsLayout = PipActionsLayout.valueOf(convertAction(it)) + actions = actionsLayout.remoteActions(context) + true + } catch (e: Exception) { + Log.e("PIP", e.message?: "Error setting layout") + false + } + } ?: false + result.success(success) + } + + private fun setIsPlaying(call: MethodCall, result: MethodChannel.Result) { + call.argument("isPlaying")?.let { isPlaying -> + if (actionsLayout.actions.contains(PipAction.PLAY) || + actionsLayout.actions.contains(PipAction.PAUSE) + ) { + var i = actionsLayout.actions.indexOf(PipAction.PLAY) + if (i == -1) { + i = actionsLayout.actions.indexOf(PipAction.PAUSE) + } + if (i >= 0) { + actionsLayout.actions[i] = + if (isPlaying) PipAction.PAUSE else PipAction.PLAY + renderPipActions() + result.success(true) + } + } else { + result.success(false) + } + } ?: result.success(false) + } + + private fun setAutoPipMode(call: MethodCall, result: MethodChannel.Result) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val aspectRatio = call.argument>("aspectRatio") + val autoEnter = call.argument("autoEnter") + val seamlessResize = call.argument("seamlessResize") + val params = PictureInPictureParams.Builder() + .setAspectRatio(Rational(aspectRatio!![0], aspectRatio[1])) + .setAutoEnterEnabled(autoEnter!!) + .setSeamlessResizeEnabled(seamlessResize!!) + .setActions(actions) + + this.params = params + + activity.setPictureInPictureParams(params.build()) + + result.success(true) + } else { + result.error( + "NotImplemented", + "System Version less than Android S found", + "Expected Android S or newer." + ) + } + } + @RequiresApi(Build.VERSION_CODES.O) private fun toggleAction(action: PipAction) { actionsLayout.toggleToAfterAction(action) From e66c4034f8ceda2a14da21b8c7252ea7e17c7f28 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 17:41:42 -0300 Subject: [PATCH 24/28] refactor: add space after comma --- .../kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt b/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt index d156b4f..0104e50 100644 --- a/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt +++ b/android/src/main/kotlin/cl/puntito/simple_pip_mode/actions/PipAction.kt @@ -22,8 +22,8 @@ enum class PipAction( NEXT(R.drawable.ic_baseline_skip_next_24, R.string.pip_action_next, R.string.pip_action_next_description), PREVIOUS(R.drawable.ic_baseline_skip_previous_24, R.string.pip_action_previous, R.string.pip_action_previous_description), LIVE(R.drawable.ic_surround_sound_24, R.string.pip_action_live, R.string.pip_action_live_description,), - REWIND(R.drawable.ic_baseline_replay_10_24,R.string.pip_action_rewind_10, R.string.pip_action_rewind_10_description), - FORWARD(R.drawable.ic_baseline_forward_10_24,R.string.pip_action_forward_10, R.string.pip_action_forward_10_description); + REWIND(R.drawable.ic_baseline_replay_10_24, R.string.pip_action_rewind_10, R.string.pip_action_rewind_10_description), + FORWARD(R.drawable.ic_baseline_forward_10_24, R.string.pip_action_forward_10, R.string.pip_action_forward_10_description); @RequiresApi(Build.VERSION_CODES.O) fun toRemoteAction(context: Context) : RemoteAction = RemoteAction( From c69a8df63fba3318201ff92cf025f032c5179c13 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 19:24:55 -0300 Subject: [PATCH 25/28] refactor: clean code in SimplePip --- lib/simple_pip.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/simple_pip.dart b/lib/simple_pip.dart index 5a82c75..89e604a 100644 --- a/lib/simple_pip.dart +++ b/lib/simple_pip.dart @@ -11,8 +11,7 @@ import 'package:simple_pip_mode/aspect_ratio.dart'; /// request entering PIP mode, /// and call some callbacks when the app changes its mode. class SimplePip { - static const MethodChannel _channel = - MethodChannel('puntito.simple_pip_mode'); + static const _channel = MethodChannel('puntito.simple_pip_mode'); /// Whether this device supports PIP mode. static Future get isPipAvailable async { @@ -39,7 +38,7 @@ class SimplePip { VoidCallback? onPipExited; /// Called when the user taps on a PIP action - Function(PipAction)? onPipAction; + void Function(PipAction)? onPipAction; /// Request entering PIP mode Future enterPipMode({ @@ -109,16 +108,13 @@ class SimplePip { switch (call.method) { case 'onPipEntered': onPipEntered?.call(); - break; case 'onPipExited': onPipExited?.call(); - break; case 'onPipAction': String arg = call.arguments; PipAction action = PipAction.values.firstWhere((e) => e.name == arg); onPipAction?.call(action); - break; } }, ); From 8631b0e4fb2b4805cb0d2d5ad52f85f58d8767f3 Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 19:25:14 -0300 Subject: [PATCH 26/28] docs: add documentation to AspectRatio --- lib/aspect_ratio.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/aspect_ratio.dart b/lib/aspect_ratio.dart index d331a8f..7be6844 100644 --- a/lib/aspect_ratio.dart +++ b/lib/aspect_ratio.dart @@ -1,8 +1,17 @@ +/// AspectRatio type to represent the width to height ratio. typedef AspectRatio = (int, int); +/// Extension to get the width, height, name, and list form of an AspectRatio. extension AspectRatioExtension on AspectRatio { + /// Width of the aspect ratio. int get width => this.$1; + + /// Height of the aspect ratio. int get height => this.$2; + + /// Aspect ratio in a human readable form. String get name => '$width:$height'; + + /// Aspect ratio as a list. List get asList => [width, height]; } From 8cf8db4928ff8b81d193fc9289cef2c3857acc0a Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 19:25:40 -0300 Subject: [PATCH 27/28] feat: add deprecation notice --- lib/pip_widget.dart | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/pip_widget.dart b/lib/pip_widget.dart index a996b7d..7a77c9b 100644 --- a/lib/pip_widget.dart +++ b/lib/pip_widget.dart @@ -1,3 +1,5 @@ +// ignore_for_file: deprecated_member_use_from_same_package + import 'package:flutter/material.dart'; import 'package:simple_pip_mode/actions/pip_action.dart'; import 'package:simple_pip_mode/actions/pip_actions_layout.dart'; @@ -22,8 +24,16 @@ class PipWidget extends StatefulWidget { final VoidCallback? onPipEntered; final VoidCallback? onPipExited; final Function(PipAction)? onPipAction; + @Deprecated( + 'Use a Builder widget as the child instead. ' + 'This field will be removed in v2.0.0.', + ) final Widget Function(BuildContext)? builder; final Widget? child; + @Deprecated( + 'Use a Builder widget as the pipChild instead. ' + 'This field will be removed in v2.0.0.', + ) final Widget Function(BuildContext)? pipBuilder; final Widget? pipChild; final PipActionsLayout pipLayout; @@ -51,41 +61,41 @@ class PipWidgetState extends State { /// Whether the app is currently in PIP mode bool _pipMode = false; + Widget? get builder => + widget.builder != null ? Builder(builder: widget.builder!) : null; + Widget? get pipBuilder => + widget.pipBuilder != null ? Builder(builder: widget.pipBuilder!) : null; + @override void initState() { super.initState(); pip = SimplePip( - onPipEntered: onPipEntered, - onPipExited: onPipExited, - onPipAction: onPipAction); + onPipEntered: onPipEntered, + onPipExited: onPipExited, + onPipAction: onPipAction, + ); pip.setPipActionsLayout(widget.pipLayout); } /// The app entered PIP mode void onPipEntered() { - setState(() { - _pipMode = true; - }); + setState(() => _pipMode = true); widget.onPipEntered?.call(); } /// The app exited PIP mode void onPipExited() { - setState(() { - _pipMode = false; - }); + setState(() => _pipMode = false); widget.onPipExited?.call(); } /// The user taps one PIP action - void onPipAction(PipAction action) { - widget.onPipAction?.call(action); - } + void onPipAction(PipAction action) => widget.onPipAction?.call(action); @override Widget build(BuildContext context) { return _pipMode - ? (widget.pipBuilder?.call(context) ?? widget.pipChild!) - : (widget.builder?.call(context) ?? widget.child!); + ? (pipBuilder ?? widget.pipChild!) + : (builder ?? widget.child!); } } From 64073e16df3869cfd2d675423f808b2c988cdbcf Mon Sep 17 00:00:00 2001 From: PuntitOwO Date: Thu, 23 Jan 2025 19:48:00 -0300 Subject: [PATCH 28/28] chore: update README and CHANGELOG --- CHANGELOG.md | 7 +++++++ README.md | 18 +++++++++--------- pubspec.yaml | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2be73b..62cba3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.1.0 + +* Fix namespace issue and add a new media action preset by [kmartins](https://github.com/kmartins): [PR #21](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/21) +* Add deprecation warning to `PipWidget.builder` and `PipWidget.pipBuilder` parameters. They will be removed in v2.0.0. +* Refactor native code method dispatcher to separate methods for each method call. +* Update README.md to include new features and deprecation warning. + ## 1.0.0 * Android 14 support by [song011794](https://github.com/song011794): [PR #12](https://github.com/PuntitOwO/simple_pip_mode_flutter/pull/12) diff --git a/README.md b/README.md index e21d3f7..44a6e2b 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ This way, when user presses home (or uses home gesture), the app enters PIP mode There's two ways of enabling callbacks: * [Activity wrapper](#activity-wrapper) (Recommended!) -* [Callback helper](#callback-helper) +* [Callback helper](#callback-helper) (The old, manual way) ### Activity wrapper @@ -189,16 +189,18 @@ SimplePip _pip = SimplePip( To use the widget, you need to [enable callbacks](#enabling-callbacks) first. Import `pip_widget.dart` file. -Add a `PipWidget` widget to your tree and give it a `builder` or a `child`, and a `pipBuilder` or a `pipChild`. +Add a `PipWidget` widget to your tree and give it a `child` and a `pipChild`. + +> [!Note] +> `builder` and `pipBuilder` are deprecated. Use a `Builder` as the `child` or `pipChild` instead. + ```dart import 'package:simple_pip_mode/pip_widget.dart'; class MyWidget extends StatelessWidget { Widget build(BuildContext context) { return PipWidget( - builder: (context) => Text('This is built when PIP mode is not active'), - child: Text('This widget is not used because builder is not null'), - //pipBuilder: (context) => Text('This is built when PIP mode is active'), - pipChild: Text('This widget is used because pipBuilder is null'), + child: Text('This is built when PIP mode is not active'), + pipChild: Text('This is built when PIP mode is active'), ); } } @@ -224,10 +226,8 @@ class MyWidget extends StatelessWidget { switch (action) { case PipAction.play: // example: videoPlayerController.play(); - break; case PipAction.pause: // example: videoPlayerController.pause(); - break; case PipAction.next: // example: videoPlayerController.next(); case PipAction.previous: @@ -236,7 +236,6 @@ class MyWidget extends StatelessWidget { // example: videoPlayerController.seek(-10); case PipAction.forward: // example: videoPlayerController.seek(10); - setState(() => actionResponse = "Forward") default: break; } @@ -268,5 +267,6 @@ Huge thanks to: * [Erick Daros](https://github.com/erickdaros) for PIP Actions feature. * [song011794](https://github.com/song011794) for updating the plugin to Android 14. * [af-ffr](https://github.com/af-ffr) for updating the plugin to add auto enter parameter. +* [kmartins](https://github.com/kmartins) for updating the plugin to add more actions. Issues and pull requests are appreciated! diff --git a/pubspec.yaml b/pubspec.yaml index cec7473..cf0f05a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: simple_pip_mode description: A complete Picture-In-Picutre mode plugin (Android support only) -version: 1.0.0 +version: 1.1.0 repository: https://github.com/PuntitOwO/simple_pip_mode_flutter environment: