diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 43d6654..cab4df7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,5 @@ name: test -# Consuming apps run their own test suites, not this package's, so without this -# workflow nothing enforces them. bcc-media-app's Semaphore `Test` block does -# fire when `/submodules/` changes, but it only runs that app's own `test/` — -# a regression in here reaches every consuming app unchallenged. on: push: branches: [main] @@ -27,3 +23,41 @@ jobs: - run: flutter analyze - run: flutter test + + android: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + + - uses: subosito/flutter-action@v2 + with: + flutter-version: 3.47.2 + channel: stable + cache: true + + - run: flutter pub get + + - run: make android-test + + ios: + runs-on: macos-26 + env: + DEVELOPER_DIR: /Applications/Xcode_26.6.app + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + flutter-version: 3.47.2 + channel: stable + cache: true + + - run: flutter pub get + + - run: make ios-test diff --git a/Makefile b/Makefile index 76b9f62..75b49d0 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: publish pigeons help +.PHONY: publish pigeons help ios-test android-test BUILD_NUMBER=$(shell grep -i -e "version: " pubspec.yaml | cut -d " " -f 2) @@ -15,4 +15,30 @@ publish: ## Publish the package to pub.dev mkdocs gh-deploy pigeons: ## Generate pigeon files - for f in pigeons/*.dart; do dart run pigeon --input $$f; done \ No newline at end of file + for f in pigeons/*.dart; do dart run pigeon --input $$f; done + +# Which simulator the native iOS tests run on. Defaults to the newest available +# iPhone, so this works on a dev machine and on a CI runner with a different +# set of runtimes installed. Override with either: +# make ios-test IOS_SIM_ID= +# make ios-test IOS_DESTINATION='platform=iOS Simulator,name=iPhone 16,OS=latest' +IOS_SIM_ID ?= $(shell xcrun simctl list devices available | awk '/^-- iOS /{ok=1; next} /^-- /{ok=0} ok && /iPhone/{l=$$0} END{print l}' | sed -E 's/.*\(([0-9A-Fa-f-]{36})\).*/\1/') +IOS_DESTINATION ?= id=$(IOS_SIM_ID) + +ios-test: ## Run the native iOS unit tests (example/ios/RunnerTests) on a simulator + @test -n "$(IOS_SIM_ID)" || (echo "No iPhone simulator available. Install one via Xcode > Settings > Components."; exit 1) + cd example && flutter pub get && flutter build ios --simulator --debug --config-only + cd example/ios && xcodebuild test \ + -workspace Runner.xcworkspace \ + -scheme Runner \ + -destination '$(IOS_DESTINATION)' \ + -only-testing:RunnerTests + +# `--config-only` rather than `pub get`: the example app's `gradlew` and +# `gradle-wrapper.jar` are gitignored (Flutter's template does this), so a fresh +# clone has no wrapper at all. `flutter build --config-only` resolves +# dependencies, writes local.properties, and injects the wrapper, leaving the +# tracked gradle-wrapper.properties (Gradle 8.14.3) in place. +android-test: ## Run the native Android unit tests (android/src/test) + cd example && flutter build apk --config-only + cd example/android && ./gradlew :bccm_player:testDebugUnitTest diff --git a/android/src/main/java/media/bcc/bccm_player/pigeon/ChromecastControllerPigeon.java b/android/src/main/java/media/bcc/bccm_player/pigeon/ChromecastControllerPigeon.java index c3e45f3..9e2166d 100644 --- a/android/src/main/java/media/bcc/bccm_player/pigeon/ChromecastControllerPigeon.java +++ b/android/src/main/java/media/bcc/bccm_player/pigeon/ChromecastControllerPigeon.java @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package media.bcc.bccm_player.pigeon; @@ -29,6 +29,154 @@ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class ChromecastControllerPigeon { + static boolean pigeonDoubleEquals(double a, double b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b)); + } + + static boolean pigeonFloatEquals(float a, float b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b)); + } + + static int pigeonDoubleHashCode(double d) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (d == 0.0) { + d = 0.0; + } + long bits = Double.doubleToLongBits(d); + return (int) (bits ^ (bits >>> 32)); + } + + static int pigeonFloatHashCode(float f) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (f == 0.0f) { + f = 0.0f; + } + return Float.floatToIntBits(f); + } + + static boolean pigeonDeepEquals(Object a, Object b) { + if (a == b) { return true; } + if (a == null || b == null) { return false; } + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + if (a instanceof int[] && b instanceof int[]) { + return Arrays.equals((int[]) a, (int[]) b); + } + if (a instanceof long[] && b instanceof long[]) { + return Arrays.equals((long[]) a, (long[]) b); + } + if (a instanceof double[] && b instanceof double[]) { + double[] da = (double[]) a; + double[] db = (double[]) b; + if (da.length != db.length) { + return false; + } + for (int i = 0; i < da.length; i++) { + if (!pigeonDoubleEquals(da[i], db[i])) { + return false; + } + } + return true; + } + if (a instanceof List && b instanceof List) { + List listA = (List) a; + List listB = (List) b; + if (listA.size() != listB.size()) { return false; } + for (int i = 0; i < listA.size(); i++) { + if (!pigeonDeepEquals(listA.get(i), listB.get(i))) { + return false; + } + } + return true; + } + if (a instanceof Map && b instanceof Map) { + Map mapA = (Map) a; + Map mapB = (Map) b; + if (mapA.size() != mapB.size()) { return false; } + for (Map.Entry entryA : mapA.entrySet()) { + Object keyA = entryA.getKey(); + Object valueA = entryA.getValue(); + boolean found = false; + for (Map.Entry entryB : mapB.entrySet()) { + Object keyB = entryB.getKey(); + if (pigeonDeepEquals(keyA, keyB)) { + Object valueB = entryB.getValue(); + if (pigeonDeepEquals(valueA, valueB)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + if (a instanceof Double && b instanceof Double) { + return pigeonDoubleEquals((double) a, (double) b); + } + if (a instanceof Float && b instanceof Float) { + return pigeonFloatEquals((float) a, (float) b); + } + return a.equals(b); + } + + static int pigeonDeepHashCode(Object value) { + if (value == null) { return 0; } + if (value instanceof byte[]) { + return Arrays.hashCode((byte[]) value); + } + if (value instanceof int[]) { + return Arrays.hashCode((int[]) value); + } + if (value instanceof long[]) { + return Arrays.hashCode((long[]) value); + } + if (value instanceof double[]) { + double[] da = (double[]) value; + int result = 1; + for (double d : da) { + result = 31 * result + pigeonDoubleHashCode(d); + } + return result; + } + if (value instanceof List) { + int result = 1; + for (Object item : (List) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Map) { + int result = 0; + for (Map.Entry entry : ((Map) value).entrySet()) { + result += ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue())); + } + return result; + } + if (value instanceof Object[]) { + int result = 1; + for (Object item : (Object[]) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Double) { + return pigeonDoubleHashCode((double) value); + } + if (value instanceof Float) { + return pigeonFloatHashCode((float) value); + } + return value.hashCode(); + } + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ public static class FlutterError extends RuntimeException { @@ -72,12 +220,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } CastSessionUnavailableEvent that = (CastSessionUnavailableEvent) o; - return Objects.equals(playbackPositionMs, that.playbackPositionMs); + return pigeonDeepEquals(playbackPositionMs, that.playbackPositionMs); } @Override public int hashCode() { - return Objects.hash(playbackPositionMs); + Object[] fields = new Object[] {getClass(), playbackPositionMs}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "CastSessionUnavailableEvent{" + "playbackPositionMs=" + playbackPositionMs + "}"; } public static final class Builder { diff --git a/android/src/main/java/media/bcc/bccm_player/pigeon/DownloaderApi.java b/android/src/main/java/media/bcc/bccm_player/pigeon/DownloaderApi.java index 120daed..1823245 100644 --- a/android/src/main/java/media/bcc/bccm_player/pigeon/DownloaderApi.java +++ b/android/src/main/java/media/bcc/bccm_player/pigeon/DownloaderApi.java @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package media.bcc.bccm_player.pigeon; @@ -29,6 +29,154 @@ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class DownloaderApi { + static boolean pigeonDoubleEquals(double a, double b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b)); + } + + static boolean pigeonFloatEquals(float a, float b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b)); + } + + static int pigeonDoubleHashCode(double d) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (d == 0.0) { + d = 0.0; + } + long bits = Double.doubleToLongBits(d); + return (int) (bits ^ (bits >>> 32)); + } + + static int pigeonFloatHashCode(float f) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (f == 0.0f) { + f = 0.0f; + } + return Float.floatToIntBits(f); + } + + static boolean pigeonDeepEquals(Object a, Object b) { + if (a == b) { return true; } + if (a == null || b == null) { return false; } + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + if (a instanceof int[] && b instanceof int[]) { + return Arrays.equals((int[]) a, (int[]) b); + } + if (a instanceof long[] && b instanceof long[]) { + return Arrays.equals((long[]) a, (long[]) b); + } + if (a instanceof double[] && b instanceof double[]) { + double[] da = (double[]) a; + double[] db = (double[]) b; + if (da.length != db.length) { + return false; + } + for (int i = 0; i < da.length; i++) { + if (!pigeonDoubleEquals(da[i], db[i])) { + return false; + } + } + return true; + } + if (a instanceof List && b instanceof List) { + List listA = (List) a; + List listB = (List) b; + if (listA.size() != listB.size()) { return false; } + for (int i = 0; i < listA.size(); i++) { + if (!pigeonDeepEquals(listA.get(i), listB.get(i))) { + return false; + } + } + return true; + } + if (a instanceof Map && b instanceof Map) { + Map mapA = (Map) a; + Map mapB = (Map) b; + if (mapA.size() != mapB.size()) { return false; } + for (Map.Entry entryA : mapA.entrySet()) { + Object keyA = entryA.getKey(); + Object valueA = entryA.getValue(); + boolean found = false; + for (Map.Entry entryB : mapB.entrySet()) { + Object keyB = entryB.getKey(); + if (pigeonDeepEquals(keyA, keyB)) { + Object valueB = entryB.getValue(); + if (pigeonDeepEquals(valueA, valueB)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + if (a instanceof Double && b instanceof Double) { + return pigeonDoubleEquals((double) a, (double) b); + } + if (a instanceof Float && b instanceof Float) { + return pigeonFloatEquals((float) a, (float) b); + } + return a.equals(b); + } + + static int pigeonDeepHashCode(Object value) { + if (value == null) { return 0; } + if (value instanceof byte[]) { + return Arrays.hashCode((byte[]) value); + } + if (value instanceof int[]) { + return Arrays.hashCode((int[]) value); + } + if (value instanceof long[]) { + return Arrays.hashCode((long[]) value); + } + if (value instanceof double[]) { + double[] da = (double[]) value; + int result = 1; + for (double d : da) { + result = 31 * result + pigeonDoubleHashCode(d); + } + return result; + } + if (value instanceof List) { + int result = 1; + for (Object item : (List) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Map) { + int result = 0; + for (Map.Entry entry : ((Map) value).entrySet()) { + result += ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue())); + } + return result; + } + if (value instanceof Object[]) { + int result = 1; + for (Object item : (Object[]) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Double) { + return pigeonDoubleHashCode((double) value); + } + if (value instanceof Float) { + return pigeonFloatHashCode((float) value); + } + return value.hashCode(); + } + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ public static class FlutterError extends RuntimeException { @@ -175,12 +323,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DownloadConfig that = (DownloadConfig) o; - return url.equals(that.url) && mimeType.equals(that.mimeType) && title.equals(that.title) && audioTrackIds.equals(that.audioTrackIds) && videoTrackIds.equals(that.videoTrackIds) && additionalData.equals(that.additionalData); + return pigeonDeepEquals(url, that.url) && pigeonDeepEquals(mimeType, that.mimeType) && pigeonDeepEquals(title, that.title) && pigeonDeepEquals(audioTrackIds, that.audioTrackIds) && pigeonDeepEquals(videoTrackIds, that.videoTrackIds) && pigeonDeepEquals(additionalData, that.additionalData); } @Override public int hashCode() { - return Objects.hash(url, mimeType, title, audioTrackIds, videoTrackIds, additionalData); + Object[] fields = new Object[] {getClass(), url, mimeType, title, audioTrackIds, videoTrackIds, additionalData}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "DownloadConfig{" + "url=" + url + ", " + "mimeType=" + mimeType + ", " + "title=" + title + ", " + "audioTrackIds=" + audioTrackIds + ", " + "videoTrackIds=" + videoTrackIds + ", " + "additionalData=" + additionalData + "}"; } public static final class Builder { @@ -357,12 +511,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Download that = (Download) o; - return key.equals(that.key) && config.equals(that.config) && Objects.equals(offlineUrl, that.offlineUrl) && fractionDownloaded.equals(that.fractionDownloaded) && status.equals(that.status) && Objects.equals(error, that.error); + return pigeonDeepEquals(key, that.key) && pigeonDeepEquals(config, that.config) && pigeonDeepEquals(offlineUrl, that.offlineUrl) && pigeonDeepEquals(fractionDownloaded, that.fractionDownloaded) && pigeonDeepEquals(status, that.status) && pigeonDeepEquals(error, that.error); } @Override public int hashCode() { - return Objects.hash(key, config, offlineUrl, fractionDownloaded, status, error); + Object[] fields = new Object[] {getClass(), key, config, offlineUrl, fractionDownloaded, status, error}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "Download{" + "key=" + key + ", " + "config=" + config + ", " + "offlineUrl=" + offlineUrl + ", " + "fractionDownloaded=" + fractionDownloaded + ", " + "status=" + status + ", " + "error=" + error + "}"; } public static final class Builder { @@ -490,12 +650,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DownloadFailedEvent that = (DownloadFailedEvent) o; - return key.equals(that.key) && Objects.equals(error, that.error); + return pigeonDeepEquals(key, that.key) && pigeonDeepEquals(error, that.error); } @Override public int hashCode() { - return Objects.hash(key, error); + Object[] fields = new Object[] {getClass(), key, error}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "DownloadFailedEvent{" + "key=" + key + ", " + "error=" + error + "}"; } public static final class Builder { @@ -565,12 +731,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DownloadRemovedEvent that = (DownloadRemovedEvent) o; - return key.equals(that.key); + return pigeonDeepEquals(key, that.key); } @Override public int hashCode() { - return Objects.hash(key); + Object[] fields = new Object[] {getClass(), key}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "DownloadRemovedEvent{" + "key=" + key + "}"; } public static final class Builder { @@ -628,12 +800,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DownloadChangedEvent that = (DownloadChangedEvent) o; - return download.equals(that.download); + return pigeonDeepEquals(download, that.download); } @Override public int hashCode() { - return Objects.hash(download); + Object[] fields = new Object[] {getClass(), download}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "DownloadChangedEvent{" + "download=" + download + "}"; } public static final class Builder { diff --git a/android/src/main/java/media/bcc/bccm_player/pigeon/PlaybackPlatformApi.java b/android/src/main/java/media/bcc/bccm_player/pigeon/PlaybackPlatformApi.java index 5fd739d..95a8f60 100644 --- a/android/src/main/java/media/bcc/bccm_player/pigeon/PlaybackPlatformApi.java +++ b/android/src/main/java/media/bcc/bccm_player/pigeon/PlaybackPlatformApi.java @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package media.bcc.bccm_player.pigeon; @@ -29,6 +29,154 @@ @SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) public class PlaybackPlatformApi { + static boolean pigeonDoubleEquals(double a, double b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0 ? 0.0 : a) == (b == 0.0 ? 0.0 : b) || (Double.isNaN(a) && Double.isNaN(b)); + } + + static boolean pigeonFloatEquals(float a, float b) { + // Normalize -0.0 to 0.0 and handle NaN equality. + return (a == 0.0f ? 0.0f : a) == (b == 0.0f ? 0.0f : b) || (Float.isNaN(a) && Float.isNaN(b)); + } + + static int pigeonDoubleHashCode(double d) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (d == 0.0) { + d = 0.0; + } + long bits = Double.doubleToLongBits(d); + return (int) (bits ^ (bits >>> 32)); + } + + static int pigeonFloatHashCode(float f) { + // Normalize -0.0 to 0.0 and handle NaN to ensure consistent hash codes. + if (f == 0.0f) { + f = 0.0f; + } + return Float.floatToIntBits(f); + } + + static boolean pigeonDeepEquals(Object a, Object b) { + if (a == b) { return true; } + if (a == null || b == null) { return false; } + if (a instanceof byte[] && b instanceof byte[]) { + return Arrays.equals((byte[]) a, (byte[]) b); + } + if (a instanceof int[] && b instanceof int[]) { + return Arrays.equals((int[]) a, (int[]) b); + } + if (a instanceof long[] && b instanceof long[]) { + return Arrays.equals((long[]) a, (long[]) b); + } + if (a instanceof double[] && b instanceof double[]) { + double[] da = (double[]) a; + double[] db = (double[]) b; + if (da.length != db.length) { + return false; + } + for (int i = 0; i < da.length; i++) { + if (!pigeonDoubleEquals(da[i], db[i])) { + return false; + } + } + return true; + } + if (a instanceof List && b instanceof List) { + List listA = (List) a; + List listB = (List) b; + if (listA.size() != listB.size()) { return false; } + for (int i = 0; i < listA.size(); i++) { + if (!pigeonDeepEquals(listA.get(i), listB.get(i))) { + return false; + } + } + return true; + } + if (a instanceof Map && b instanceof Map) { + Map mapA = (Map) a; + Map mapB = (Map) b; + if (mapA.size() != mapB.size()) { return false; } + for (Map.Entry entryA : mapA.entrySet()) { + Object keyA = entryA.getKey(); + Object valueA = entryA.getValue(); + boolean found = false; + for (Map.Entry entryB : mapB.entrySet()) { + Object keyB = entryB.getKey(); + if (pigeonDeepEquals(keyA, keyB)) { + Object valueB = entryB.getValue(); + if (pigeonDeepEquals(valueA, valueB)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + if (a instanceof Double && b instanceof Double) { + return pigeonDoubleEquals((double) a, (double) b); + } + if (a instanceof Float && b instanceof Float) { + return pigeonFloatEquals((float) a, (float) b); + } + return a.equals(b); + } + + static int pigeonDeepHashCode(Object value) { + if (value == null) { return 0; } + if (value instanceof byte[]) { + return Arrays.hashCode((byte[]) value); + } + if (value instanceof int[]) { + return Arrays.hashCode((int[]) value); + } + if (value instanceof long[]) { + return Arrays.hashCode((long[]) value); + } + if (value instanceof double[]) { + double[] da = (double[]) value; + int result = 1; + for (double d : da) { + result = 31 * result + pigeonDoubleHashCode(d); + } + return result; + } + if (value instanceof List) { + int result = 1; + for (Object item : (List) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Map) { + int result = 0; + for (Map.Entry entry : ((Map) value).entrySet()) { + result += ((pigeonDeepHashCode(entry.getKey()) * 31) ^ pigeonDeepHashCode(entry.getValue())); + } + return result; + } + if (value instanceof Object[]) { + int result = 1; + for (Object item : (Object[]) value) { + result = 31 * result + pigeonDeepHashCode(item); + } + return result; + } + if (value instanceof Double) { + return pigeonDoubleHashCode((double) value); + } + if (value instanceof Float) { + return pigeonFloatHashCode((float) value); + } + return value.hashCode(); + } + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ public static class FlutterError extends RuntimeException { @@ -179,12 +327,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } NpawConfig that = (NpawConfig) o; - return Objects.equals(appName, that.appName) && Objects.equals(appReleaseVersion, that.appReleaseVersion) && Objects.equals(accountCode, that.accountCode) && Objects.equals(deviceIsAnonymous, that.deviceIsAnonymous); + return pigeonDeepEquals(appName, that.appName) && pigeonDeepEquals(appReleaseVersion, that.appReleaseVersion) && pigeonDeepEquals(accountCode, that.accountCode) && pigeonDeepEquals(deviceIsAnonymous, that.deviceIsAnonymous); } @Override public int hashCode() { - return Objects.hash(appName, appReleaseVersion, accountCode, deviceIsAnonymous); + Object[] fields = new Object[] {getClass(), appName, appReleaseVersion, accountCode, deviceIsAnonymous}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "NpawConfig{" + "appName=" + appName + ", " + "appReleaseVersion=" + appReleaseVersion + ", " + "accountCode=" + accountCode + ", " + "deviceIsAnonymous=" + deviceIsAnonymous + "}"; } public static final class Builder { @@ -321,12 +475,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AppConfig that = (AppConfig) o; - return Objects.equals(appLanguage, that.appLanguage) && audioLanguages.equals(that.audioLanguages) && subtitleLanguages.equals(that.subtitleLanguages) && Objects.equals(analyticsId, that.analyticsId) && Objects.equals(sessionId, that.sessionId); + return pigeonDeepEquals(appLanguage, that.appLanguage) && pigeonDeepEquals(audioLanguages, that.audioLanguages) && pigeonDeepEquals(subtitleLanguages, that.subtitleLanguages) && pigeonDeepEquals(analyticsId, that.analyticsId) && pigeonDeepEquals(sessionId, that.sessionId); } @Override public int hashCode() { - return Objects.hash(appLanguage, audioLanguages, subtitleLanguages, analyticsId, sessionId); + Object[] fields = new Object[] {getClass(), appLanguage, audioLanguages, subtitleLanguages, analyticsId, sessionId}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "AppConfig{" + "appLanguage=" + appLanguage + ", " + "audioLanguages=" + audioLanguages + ", " + "subtitleLanguages=" + subtitleLanguages + ", " + "analyticsId=" + analyticsId + ", " + "sessionId=" + sessionId + "}"; } public static final class Builder { @@ -426,12 +586,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } User that = (User) o; - return Objects.equals(id, that.id); + return pigeonDeepEquals(id, that.id); } @Override public int hashCode() { - return Objects.hash(id); + Object[] fields = new Object[] {getClass(), id}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "User{" + "id=" + id + "}"; } public static final class Builder { @@ -512,12 +678,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SetUrlArgs that = (SetUrlArgs) o; - return playerId.equals(that.playerId) && url.equals(that.url) && Objects.equals(isLive, that.isLive); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(url, that.url) && pigeonDeepEquals(isLive, that.isLive); } @Override public int hashCode() { - return Objects.hash(playerId, url, isLive); + Object[] fields = new Object[] {getClass(), playerId, url, isLive}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "SetUrlArgs{" + "playerId=" + playerId + ", " + "url=" + url + ", " + "isLive=" + isLive + "}"; } public static final class Builder { @@ -673,12 +845,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MediaItem that = (MediaItem) o; - return Objects.equals(id, that.id) && Objects.equals(url, that.url) && Objects.equals(mimeType, that.mimeType) && Objects.equals(metadata, that.metadata) && Objects.equals(isLive, that.isLive) && Objects.equals(isOffline, that.isOffline) && Objects.equals(playbackStartPositionMs, that.playbackStartPositionMs) && Objects.equals(lastKnownAudioLanguage, that.lastKnownAudioLanguage) && Objects.equals(lastKnownSubtitleLanguage, that.lastKnownSubtitleLanguage); + return pigeonDeepEquals(id, that.id) && pigeonDeepEquals(url, that.url) && pigeonDeepEquals(mimeType, that.mimeType) && pigeonDeepEquals(metadata, that.metadata) && pigeonDeepEquals(isLive, that.isLive) && pigeonDeepEquals(isOffline, that.isOffline) && pigeonDeepEquals(playbackStartPositionMs, that.playbackStartPositionMs) && pigeonDeepEquals(lastKnownAudioLanguage, that.lastKnownAudioLanguage) && pigeonDeepEquals(lastKnownSubtitleLanguage, that.lastKnownSubtitleLanguage); } @Override public int hashCode() { - return Objects.hash(id, url, mimeType, metadata, isLive, isOffline, playbackStartPositionMs, lastKnownAudioLanguage, lastKnownSubtitleLanguage); + Object[] fields = new Object[] {getClass(), id, url, mimeType, metadata, isLive, isOffline, playbackStartPositionMs, lastKnownAudioLanguage, lastKnownSubtitleLanguage}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "MediaItem{" + "id=" + id + ", " + "url=" + url + ", " + "mimeType=" + mimeType + ", " + "metadata=" + metadata + ", " + "isLive=" + isLive + ", " + "isOffline=" + isOffline + ", " + "playbackStartPositionMs=" + playbackStartPositionMs + ", " + "lastKnownAudioLanguage=" + lastKnownAudioLanguage + ", " + "lastKnownSubtitleLanguage=" + lastKnownSubtitleLanguage + "}"; } public static final class Builder { @@ -866,12 +1044,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MediaMetadata that = (MediaMetadata) o; - return Objects.equals(artworkUri, that.artworkUri) && Objects.equals(title, that.title) && Objects.equals(artist, that.artist) && Objects.equals(durationMs, that.durationMs) && Objects.equals(extras, that.extras); + return pigeonDeepEquals(artworkUri, that.artworkUri) && pigeonDeepEquals(title, that.title) && pigeonDeepEquals(artist, that.artist) && pigeonDeepEquals(durationMs, that.durationMs) && pigeonDeepEquals(extras, that.extras); } @Override public int hashCode() { - return Objects.hash(artworkUri, title, artist, durationMs, extras); + Object[] fields = new Object[] {getClass(), artworkUri, title, artist, durationMs, extras}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "MediaMetadata{" + "artworkUri=" + artworkUri + ", " + "title=" + title + ", " + "artist=" + artist + ", " + "durationMs=" + durationMs + ", " + "extras=" + extras + "}"; } public static final class Builder { @@ -1109,12 +1293,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlayerStateSnapshot that = (PlayerStateSnapshot) o; - return playerId.equals(that.playerId) && playbackState.equals(that.playbackState) && isBuffering.equals(that.isBuffering) && isFullscreen.equals(that.isFullscreen) && playbackSpeed.equals(that.playbackSpeed) && Objects.equals(videoSize, that.videoSize) && Objects.equals(currentMediaItem, that.currentMediaItem) && Objects.equals(playbackPositionMs, that.playbackPositionMs) && Objects.equals(textureId, that.textureId) && Objects.equals(volume, that.volume) && Objects.equals(error, that.error) && Objects.equals(seekableRangeStartMs, that.seekableRangeStartMs) && Objects.equals(seekableRangeEndMs, that.seekableRangeEndMs); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(playbackState, that.playbackState) && pigeonDeepEquals(isBuffering, that.isBuffering) && pigeonDeepEquals(isFullscreen, that.isFullscreen) && pigeonDeepEquals(playbackSpeed, that.playbackSpeed) && pigeonDeepEquals(videoSize, that.videoSize) && pigeonDeepEquals(currentMediaItem, that.currentMediaItem) && pigeonDeepEquals(playbackPositionMs, that.playbackPositionMs) && pigeonDeepEquals(textureId, that.textureId) && pigeonDeepEquals(volume, that.volume) && pigeonDeepEquals(error, that.error) && pigeonDeepEquals(seekableRangeStartMs, that.seekableRangeStartMs) && pigeonDeepEquals(seekableRangeEndMs, that.seekableRangeEndMs); } @Override public int hashCode() { - return Objects.hash(playerId, playbackState, isBuffering, isFullscreen, playbackSpeed, videoSize, currentMediaItem, playbackPositionMs, textureId, volume, error, seekableRangeStartMs, seekableRangeEndMs); + Object[] fields = new Object[] {getClass(), playerId, playbackState, isBuffering, isFullscreen, playbackSpeed, videoSize, currentMediaItem, playbackPositionMs, textureId, volume, error, seekableRangeStartMs, seekableRangeEndMs}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PlayerStateSnapshot{" + "playerId=" + playerId + ", " + "playbackState=" + playbackState + ", " + "isBuffering=" + isBuffering + ", " + "isFullscreen=" + isFullscreen + ", " + "playbackSpeed=" + playbackSpeed + ", " + "videoSize=" + videoSize + ", " + "currentMediaItem=" + currentMediaItem + ", " + "playbackPositionMs=" + playbackPositionMs + ", " + "textureId=" + textureId + ", " + "volume=" + volume + ", " + "error=" + error + ", " + "seekableRangeStartMs=" + seekableRangeStartMs + ", " + "seekableRangeEndMs=" + seekableRangeEndMs + "}"; } public static final class Builder { @@ -1320,12 +1510,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlayerError that = (PlayerError) o; - return Objects.equals(code, that.code) && Objects.equals(message, that.message); + return pigeonDeepEquals(code, that.code) && pigeonDeepEquals(message, that.message); } @Override public int hashCode() { - return Objects.hash(code, message); + Object[] fields = new Object[] {getClass(), code, message}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PlayerError{" + "code=" + code + ", " + "message=" + message + "}"; } public static final class Builder { @@ -1408,12 +1604,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } VideoSize that = (VideoSize) o; - return width.equals(that.width) && height.equals(that.height); + return pigeonDeepEquals(width, that.width) && pigeonDeepEquals(height, that.height); } @Override public int hashCode() { - return Objects.hash(width, height); + Object[] fields = new Object[] {getClass(), width, height}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "VideoSize{" + "width=" + width + ", " + "height=" + height + "}"; } public static final class Builder { @@ -1493,12 +1695,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ChromecastState that = (ChromecastState) o; - return connectionState.equals(that.connectionState) && Objects.equals(mediaItem, that.mediaItem); + return pigeonDeepEquals(connectionState, that.connectionState) && pigeonDeepEquals(mediaItem, that.mediaItem); } @Override public int hashCode() { - return Objects.hash(connectionState, mediaItem); + Object[] fields = new Object[] {getClass(), connectionState, mediaItem}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "ChromecastState{" + "connectionState=" + connectionState + ", " + "mediaItem=" + mediaItem + "}"; } public static final class Builder { @@ -1594,12 +1802,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MediaInfo that = (MediaInfo) o; - return audioTracks.equals(that.audioTracks) && textTracks.equals(that.textTracks) && videoTracks.equals(that.videoTracks); + return pigeonDeepEquals(audioTracks, that.audioTracks) && pigeonDeepEquals(textTracks, that.textTracks) && pigeonDeepEquals(videoTracks, that.videoTracks); } @Override public int hashCode() { - return Objects.hash(audioTracks, textTracks, videoTracks); + Object[] fields = new Object[] {getClass(), audioTracks, textTracks, videoTracks}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "MediaInfo{" + "audioTracks=" + audioTracks + ", " + "textTracks=" + textTracks + ", " + "videoTracks=" + videoTracks + "}"; } public static final class Builder { @@ -1720,12 +1934,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlayerTracksSnapshot that = (PlayerTracksSnapshot) o; - return playerId.equals(that.playerId) && audioTracks.equals(that.audioTracks) && textTracks.equals(that.textTracks) && videoTracks.equals(that.videoTracks); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(audioTracks, that.audioTracks) && pigeonDeepEquals(textTracks, that.textTracks) && pigeonDeepEquals(videoTracks, that.videoTracks); } @Override public int hashCode() { - return Objects.hash(playerId, audioTracks, textTracks, videoTracks); + Object[] fields = new Object[] {getClass(), playerId, audioTracks, textTracks, videoTracks}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PlayerTracksSnapshot{" + "playerId=" + playerId + ", " + "audioTracks=" + audioTracks + ", " + "textTracks=" + textTracks + ", " + "videoTracks=" + videoTracks + "}"; } public static final class Builder { @@ -1902,12 +2122,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Track that = (Track) o; - return id.equals(that.id) && Objects.equals(label, that.label) && Objects.equals(language, that.language) && Objects.equals(frameRate, that.frameRate) && Objects.equals(bitrate, that.bitrate) && Objects.equals(width, that.width) && Objects.equals(height, that.height) && Objects.equals(downloaded, that.downloaded) && isSelected.equals(that.isSelected); + return pigeonDeepEquals(id, that.id) && pigeonDeepEquals(label, that.label) && pigeonDeepEquals(language, that.language) && pigeonDeepEquals(frameRate, that.frameRate) && pigeonDeepEquals(bitrate, that.bitrate) && pigeonDeepEquals(width, that.width) && pigeonDeepEquals(height, that.height) && pigeonDeepEquals(downloaded, that.downloaded) && pigeonDeepEquals(isSelected, that.isSelected); } @Override public int hashCode() { - return Objects.hash(id, label, language, frameRate, bitrate, width, height, downloaded, isSelected); + Object[] fields = new Object[] {getClass(), id, label, language, frameRate, bitrate, width, height, downloaded, isSelected}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "Track{" + "id=" + id + ", " + "label=" + label + ", " + "language=" + language + ", " + "frameRate=" + frameRate + ", " + "bitrate=" + bitrate + ", " + "width=" + width + ", " + "height=" + height + ", " + "downloaded=" + downloaded + ", " + "isSelected=" + isSelected + "}"; } public static final class Builder { @@ -2055,12 +2281,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PrimaryPlayerChangedEvent that = (PrimaryPlayerChangedEvent) o; - return Objects.equals(playerId, that.playerId); + return pigeonDeepEquals(playerId, that.playerId); } @Override public int hashCode() { - return Objects.hash(playerId); + Object[] fields = new Object[] {getClass(), playerId}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PrimaryPlayerChangedEvent{" + "playerId=" + playerId + "}"; } public static final class Builder { @@ -2131,12 +2363,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlayerStateUpdateEvent that = (PlayerStateUpdateEvent) o; - return playerId.equals(that.playerId) && snapshot.equals(that.snapshot); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(snapshot, that.snapshot); } @Override public int hashCode() { - return Objects.hash(playerId, snapshot); + Object[] fields = new Object[] {getClass(), playerId, snapshot}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PlayerStateUpdateEvent{" + "playerId=" + playerId + ", " + "snapshot=" + snapshot + "}"; } public static final class Builder { @@ -2216,12 +2454,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PositionDiscontinuityEvent that = (PositionDiscontinuityEvent) o; - return playerId.equals(that.playerId) && Objects.equals(playbackPositionMs, that.playbackPositionMs); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(playbackPositionMs, that.playbackPositionMs); } @Override public int hashCode() { - return Objects.hash(playerId, playbackPositionMs); + Object[] fields = new Object[] {getClass(), playerId, playbackPositionMs}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PositionDiscontinuityEvent{" + "playerId=" + playerId + ", " + "playbackPositionMs=" + playbackPositionMs + "}"; } public static final class Builder { @@ -2317,12 +2561,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlaybackStateChangedEvent that = (PlaybackStateChangedEvent) o; - return playerId.equals(that.playerId) && playbackState.equals(that.playbackState) && isBuffering.equals(that.isBuffering); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(playbackState, that.playbackState) && pigeonDeepEquals(isBuffering, that.isBuffering); } @Override public int hashCode() { - return Objects.hash(playerId, playbackState, isBuffering); + Object[] fields = new Object[] {getClass(), playerId, playbackState, isBuffering}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PlaybackStateChangedEvent{" + "playerId=" + playerId + ", " + "playbackState=" + playbackState + ", " + "isBuffering=" + isBuffering + "}"; } public static final class Builder { @@ -2414,12 +2664,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PlaybackEndedEvent that = (PlaybackEndedEvent) o; - return playerId.equals(that.playerId) && Objects.equals(mediaItem, that.mediaItem); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(mediaItem, that.mediaItem); } @Override public int hashCode() { - return Objects.hash(playerId, mediaItem); + Object[] fields = new Object[] {getClass(), playerId, mediaItem}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PlaybackEndedEvent{" + "playerId=" + playerId + ", " + "mediaItem=" + mediaItem + "}"; } public static final class Builder { @@ -2502,12 +2758,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } PictureInPictureModeChangedEvent that = (PictureInPictureModeChangedEvent) o; - return playerId.equals(that.playerId) && isInPipMode.equals(that.isInPipMode); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(isInPipMode, that.isInPipMode); } @Override public int hashCode() { - return Objects.hash(playerId, isInPipMode); + Object[] fields = new Object[] {getClass(), playerId, isInPipMode}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "PictureInPictureModeChangedEvent{" + "playerId=" + playerId + ", " + "isInPipMode=" + isInPipMode + "}"; } public static final class Builder { @@ -2587,12 +2849,18 @@ public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MediaItemTransitionEvent that = (MediaItemTransitionEvent) o; - return playerId.equals(that.playerId) && Objects.equals(mediaItem, that.mediaItem); + return pigeonDeepEquals(playerId, that.playerId) && pigeonDeepEquals(mediaItem, that.mediaItem); } @Override public int hashCode() { - return Objects.hash(playerId, mediaItem); + Object[] fields = new Object[] {getClass(), playerId, mediaItem}; + return pigeonDeepHashCode(fields); + } + + @Override + public String toString() { + return "MediaItemTransitionEvent{" + "playerId=" + playerId + ", " + "mediaItem=" + mediaItem + "}"; } public static final class Builder { diff --git a/android/src/test/kotlin/media/bcc/bccm_player/PigeonConformanceTest.kt b/android/src/test/kotlin/media/bcc/bccm_player/PigeonConformanceTest.kt new file mode 100644 index 0000000..a99099e --- /dev/null +++ b/android/src/test/kotlin/media/bcc/bccm_player/PigeonConformanceTest.kt @@ -0,0 +1,70 @@ +package media.bcc.bccm_player + +import media.bcc.bccm_player.pigeon.DownloaderApi +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +/** + * Guards on the shape of the Pigeon-generated Kotlin/Java APIs. + * + * Pigeon has silently reshaped these before: v22 -> v28 moved the *Swift* + * `@async` host APIs from completion handlers to `async throws`, which broke + * every hand-written iOS implementation without a single Dart test noticing. + * Android happened to be spared that one, but nothing in `flutter analyze` or + * `flutter test` compiles Kotlin either, so the same class of break would reach + * consumers unchallenged. + * + * Most of the value is in whether this file *compiles* alongside the main + * sources — a `DownloaderApiImpl` that no longer satisfies `DownloaderPigeon` + * fails the Gradle test task before any assertion runs. The reflective checks + * additionally pin the callback style, which a compile would not notice if + * codegen swapped `Result` for something else and the impl were regenerated + * with it. + */ +class PigeonConformanceTest { + + @Test + fun downloaderApiImplImplementsGeneratedInterface() { + assertTrue( + DownloaderApi.DownloaderPigeon::class.java + .isAssignableFrom(DownloaderApiImpl::class.java), + ) + } + + @Test + fun playbackApiImplImplementsGeneratedInterface() { + assertTrue( + PlaybackPlatformApi.PlaybackPlatformPigeon::class.java + .isAssignableFrom(PlaybackApiImpl::class.java), + ) + } + + /** + * The downloader host API stays callback-based on Android, with the + * result type varying by nullability. `getMethod` throws if any signature + * drifts. + */ + @Test + fun downloaderHostApiUsesResultCallbacks() { + val api = DownloaderApi.DownloaderPigeon::class.java + + assertEquals( + Void.TYPE, + api.getMethod( + "startDownload", + DownloaderApi.DownloadConfig::class.java, + DownloaderApi.Result::class.java, + ).returnType, + ) + api.getMethod("getDownloadStatus", String::class.java, DownloaderApi.Result::class.java) + api.getMethod("getDownloads", DownloaderApi.Result::class.java) + // Nullable return -> NullableResult, void return -> VoidResult. These + // three interfaces are easy to conflate and a swap compiles fine on the + // Dart side. + api.getMethod("getDownload", String::class.java, DownloaderApi.NullableResult::class.java) + api.getMethod("removeDownload", String::class.java, DownloaderApi.VoidResult::class.java) + api.getMethod("getFreeDiskSpace", DownloaderApi.Result::class.java) + } +} diff --git a/android/src/test/kotlin/media/bcc/bccm_player/pigeon/PigeonCodecTest.kt b/android/src/test/kotlin/media/bcc/bccm_player/pigeon/PigeonCodecTest.kt new file mode 100644 index 0000000..6c07968 --- /dev/null +++ b/android/src/test/kotlin/media/bcc/bccm_player/pigeon/PigeonCodecTest.kt @@ -0,0 +1,217 @@ +package media.bcc.bccm_player.pigeon + +import media.bcc.bccm_player.pigeon.DownloaderApi.Download +import media.bcc.bccm_player.pigeon.DownloaderApi.DownloadConfig +import media.bcc.bccm_player.pigeon.DownloaderApi.DownloadFailedEvent +import media.bcc.bccm_player.pigeon.DownloaderApi.DownloadRemovedEvent +import media.bcc.bccm_player.pigeon.DownloaderApi.DownloadStatus +import media.bcc.bccm_player.pigeon.DownloaderApi.DownloaderPigeon +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.MediaItem +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.MediaMetadata +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.PlaybackPlatformPigeon +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.PlaybackState +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.PlayerStateSnapshot +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.Track +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.VideoSize +import org.junit.Assert.assertEquals +import org.junit.Test +import java.nio.ByteBuffer + +/** + * Round-trips every shape we care about through the generated codecs. + * + * This pins two things a compile cannot: the **type identifier** each class is + * written with (drift here corrupts data between a Dart side and a native side + * that were regenerated at different times), and the **field order** inside + * `toList`/`fromList` — a reordered field survives encode/decode as a value of + * the wrong property, which no type checker will notice. + * + * The type identifiers asserted here must match + * `example/ios/RunnerTests/PigeonCodecTests.swift`; that pairing is the whole + * point, since a mismatch between platforms is the failure being guarded + * against. + */ +class PigeonCodecTest { + + private val playbackCodec = PlaybackPlatformPigeon.getCodec() + private val downloaderCodec = DownloaderPigeon.getCodec() + + /** + * A full encode/decode through the generated codec. Only valid for shapes + * with no enum fields — see [playerStateSnapshotFieldOrder] for why. + */ + private fun roundTrip(codec: io.flutter.plugin.common.MessageCodec, value: Any): Any? { + val encoded = codec.encodeMessage(value)!! + encoded.rewind() + return codec.decodeMessage(encoded) + } + + private fun typeIdentifier( + codec: io.flutter.plugin.common.MessageCodec, + value: Any, + ): Int { + val encoded: ByteBuffer = codec.encodeMessage(value)!! + encoded.rewind() + return encoded.get().toInt() and 0xFF + } + + @Test + fun mediaItemRoundTrip() { + val item = sampleMediaItem() + val decoded = roundTrip(playbackCodec, item) as MediaItem + + assertEquals(item, decoded) + // Spot-check individual fields too: `equals` is itself generated, so a + // codegen bug could in principle break both symmetrically. + assertEquals("item-id", decoded.id) + assertEquals("https://example.com/stream.m3u8", decoded.url) + assertEquals("Sample title", decoded.metadata?.title) + assertEquals("value", decoded.metadata?.extras?.get("key")) + assertEquals("no", decoded.lastKnownAudioLanguage) + assertEquals("en", decoded.lastKnownSubtitleLanguage) + } + + @Test + fun trackRoundTrip() { + val track = Track.Builder() + .setId("track-1") + .setLabel("Norsk") + .setLanguage("no") + .setFrameRate(null) + .setBitrate(128_000L) + .setWidth(null) + .setHeight(null) + .setDownloaded(false) + .setIsSelected(true) + .build() + + val decoded = roundTrip(playbackCodec, track) as Track + + assertEquals(track, decoded) + assertEquals(true, decoded.isSelected) + assertEquals(false, decoded.downloaded) + assertEquals(128_000L, decoded.bitrate) + } + + /** + * Pins field order via `toList`/`fromList` rather than a full codec + * round-trip, because a **Java-to-Java** codec round-trip is not + * representative for enum-bearing shapes and fails spuriously. + * + * Pigeon's Dart codec overrides `writeValue` to emit every `int` as int64, + * and the generated Java decoder relies on that — it reads an enum index + * with `((Long) value).intValue()`. But Java's own `StandardMessageCodec` + * writes a boxed `Integer` as int32, so encoding here and decoding here + * throws `ClassCastException: Integer cannot be cast to Long`. That + * asymmetry is harmless in production: this codec only ever decodes bytes + * Dart wrote, and only ever encodes bytes Dart will read. It is an artifact + * of the test direction, not a defect — and it predates Pigeon v28. + * + * `fromList` receives already-decoded values, so this still pins the thing + * that matters: which list slot maps to which property. + */ + @Test + fun playerStateSnapshotFieldOrder() { + val snapshot = PlayerStateSnapshot.Builder() + .setPlayerId("player-1") + .setPlaybackState(PlaybackState.PLAYING) + .setIsBuffering(false) + .setIsFullscreen(false) + .setPlaybackSpeed(1.0) + .setVideoSize(VideoSize.Builder().setWidth(1920L).setHeight(1080L).build()) + .setCurrentMediaItem(sampleMediaItem()) + .setPlaybackPositionMs(12_345.0) + .setTextureId(null) + .setVolume(0.8) + .setError(null) + .setSeekableRangeStartMs(1_000.0) + .setSeekableRangeEndMs(99_000.0) + .build() + + val decoded = PlayerStateSnapshot.fromList(snapshot.toList()) + + assertEquals(snapshot, decoded) + assertEquals(PlaybackState.PLAYING, decoded.playbackState) + // Encoding is symmetric even for enum-bearing shapes, so the type + // identifier is still worth pinning here. + assertEquals(140, typeIdentifier(playbackCodec, snapshot)) + // The seekable range is the pair the live-edge UI depends on, and the + // two fields sit next to each other — exactly the shape a field reorder + // would swap unnoticed. + assertEquals(1_000.0, decoded.seekableRangeStartMs!!, 0.0001) + assertEquals(99_000.0, decoded.seekableRangeEndMs!!, 0.0001) + } + + /** + * The first byte of an encoded value is the Pigeon type identifier. These + * are assigned by declaration order in the `.dart` pigeon file, so + * inserting a class in the middle renumbers everything after it. + */ + @Test + fun typeIdentifiersAreStable() { + assertEquals(138, typeIdentifier(playbackCodec, sampleMediaItem())) + assertEquals(139, typeIdentifier(playbackCodec, MediaMetadata.Builder().build())) + assertEquals( + 142, + typeIdentifier(playbackCodec, VideoSize.Builder().setWidth(1L).setHeight(1L).build()), + ) + } + + /** Field order via `toList`/`fromList` — see [playerStateSnapshotFieldOrder]. */ + @Test + fun downloadFieldOrder() { + val download = Download.Builder() + .setKey("download-key") + .setConfig( + DownloadConfig.Builder() + .setUrl("https://example.com/stream.m3u8") + .setMimeType("application/x-mpegURL") + .setTitle("Sample") + .setAudioTrackIds(listOf("no", "en")) + .setVideoTrackIds(listOf("720")) + .setAdditionalData(mapOf("key" to "value")) + .build(), + ) + .setOfflineUrl("file:///offline/stream") + .setFractionDownloaded(0.42) + .setStatus(DownloadStatus.DOWNLOADING) + .setError(null) + .build() + + val decoded = Download.fromList(download.toList()) + + assertEquals(download, decoded) + assertEquals(DownloadStatus.DOWNLOADING, decoded.status) + assertEquals(0.42, decoded.fractionDownloaded, 0.0001) + assertEquals(2, decoded.config.audioTrackIds.size) + } + + @Test + fun downloadEventsRoundTrip() { + val removed = DownloadRemovedEvent.Builder().setKey("gone").build() + assertEquals(removed, roundTrip(downloaderCodec, removed)) + + val failed = DownloadFailedEvent.Builder().setKey("bad").setError("network").build() + assertEquals(failed, roundTrip(downloaderCodec, failed)) + } + + private fun sampleMediaItem(): MediaItem = MediaItem.Builder() + .setId("item-id") + .setUrl("https://example.com/stream.m3u8") + .setMimeType("application/x-mpegURL") + .setMetadata( + MediaMetadata.Builder() + .setArtworkUri("https://example.com/art.jpg") + .setTitle("Sample title") + .setArtist("Sample artist") + .setDurationMs(60_000.0) + .setExtras(mapOf("key" to "value")) + .build(), + ) + .setIsLive(true) + .setIsOffline(false) + .setPlaybackStartPositionMs(42.0) + .setLastKnownAudioLanguage("no") + .setLastKnownSubtitleLanguage("en") + .build() +} diff --git a/android/src/test/kotlin/media/bcc/bccm_player/pigeon/PigeonEnumOrdinalTest.kt b/android/src/test/kotlin/media/bcc/bccm_player/pigeon/PigeonEnumOrdinalTest.kt new file mode 100644 index 0000000..330eaaa --- /dev/null +++ b/android/src/test/kotlin/media/bcc/bccm_player/pigeon/PigeonEnumOrdinalTest.kt @@ -0,0 +1,90 @@ +package media.bcc.bccm_player.pigeon + +import media.bcc.bccm_player.pigeon.DownloaderApi.DownloadStatus +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.BufferMode +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.CastConnectionState +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.PlaybackState +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.RepeatMode +import media.bcc.bccm_player.pigeon.PlaybackPlatformApi.TrackType +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * Pigeon encodes enums **by index**, not by name. Reordering a case in + * the `pigeons` directory therefore changes the wire value on every platform while + * everything still compiles and every regenerated side still agrees with + * itself — so a consumer running a stale native build silently gets + * `PLAYING` where `PAUSED` was meant. + * + * These are tripwires, not descriptions: if one fails, either the change was a + * mistake, or it is deliberate and the matching Dart and Swift assertions + * (`example/ios/RunnerTests/PigeonEnumOrdinalTests.swift`) must be updated in + * the same commit. + */ +class PigeonEnumOrdinalTest { + @Test + fun bufferModeOrdinals() { + assertEquals(0, BufferMode.STANDARD.ordinal) + assertEquals(1, BufferMode.FAST_START_SHORT_FORM.ordinal) + assertEquals(2, BufferMode.entries.size) + } + + @Test + fun repeatModeOrdinals() { + assertEquals(0, RepeatMode.OFF.ordinal) + assertEquals(1, RepeatMode.ONE.ordinal) + assertEquals(2, RepeatMode.entries.size) + } + + @Test + fun playbackStateOrdinals() { + assertEquals(0, PlaybackState.STOPPED.ordinal) + assertEquals(1, PlaybackState.PAUSED.ordinal) + assertEquals(2, PlaybackState.PLAYING.ordinal) + assertEquals(3, PlaybackState.entries.size) + } + + @Test + fun castConnectionStateOrdinals() { + assertEquals(0, CastConnectionState.NONE.ordinal) + assertEquals(1, CastConnectionState.NO_DEVICES_AVAILABLE.ordinal) + assertEquals(2, CastConnectionState.NOT_CONNECTED.ordinal) + assertEquals(3, CastConnectionState.CONNECTING.ordinal) + assertEquals(4, CastConnectionState.CONNECTED.ordinal) + assertEquals(5, CastConnectionState.entries.size) + } + + @Test + fun trackTypeOrdinals() { + assertEquals(0, TrackType.AUDIO.ordinal) + assertEquals(1, TrackType.TEXT.ordinal) + assertEquals(2, TrackType.VIDEO.ordinal) + assertEquals(3, TrackType.entries.size) + } + + @Test + fun downloadStatusOrdinals() { + assertEquals(0, DownloadStatus.DOWNLOADING.ordinal) + assertEquals(1, DownloadStatus.PAUSED.ordinal) + assertEquals(2, DownloadStatus.FINISHED.ordinal) + assertEquals(3, DownloadStatus.FAILED.ordinal) + assertEquals(4, DownloadStatus.QUEUED.ordinal) + assertEquals(5, DownloadStatus.REMOVING.ordinal) + assertEquals(6, DownloadStatus.entries.size) + } + + /** + * Pigeon's generated `index` field is what actually goes on the wire. It is + * assigned from declaration order, so it should always track `ordinal` — but + * they are two independent pieces of codegen, so pin them to each other. + */ + @Test + fun wireIndexMatchesOrdinal() { + PlaybackState.entries.forEach { assertEquals(it.ordinal, it.index) } + TrackType.entries.forEach { assertEquals(it.ordinal, it.index) } + CastConnectionState.entries.forEach { assertEquals(it.ordinal, it.index) } + BufferMode.entries.forEach { assertEquals(it.ordinal, it.index) } + RepeatMode.entries.forEach { assertEquals(it.ordinal, it.index) } + DownloadStatus.entries.forEach { assertEquals(it.ordinal, it.index) } + } +} diff --git a/doc/contributing/swift-package-manager-plan.md b/doc/contributing/swift-package-manager-plan.md index cb71696..f86af47 100644 --- a/doc/contributing/swift-package-manager-plan.md +++ b/doc/contributing/swift-package-manager-plan.md @@ -14,7 +14,8 @@ until then. Mechanically the migration is "move `ios/Classes/` into `ios/bccm_player/Sources/bccm_player/` and add a `Package.swift`". Two things make it more than that for this plugin: SPM forbids mixed ObjC/Swift targets -(we have 7 ObjC files), and none of our native dependencies ship SPM today. +(we have 7 ObjC files), and the Google Cast SDK has no official SPM +distribution. iOS only — there is no `macos/` directory in this plugin. @@ -31,18 +32,27 @@ bccm_player → google-cast-sdk 4.8.3 → Protobuf ~>3.13 |---|---|---| | `google-cast-sdk` | No official SPM from Google ([issue open since 2019](https://issuetracker.google.com/issues/141729360)) | Community wrapper [`SRGSSR/google-cast-sdk`](https://github.com/SRGSSR/google-cast-sdk) (4.8.4, `GoogleCast` xcframework, **iOS 15+**), or vendor our own `.binaryTarget(url:checksum:)` against Google's official XCFramework zip | | `Protobuf` | n/a | Disappears — only a transitive dep of the cast *pod*; the xcframework doesn't need it | -| `NpawPluginPkg` + `GCDWebServer` | NPAW has a `Package.swift` in their `plugin-ios` repo, but it's hosted on **private Bitbucket** | **Open question — ask NPAW** whether they publish a publicly-resolvable SPM URL | +| `NpawPluginPkg` + `GCDWebServer` | **Ships a real SPM package** at `https://bitbucket.org/npaw/plugin-ios.git` | Depend on it directly — see below | -**The NPAW item can block the whole migration.** A `Package.swift` dependency on -a credentialed git remote is a non-starter for a public pub.dev package — every -consumer would need NPAW Bitbucket credentials at resolve time. If NPAW has no -public SPM endpoint, the options are: +**NPAW is not a blocker** (verified 2026-09-11, anonymously with an isolated +`HOME`, so no cached credentials were in play): -- mirror their XCFramework ourselves as a `binaryTarget`, or -- make NPAW optional (conditional compilation / app-provided) so SPM builds - don't require it. +- `bitbucket.org/npaw/plugin-ios.git` clones anonymously, carries 108 semver + tags, and **7.3.6 — the exact version we pin — has a `Package.swift`**. +- That manifest declares no external dependencies. `GCDWebServer` is vendored + as one of its own binary targets, so it stops being a separate dependency. +- Its binary targets point at `artifact.plugin.npaw.com`, which serves + anonymously (HTTP 200). +- Products: `NpawPlugin`, `NpawPlugin-Static`, plus Balancer/P2P variants. + `NpawPlugin` is the direct equivalent of today's `NpawPluginPkg` pod. +- Platforms: iOS 13+, so it does not constrain our deployment target. -Ask NPAW first; the answer shapes everything else. +The CocoaPods spec repo (`bitbucket.org/npaw/plugin-ios-cocoapods.git`, +referenced by `source` in `example/ios/Podfile`) is public too — nothing in this +plugin's iOS dependency chain needs credentials. + +That leaves the cast SDK as the only dependency question, and it has a working +answer rather than an open one. ### Deployment target @@ -113,12 +123,13 @@ let package = Package( dependencies: [ .package(name: "FlutterFramework", path: "../FlutterFramework"), .package(url: "https://github.com/SRGSSR/google-cast-sdk", from: "4.8.4"), - // NPAW — pending the question in section 1 + .package(url: "https://bitbucket.org/npaw/plugin-ios.git", from: "7.3.6"), ], targets: [ .target(name: "bccm_player", dependencies: [ .product(name: "FlutterFramework", package: "FlutterFramework"), .product(name: "GoogleCast", package: "google-cast-sdk"), + .product(name: "NpawPlugin", package: "plugin-ios"), ]) ] ) @@ -166,5 +177,7 @@ flutter config --enable-swift-package-manager `.github/workflows/test.yml` runs on `ubuntu-latest` and only does `flutter analyze` / `flutter test`, so it cannot catch iOS build breakage in -either mode. Adding a macOS job that builds the example app both ways is worth -doing as part of this. +either mode. Adding a macOS job that builds the example app both ways — and runs +`make ios-test` — is worth doing as part of this. Every host it needs +(`bitbucket.org/npaw/*`, `artifact.plugin.npaw.com`, CocoaPods trunk) serves +anonymously, so no CI secrets are required. diff --git a/example/ios/Podfile b/example/ios/Podfile index 231959b..2b1f9a1 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -37,10 +37,19 @@ target 'Runner' do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) pod 'NpawPluginPkg', '7.3.6' + + target 'RunnerTests' do + inherit! :search_paths + end end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) + # `@testable import bccm_player` in RunnerTests needs the pod's module + # built with testing enabled. + target.build_configurations.each do |config| + config.build_settings['ENABLE_TESTABILITY'] = 'YES' if config.name == 'Debug' + end end end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 405451f..0440420 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -39,6 +39,6 @@ SPEC CHECKSUMS: NpawPluginPkg: 8c758cbac896f97980065f7b9fff1f62fb093004 Protobuf: 28c89b24435762f60244e691544ed80f50d82701 -PODFILE CHECKSUM: 97fcd148e1093e86d6efeb06748f8663dbfb8f3e +PODFILE CHECKSUM: 849f6a45f5e7c0ef21de3f21ca79a1ad24971194 -COCOAPODS: 1.16.2 +COCOAPODS: 1.17.0 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 74afd33..15bdb11 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,15 +8,30 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 1E06713558C0612C8B9B077E /* PigeonCodecTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DFB461A7F0C7E7277A0964C /* PigeonCodecTests.swift */; }; + 350C43BD50A57D266CA800DF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B01712CCCD160DC1349D8B /* Foundation.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 60B0CB27270D63FD4E935D68 /* PigeonEnumOrdinalTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA413CC156740A4B82CFCF92 /* PigeonEnumOrdinalTests.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + A7A39AC7A6874284500D82D6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 206A8CEA31EF7CF329E901B1 /* Pods_RunnerTests.framework */; }; + C409C58208A84FF9846C890C /* PigeonConformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6D720B4FCBC9FB8C1EE08 /* PigeonConformanceTests.swift */; }; E0EA73AD39E36C2CF67EE3D2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 92DC6938909E66F6E88C0E48 /* Pods_Runner.framework */; }; - 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 92436958F09632A9A8FB33AB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 9705A1C41CF9048500538489 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; @@ -33,11 +48,20 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 206A8CEA31EF7CF329E901B1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 29B01712CCCD160DC1349D8B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 355760C778515FD885481D47 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 77E7F391D2B1A301538C8431 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* bccm_player */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = bccm_player; path = ../../ios/bccm_player; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; + 7AF6D720B4FCBC9FB8C1EE08 /* PigeonConformanceTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PigeonConformanceTests.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7DB1A8A0B81A23B06F59AF59 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 7DFB461A7F0C7E7277A0964C /* PigeonCodecTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PigeonCodecTests.swift; sourceTree = ""; }; 92DC6938909E66F6E88C0E48 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -46,14 +70,23 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9BC8535179ABC227B59304C2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; A90674F5250E2602007380B1 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + AA413CC156740A4B82CFCF92 /* PigeonEnumOrdinalTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = PigeonEnumOrdinalTests.swift; sourceTree = ""; }; D5C7D7CB7CA0A9714AE1A380 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; - 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = Flutter/ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; - 78DABEA22ED26510000E7860 /* bccm_player */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = bccm_player; path = ../../ios/bccm_player; sourceTree = ""; }; + DE581F079F5E9666A52BBF47 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 2D2B2FF0B0C4192986C56607 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 350C43BD50A57D266CA800DF /* Foundation.framework in Frameworks */, + A7A39AC7A6874284500D82D6 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -66,10 +99,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3D67E0B87604569678023542 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 7DFB461A7F0C7E7277A0964C /* PigeonCodecTests.swift */, + 7AF6D720B4FCBC9FB8C1EE08 /* PigeonConformanceTests.swift */, + AA413CC156740A4B82CFCF92 /* PigeonEnumOrdinalTests.swift */, + ); + name = RunnerTests; + path = RunnerTests; + sourceTree = ""; + }; 5C70E04A97865AA6B605144F /* Frameworks */ = { isa = PBXGroup; children = ( 92DC6938909E66F6E88C0E48 /* Pods_Runner.framework */, + 9CEC6A08C7A25EBA7C7B713B /* iOS */, + 206A8CEA31EF7CF329E901B1 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -96,6 +142,7 @@ 97C146EF1CF9000F007C117D /* Products */, 9CB683FB681E88D9C3F4678A /* Pods */, 5C70E04A97865AA6B605144F /* Frameworks */, + 3D67E0B87604569678023542 /* RunnerTests */, ); sourceTree = ""; }; @@ -103,6 +150,7 @@ isa = PBXGroup; children = ( 97C146EE1CF9000F007C117D /* Runner.app */, + 77E7F391D2B1A301538C8431 /* RunnerTests.xctest */, ); name = Products; sourceTree = ""; @@ -128,17 +176,44 @@ 355760C778515FD885481D47 /* Pods-Runner.debug.xcconfig */, D5C7D7CB7CA0A9714AE1A380 /* Pods-Runner.release.xcconfig */, A90674F5250E2602007380B1 /* Pods-Runner.profile.xcconfig */, + DE581F079F5E9666A52BBF47 /* Pods-RunnerTests.release.xcconfig */, + 9BC8535179ABC227B59304C2 /* Pods-RunnerTests.debug.xcconfig */, + 7DB1A8A0B81A23B06F59AF59 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; }; + 9CEC6A08C7A25EBA7C7B713B /* iOS */ = { + isa = PBXGroup; + children = ( + 29B01712CCCD160DC1349D8B /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - packageProductDependencies = ( - 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + 2F27E825270E64CAEB69E13F /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 516583D4F58FC4266D2D3A00 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + F43D752437C57F61B902B31B /* [CP] Check Pods Manifest.lock */, + C724BDEF67C8B1A67F739056 /* Sources */, + 2D2B2FF0B0C4192986C56607 /* Frameworks */, + 053069475AF8042B636CA746 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2ADC9B2BF4B6CE067A70E512 /* PBXTargetDependency */, ); + name = RunnerTests; + productName = RunnerTests; + productReference = 77E7F391D2B1A301538C8431 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -157,6 +232,9 @@ dependencies = ( ); name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); productName = Runner; productReference = 97C146EE1CF9000F007C117D /* Runner.app */; productType = "com.apple.product-type.application"; @@ -165,9 +243,6 @@ /* Begin PBXProject section */ 97C146E61CF9000F007C117D /* Project object */ = { - packageReferences = ( - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, - ); isa = PBXProject; attributes = { LastUpgradeCheck = 1510; @@ -188,16 +263,27 @@ Base, ); mainGroup = 97C146E51CF9000F007C117D; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */, + ); productRefGroup = 97C146EF1CF9000F007C117D /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( 97C146ED1CF9000F007C117D /* Runner */, + 2F27E825270E64CAEB69E13F /* RunnerTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 053069475AF8042B636CA746 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EC1CF9000F007C117D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -299,6 +385,28 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; + F43D752437C57F61B902B31B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -311,8 +419,27 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C724BDEF67C8B1A67F739056 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1E06713558C0612C8B9B077E /* PigeonCodecTests.swift in Sources */, + C409C58208A84FF9846C890C /* PigeonConformanceTests.swift in Sources */, + 60B0CB27270D63FD4E935D68 /* PigeonEnumOrdinalTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 2ADC9B2BF4B6CE067A70E512 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Runner; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 92436958F09632A9A8FB33AB /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -333,6 +460,24 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 0CB22FFD483E0EDCA9F513EC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DE581F079F5E9666A52BBF47 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + ENABLE_TESTABILITY = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = media.bcc.bccmPlayerExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; buildSettings = { @@ -406,6 +551,23 @@ }; name = Profile; }; + 4A5F6C18996AA528470BF267 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9BC8535179ABC227B59304C2 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + ENABLE_TESTABILITY = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = media.bcc.bccmPlayerExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + }; + name = Debug; + }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -560,9 +722,37 @@ }; name = Release; }; + D19CB2B7A09E1161F8D548A0 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DB1A8A0B81A23B06F59AF59 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + ENABLE_TESTABILITY = YES; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + PRODUCT_BUNDLE_IDENTIFIER = media.bcc.bccmPlayerExample.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 516583D4F58FC4266D2D3A00 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 0CB22FFD483E0EDCA9F513EC /* Release */, + 4A5F6C18996AA528470BF267 /* Debug */, + D19CB2B7A09E1161F8D548A0 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -584,12 +774,14 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + /* Begin XCLocalSwiftPackageReference section */ - 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = { isa = XCLocalSwiftPackageReference; relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; }; /* End XCLocalSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { isa = XCSwiftPackageProductDependency; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 66d5857..ac0d71f 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -56,6 +56,16 @@ + + + + UInt8 { + let data = try XCTUnwrap(playbackCodec.encode(value)) + return try XCTUnwrap(data.first) + } + + XCTAssertEqual(try typeIdentifier(of: Self.sampleMediaItem), 138) + XCTAssertEqual( + try typeIdentifier(of: MediaMetadata.make( + withArtworkUri: nil, title: nil, artist: nil, durationMs: nil, extras: nil)), + 139) + XCTAssertEqual( + try typeIdentifier(of: VideoSize.make(withWidth: 1, height: 1)), + 142) + } + + // MARK: - Swift generated types (downloader API) + + func testDownloadRoundTrip() throws { + let codec = DownloaderApiPigeonCodec.shared + let download = Download( + key: "download-key", + config: DownloadConfig( + url: "https://example.com/stream.m3u8", + mimeType: "application/x-mpegURL", + title: "Sample", + audioTrackIds: ["no", "en"], + videoTrackIds: ["720"], + additionalData: ["key": "value"]), + offlineUrl: "file:///offline/stream", + fractionDownloaded: 0.42, + status: .downloading, + error: nil) + + let data = try XCTUnwrap(codec.encode(download)) + let decoded = try XCTUnwrap(codec.decode(data) as? Download) + + XCTAssertEqual(decoded, download) + XCTAssertEqual(decoded.status, .downloading) + XCTAssertEqual(decoded.fractionDownloaded, 0.42, accuracy: 0.0001) + XCTAssertEqual(decoded.config.audioTrackIds.count, 2) + } + + func testDownloadEventsRoundTrip() throws { + let codec = DownloaderApiPigeonCodec.shared + + let removed = DownloadRemovedEvent(key: "gone") + let removedData = try XCTUnwrap(codec.encode(removed)) + XCTAssertEqual(try XCTUnwrap(codec.decode(removedData) as? DownloadRemovedEvent), removed) + + let failed = DownloadFailedEvent(key: "bad", error: "network") + let failedData = try XCTUnwrap(codec.encode(failed)) + XCTAssertEqual(try XCTUnwrap(codec.decode(failedData) as? DownloadFailedEvent), failed) + } + + // MARK: - Fixtures + + static let sampleMediaItem = MediaItem.make( + withId: "item-id", + url: "https://example.com/stream.m3u8", + mimeType: "application/x-mpegURL", + metadata: MediaMetadata.make( + withArtworkUri: "https://example.com/art.jpg", + title: "Sample title", + artist: "Sample artist", + durationMs: NSNumber(value: 60_000.0), + extras: ["key": "value"]), + isLive: NSNumber(value: true), + isOffline: NSNumber(value: false), + playbackStartPositionMs: NSNumber(value: 42.0), + lastKnownAudioLanguage: "no", + lastKnownSubtitleLanguage: "en") +} diff --git a/example/ios/RunnerTests/PigeonConformanceTests.swift b/example/ios/RunnerTests/PigeonConformanceTests.swift new file mode 100644 index 0000000..d5f4ccf --- /dev/null +++ b/example/ios/RunnerTests/PigeonConformanceTests.swift @@ -0,0 +1,74 @@ +import XCTest + +@testable import bccm_player + +/// Compile-time guards on the shape of the Pigeon-generated Swift APIs. +/// +/// Pigeon has silently reshaped these before: v22 -> v28 moved `@async` host +/// APIs from completion handlers to `async throws`, which broke every +/// hand-written implementation without a single Dart test noticing. Nothing +/// in `flutter test` or `flutter analyze` compiles Swift, so the build was +/// the only thing that caught it — and only if someone happened to build iOS. +/// +/// These references make that breakage a test-suite failure instead. Most of +/// the value is in whether this file *compiles*; the assertions are almost +/// incidental. +final class PigeonConformanceTests: XCTestCase { + /// `DownloaderApiImpl` must satisfy the generated `DownloaderPigeon` + /// protocol. This is exactly what broke on the v28 regeneration. + func testDownloaderApiImplConformsToGeneratedProtocol() { + XCTAssertTrue(DownloaderApiImpl.self is DownloaderPigeon.Type) + } + + /// `PlaybackApiImpl` must satisfy the generated (Objective-C) + /// `PlaybackPlatformPigeon` protocol. + func testPlaybackApiImplConformsToGeneratedProtocol() { + XCTAssertTrue(PlaybackApiImpl.self is PlaybackPlatformPigeon.Type) + } + + /// The downloader listener must stay `async throws`. `SwiftBccmPlayerPlugin` + /// calls these from synchronous Combine sinks via `Task { try? await ... }`; + /// if codegen reverts to completion handlers this stops compiling here as + /// well as there. + func testDownloaderListenerIsAsync() async throws { + func requireAsyncShape(_ listener: any DownloaderListenerPigeonProtocol) async throws { + try await listener.onDownloadStatusChanged( + event: DownloadChangedEvent(download: Self.sampleDownload)) + try await listener.onDownloadRemoved( + event: DownloadRemovedEvent(key: "key")) + try await listener.onDownloadFailed( + event: DownloadFailedEvent(key: "key", error: "boom")) + } + XCTAssertNotNil(requireAsyncShape) + } + + /// The downloader host API must stay `async throws` and keep its return + /// types. Referencing every method pins the full signature set. + func testDownloaderHostApiIsAsync() async throws { + func requireAsyncShape(_ api: any DownloaderPigeon) async throws { + _ = try await api.startDownload(downloadConfig: Self.sampleConfig) + _ = try await api.getDownloadStatus(downloadKey: "key") + _ = try await api.getDownloads() + _ = try await api.getDownload(downloadKey: "key") + try await api.removeDownload(downloadKey: "key") + _ = try await api.getFreeDiskSpace() + } + XCTAssertNotNil(requireAsyncShape) + } + + static let sampleConfig = DownloadConfig( + url: "https://example.com/stream.m3u8", + mimeType: "application/x-mpegURL", + title: "Sample", + audioTrackIds: ["no"], + videoTrackIds: ["720"], + additionalData: ["k": "v"]) + + static let sampleDownload = Download( + key: "key", + config: sampleConfig, + offlineUrl: nil, + fractionDownloaded: 0.5, + status: .downloading, + error: nil) +} diff --git a/example/ios/RunnerTests/PigeonEnumOrdinalTests.swift b/example/ios/RunnerTests/PigeonEnumOrdinalTests.swift new file mode 100644 index 0000000..60eb78e --- /dev/null +++ b/example/ios/RunnerTests/PigeonEnumOrdinalTests.swift @@ -0,0 +1,59 @@ +import XCTest + +@testable import bccm_player + +/// Pigeon encodes enums **by index**, not by name. Reordering a case in +/// `pigeons/*.dart` therefore changes the wire value on every platform while +/// everything still compiles and every regenerated side still agrees with +/// itself — so a consumer running a stale native build silently gets +/// `playing` where `paused` was meant. +/// +/// These are tripwires, not descriptions: if one fails, either the change was +/// a mistake, or it is deliberate and the matching Dart and Kotlin +/// assertions must be updated in the same commit. +final class PigeonEnumOrdinalTests: XCTestCase { + func testBufferModeOrdinals() { + XCTAssertEqual(BufferMode.standard.rawValue, 0) + XCTAssertEqual(BufferMode.fastStartShortForm.rawValue, 1) + } + + func testRepeatModeOrdinals() { + XCTAssertEqual(RepeatMode.off.rawValue, 0) + XCTAssertEqual(RepeatMode.one.rawValue, 1) + } + + func testPlaybackStateOrdinals() { + XCTAssertEqual(PlaybackState.stopped.rawValue, 0) + XCTAssertEqual(PlaybackState.paused.rawValue, 1) + XCTAssertEqual(PlaybackState.playing.rawValue, 2) + } + + func testCastConnectionStateOrdinals() { + let noDevices: CastConnectionState = .noDevicesAvailable + let notConnected: CastConnectionState = .notConnected + let connecting: CastConnectionState = .connecting + let connected: CastConnectionState = .connected + XCTAssertEqual(CastConnectionState(rawValue: 0), CastConnectionState.none) + XCTAssertEqual(noDevices.rawValue, 1) + XCTAssertEqual(notConnected.rawValue, 2) + XCTAssertEqual(connecting.rawValue, 3) + XCTAssertEqual(connected.rawValue, 4) + } + + func testTrackTypeOrdinals() { + XCTAssertEqual(TrackType.audio.rawValue, 0) + XCTAssertEqual(TrackType.text.rawValue, 1) + XCTAssertEqual(TrackType.video.rawValue, 2) + } + + func testDownloadStatusOrdinals() { + XCTAssertEqual(DownloadStatus.downloading.rawValue, 0) + XCTAssertEqual(DownloadStatus.paused.rawValue, 1) + XCTAssertEqual(DownloadStatus.finished.rawValue, 2) + XCTAssertEqual(DownloadStatus.failed.rawValue, 3) + XCTAssertEqual(DownloadStatus.queued.rawValue, 4) + XCTAssertEqual(DownloadStatus.removing.rawValue, 5) + // Catches a case being added or removed rather than reordered. + XCTAssertEqual(DownloadStatus.allCases.count, 6) + } +} diff --git a/ios/Classes/DownloaderApiImpl.swift b/ios/Classes/DownloaderApiImpl.swift index 1cfc246..e487830 100644 --- a/ios/Classes/DownloaderApiImpl.swift +++ b/ios/Classes/DownloaderApiImpl.swift @@ -6,50 +6,28 @@ class DownloaderApiImpl: NSObject, DownloaderPigeon { self.downloader = downloader } - func startDownload(downloadConfig: DownloadConfig, completion: @escaping (Result) -> Void) { - Task { - do { - let download = try await downloader.startDownload(config: downloadConfig) - completion(.success(download)) - } catch { - completion(.failure(error)) - } - } + func startDownload(downloadConfig: DownloadConfig) async throws -> Download { + return try await downloader.startDownload(config: downloadConfig) } - func getDownloadStatus(downloadKey: String, completion: @escaping (Result) -> Void) { - Task { - do { - let progress = try await downloader.progress(forKey: downloadKey) - completion(.success(progress)) - } catch { - completion(.failure(error)) - } - } + func getDownloadStatus(downloadKey: String) async throws -> Double { + return try await downloader.progress(forKey: downloadKey) } - func getDownloads(completion: @escaping (Result<[Download], Error>) -> Void) { - completion(Result(catching: { - downloader.getAll() - })) + func getDownloads() async throws -> [Download] { + return downloader.getAll() } - func getDownload(downloadKey: String, completion: @escaping (Result) -> Void) { - completion(Result(catching: { - downloader.get(forKey: downloadKey) - })) + func getDownload(downloadKey: String) async throws -> Download? { + return downloader.get(forKey: downloadKey) } - func removeDownload(downloadKey: String, completion: @escaping (Result) -> Void) { - completion(Result(catching: { - try downloader.remove(download: downloadKey) - })) + func removeDownload(downloadKey: String) async throws { + try downloader.remove(download: downloadKey) } - func getFreeDiskSpace(completion: @escaping (Result) -> Void) { - completion(Result(catching: { - try _getFreeDiskSpace() - })) + func getFreeDiskSpace() async throws -> Double { + return try _getFreeDiskSpace() } private func _getFreeDiskSpace() throws -> Double { diff --git a/ios/Classes/Pigeon/ChromecastPigeon.h b/ios/Classes/Pigeon/ChromecastPigeon.h index 7251b38..62fb0c4 100644 --- a/ios/Classes/Pigeon/ChromecastPigeon.h +++ b/ios/Classes/Pigeon/ChromecastPigeon.h @@ -1,7 +1,7 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; diff --git a/ios/Classes/Pigeon/ChromecastPigeon.m b/ios/Classes/Pigeon/ChromecastPigeon.m index 704c37d..833e761 100644 --- a/ios/Classes/Pigeon/ChromecastPigeon.m +++ b/ios/Classes/Pigeon/ChromecastPigeon.m @@ -1,17 +1,104 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "ChromecastPigeon.h" #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import +@import Flutter; #endif -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} + static FlutterError *createConnectionError(NSString *channelName) { return [FlutterError errorWithCode:@"channel-error" message:[NSString stringWithFormat:@"%@/%@/%@", @"Unable to establish connection on channel: '", channelName, @"'."] details:@""]; @@ -47,6 +134,25 @@ + (nullable CastSessionUnavailableEvent *)nullableFromList:(NSArray *)list { self.playbackPositionMs ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + CastSessionUnavailableEvent *other = (CastSessionUnavailableEvent *)object; + return FLTPigeonDeepEquals(self.playbackPositionMs, other.playbackPositionMs); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playbackPositionMs); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"CastSessionUnavailableEvent(playbackPositionMs: %@)", self.playbackPositionMs]; +} @end @interface nullChromecastPigeonPigeonCodecReader : FlutterStandardReader diff --git a/ios/Classes/Pigeon/DownloaderApi.swift b/ios/Classes/Pigeon/DownloaderApi.swift index 41cdc63..63c557f 100644 --- a/ios/Classes/Pigeon/DownloaderApi.swift +++ b/ios/Classes/Pigeon/DownloaderApi.swift @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -15,9 +15,9 @@ import Foundation final class PigeonError: Error { let code: String let message: String? - let details: Any? + let details: Sendable? - init(code: String, message: String?, details: Any?) { + init(code: String, message: String?, details: Sendable?) { self.code = code self.message = message self.details = details @@ -26,7 +26,7 @@ final class PigeonError: Error { var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" - } + } } private func wrapResult(_ result: Any?) -> [Any?] { @@ -50,7 +50,7 @@ private func wrapError(_ error: Any) -> [Any?] { } return [ "\(error)", - "\(type(of: error))", + "\(Swift.type(of: error))", "Stacktrace: \(Thread.callStackSymbols)", ] } @@ -59,8 +59,127 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") } -private func isNullish(_ value: Any?) -> Bool { - return value is NSNull || value == nil +enum DownloaderApiPigeonInternal { + static func isNullish(_ value: Any?) -> Bool { + guard let innerValue = value else { + return true + } + + if case Optional.some(Optional.none) = value { + return true + } + + return innerValue is NSNull + } + static func doubleEquals(_ lhs: Double, _ rhs: Double) -> Bool { + return (lhs.isNaN && rhs.isNaN) || lhs == rhs + } + + static func doubleHash(_ value: Double, _ hasher: inout Hasher) { + if value.isNaN { + hasher.combine(0x7FF8000000000000) + } else { + // Normalize -0.0 to 0.0 + hasher.combine(value == 0 ? 0 : value) + } + } + + static func deepEquals(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case (let lhs as AnyObject, let rhs as AnyObject) where lhs === rhs: + return true + + case is (Void, Void): + return true + + case (let lhsArray, let rhsArray) as ([Any?], [Any?]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !deepEquals(element, rhsArray[index]) { + return false + } + } + return true + + case (let lhsArray, let rhsArray) as ([Double], [Double]): + guard lhsArray.count == rhsArray.count else { return false } + for (index, element) in lhsArray.enumerated() { + if !doubleEquals(element, rhsArray[index]) { + return false + } + } + return true + + case (let lhsDictionary, let rhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard lhsDictionary.count == rhsDictionary.count else { return false } + for (lhsKey, lhsValue) in lhsDictionary { + var found = false + for (rhsKey, rhsValue) in rhsDictionary { + if deepEquals(lhsKey, rhsKey) { + if deepEquals(lhsValue, rhsValue) { + found = true + break + } else { + return false + } + } + } + if !found { return false } + } + return true + + case (let lhs as Double, let rhs as Double): + return doubleEquals(lhs, rhs) + + case (let lhsHashable, let rhsHashable) as (AnyHashable, AnyHashable): + return lhsHashable == rhsHashable + + default: + return false + } + } + + static func deepHash(value: Any?, hasher: inout Hasher) { + let cleanValue = nilOrValue(value) as Any? + if let cleanValue = cleanValue { + if let doubleValue = cleanValue as? Double { + doubleHash(doubleValue, &hasher) + } else if let valueList = cleanValue as? [Any?] { + for item in valueList { + deepHash(value: item, hasher: &hasher) + } + } else if let valueList = cleanValue as? [Double] { + for item in valueList { + doubleHash(item, &hasher) + } + } else if let valueDict = cleanValue as? [AnyHashable: Any?] { + var result = 0 + for (key, value) in valueDict { + var entryKeyHasher = Hasher() + deepHash(value: key, hasher: &entryKeyHasher) + var entryValueHasher = Hasher() + deepHash(value: value, hasher: &entryValueHasher) + result = result &+ ((entryKeyHasher.finalize() &* 31) ^ entryValueHasher.finalize()) + } + hasher.combine(result) + } else if let hashableValue = cleanValue as? AnyHashable { + hasher.combine(hashableValue) + } else { + hasher.combine(String(describing: cleanValue)) + } + } else { + hasher.combine(0) + } + } + } private func nilOrValue(_ value: Any?) -> T? { @@ -68,7 +187,8 @@ private func nilOrValue(_ value: Any?) -> T? { return value as! T? } -enum DownloadStatus: Int { + +enum DownloadStatus: Int, CaseIterable { case downloading = 0 case paused = 1 case finished = 2 @@ -78,7 +198,7 @@ enum DownloadStatus: Int { } /// Generated class from Pigeon that represents data sent in messages. -struct DownloadConfig { +struct DownloadConfig: Hashable, CustomStringConvertible { var url: String var mimeType: String var title: String @@ -115,10 +235,30 @@ struct DownloadConfig { additionalData, ] } + static func == (lhs: DownloadConfig, rhs: DownloadConfig) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return DownloaderApiPigeonInternal.deepEquals(lhs.url, rhs.url) && DownloaderApiPigeonInternal.deepEquals(lhs.mimeType, rhs.mimeType) && DownloaderApiPigeonInternal.deepEquals(lhs.title, rhs.title) && DownloaderApiPigeonInternal.deepEquals(lhs.audioTrackIds, rhs.audioTrackIds) && DownloaderApiPigeonInternal.deepEquals(lhs.videoTrackIds, rhs.videoTrackIds) && DownloaderApiPigeonInternal.deepEquals(lhs.additionalData, rhs.additionalData) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("DownloadConfig") + DownloaderApiPigeonInternal.deepHash(value: url, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: mimeType, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: title, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: audioTrackIds, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: videoTrackIds, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: additionalData, hasher: &hasher) + } + + public var description: String { + return "DownloadConfig(url: \(String(describing: url)), mimeType: \(String(describing: mimeType)), title: \(String(describing: title)), audioTrackIds: \(String(describing: audioTrackIds)), videoTrackIds: \(String(describing: videoTrackIds)), additionalData: \(String(describing: additionalData)))" + } } /// Generated class from Pigeon that represents data sent in messages. -struct Download { +struct Download: Hashable, CustomStringConvertible { var key: String var config: DownloadConfig var offlineUrl: String? = nil @@ -155,10 +295,30 @@ struct Download { error, ] } + static func == (lhs: Download, rhs: Download) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return DownloaderApiPigeonInternal.deepEquals(lhs.key, rhs.key) && DownloaderApiPigeonInternal.deepEquals(lhs.config, rhs.config) && DownloaderApiPigeonInternal.deepEquals(lhs.offlineUrl, rhs.offlineUrl) && DownloaderApiPigeonInternal.deepEquals(lhs.fractionDownloaded, rhs.fractionDownloaded) && DownloaderApiPigeonInternal.deepEquals(lhs.status, rhs.status) && DownloaderApiPigeonInternal.deepEquals(lhs.error, rhs.error) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("Download") + DownloaderApiPigeonInternal.deepHash(value: key, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: config, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: offlineUrl, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: fractionDownloaded, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: status, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: error, hasher: &hasher) + } + + public var description: String { + return "Download(key: \(String(describing: key)), config: \(String(describing: config)), offlineUrl: \(String(describing: offlineUrl)), fractionDownloaded: \(String(describing: fractionDownloaded)), status: \(String(describing: status)), error: \(String(describing: error)))" + } } /// Generated class from Pigeon that represents data sent in messages. -struct DownloadFailedEvent { +struct DownloadFailedEvent: Hashable, CustomStringConvertible { var key: String var error: String? = nil @@ -179,10 +339,26 @@ struct DownloadFailedEvent { error, ] } + static func == (lhs: DownloadFailedEvent, rhs: DownloadFailedEvent) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return DownloaderApiPigeonInternal.deepEquals(lhs.key, rhs.key) && DownloaderApiPigeonInternal.deepEquals(lhs.error, rhs.error) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("DownloadFailedEvent") + DownloaderApiPigeonInternal.deepHash(value: key, hasher: &hasher) + DownloaderApiPigeonInternal.deepHash(value: error, hasher: &hasher) + } + + public var description: String { + return "DownloadFailedEvent(key: \(String(describing: key)), error: \(String(describing: error)))" + } } /// Generated class from Pigeon that represents data sent in messages. -struct DownloadRemovedEvent { +struct DownloadRemovedEvent: Hashable, CustomStringConvertible { var key: String @@ -199,10 +375,25 @@ struct DownloadRemovedEvent { key ] } + static func == (lhs: DownloadRemovedEvent, rhs: DownloadRemovedEvent) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return DownloaderApiPigeonInternal.deepEquals(lhs.key, rhs.key) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("DownloadRemovedEvent") + DownloaderApiPigeonInternal.deepHash(value: key, hasher: &hasher) + } + + public var description: String { + return "DownloadRemovedEvent(key: \(String(describing: key)))" + } } /// Generated class from Pigeon that represents data sent in messages. -struct DownloadChangedEvent { +struct DownloadChangedEvent: Hashable, CustomStringConvertible { var download: Download @@ -219,6 +410,21 @@ struct DownloadChangedEvent { download ] } + static func == (lhs: DownloadChangedEvent, rhs: DownloadChangedEvent) -> Bool { + if Swift.type(of: lhs) != Swift.type(of: rhs) { + return false + } + return DownloaderApiPigeonInternal.deepEquals(lhs.download, rhs.download) + } + + func hash(into hasher: inout Hasher) { + hasher.combine("DownloadChangedEvent") + DownloaderApiPigeonInternal.deepHash(value: download, hasher: &hasher) + } + + public var description: String { + return "DownloadChangedEvent(download: \(String(describing: download)))" + } } private class DownloaderApiPigeonCodecReader: FlutterStandardReader { @@ -291,13 +497,13 @@ class DownloaderApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable /// /// Generated protocol from Pigeon that represents a handler of messages from Flutter. protocol DownloaderPigeon { - func startDownload(downloadConfig: DownloadConfig, completion: @escaping (Result) -> Void) - func getDownloadStatus(downloadKey: String, completion: @escaping (Result) -> Void) - func getDownloads(completion: @escaping (Result<[Download], Error>) -> Void) - func getDownload(downloadKey: String, completion: @escaping (Result) -> Void) - func removeDownload(downloadKey: String, completion: @escaping (Result) -> Void) + func startDownload(downloadConfig: DownloadConfig) async throws -> Download + func getDownloadStatus(downloadKey: String) async throws -> Double + func getDownloads() async throws -> [Download] + func getDownload(downloadKey: String) async throws -> Download? + func removeDownload(downloadKey: String) async throws /// Returns free space in bytes - func getFreeDiskSpace(completion: @escaping (Result) -> Void) + func getFreeDiskSpace() async throws -> Double } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -311,11 +517,11 @@ class DownloaderPigeonSetup { startDownloadChannel.setMessageHandler { message, reply in let args = message as! [Any?] let downloadConfigArg = args[0] as! DownloadConfig - api.startDownload(downloadConfig: downloadConfigArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): + Task { @MainActor in + do { + let result = try await api.startDownload(downloadConfig: downloadConfigArg) + reply(wrapResult(result)) + } catch { reply(wrapError(error)) } } @@ -328,11 +534,11 @@ class DownloaderPigeonSetup { getDownloadStatusChannel.setMessageHandler { message, reply in let args = message as! [Any?] let downloadKeyArg = args[0] as! String - api.getDownloadStatus(downloadKey: downloadKeyArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): + Task { @MainActor in + do { + let result = try await api.getDownloadStatus(downloadKey: downloadKeyArg) + reply(wrapResult(result)) + } catch { reply(wrapError(error)) } } @@ -343,11 +549,11 @@ class DownloaderPigeonSetup { let getDownloadsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownloads\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getDownloadsChannel.setMessageHandler { _, reply in - api.getDownloads { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): + Task { @MainActor in + do { + let result = try await api.getDownloads() + reply(wrapResult(result)) + } catch { reply(wrapError(error)) } } @@ -360,11 +566,11 @@ class DownloaderPigeonSetup { getDownloadChannel.setMessageHandler { message, reply in let args = message as! [Any?] let downloadKeyArg = args[0] as! String - api.getDownload(downloadKey: downloadKeyArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): + Task { @MainActor in + do { + let result = try await api.getDownload(downloadKey: downloadKeyArg) + reply(wrapResult(result)) + } catch { reply(wrapError(error)) } } @@ -377,11 +583,11 @@ class DownloaderPigeonSetup { removeDownloadChannel.setMessageHandler { message, reply in let args = message as! [Any?] let downloadKeyArg = args[0] as! String - api.removeDownload(downloadKey: downloadKeyArg) { result in - switch result { - case .success: + Task { @MainActor in + do { + try await api.removeDownload(downloadKey: downloadKeyArg) reply(wrapResult(nil)) - case .failure(let error): + } catch { reply(wrapError(error)) } } @@ -393,11 +599,11 @@ class DownloaderPigeonSetup { let getFreeDiskSpaceChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.bccm_player.DownloaderPigeon.getFreeDiskSpace\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) if let api = api { getFreeDiskSpaceChannel.setMessageHandler { _, reply in - api.getFreeDiskSpace { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): + Task { @MainActor in + do { + let result = try await api.getFreeDiskSpace() + reply(wrapResult(result)) + } catch { reply(wrapError(error)) } } @@ -407,11 +613,12 @@ class DownloaderPigeonSetup { } } } + /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol DownloaderListenerPigeonProtocol { - func onDownloadStatusChanged(event eventArg: DownloadChangedEvent, completion: @escaping (Result) -> Void) - func onDownloadRemoved(event eventArg: DownloadRemovedEvent, completion: @escaping (Result) -> Void) - func onDownloadFailed(event eventArg: DownloadFailedEvent, completion: @escaping (Result) -> Void) + func onDownloadStatusChanged(event eventArg: DownloadChangedEvent) async throws + func onDownloadRemoved(event eventArg: DownloadRemovedEvent) async throws + func onDownloadFailed(event eventArg: DownloadFailedEvent) async throws } class DownloaderListenerPigeon: DownloaderListenerPigeonProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -423,57 +630,63 @@ class DownloaderListenerPigeon: DownloaderListenerPigeonProtocol { var codec: DownloaderApiPigeonCodec { return DownloaderApiPigeonCodec.shared } - func onDownloadStatusChanged(event eventArg: DownloadChangedEvent, completion: @escaping (Result) -> Void) { - let channelName: String = "dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadStatusChanged\(messageChannelSuffix)" - let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([eventArg] as [Any?]) { response in - guard let listResponse = response as? [Any?] else { - completion(.failure(createConnectionError(withChannelName: channelName))) - return - } - if listResponse.count > 1 { - let code: String = listResponse[0] as! String - let message: String? = nilOrValue(listResponse[1]) - let details: String? = nilOrValue(listResponse[2]) - completion(.failure(PigeonError(code: code, message: message, details: details))) - } else { - completion(.success(Void())) + func onDownloadStatusChanged(event eventArg: DownloadChangedEvent) async throws { + return try await withCheckedThrowingContinuation { continuation in + let channelName: String = "dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadStatusChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([eventArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + continuation.resume(throwing: createConnectionError(withChannelName: channelName)) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + continuation.resume(throwing: PigeonError(code: code, message: message, details: details)) + } else { + continuation.resume() + } } } } - func onDownloadRemoved(event eventArg: DownloadRemovedEvent, completion: @escaping (Result) -> Void) { - let channelName: String = "dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadRemoved\(messageChannelSuffix)" - let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([eventArg] as [Any?]) { response in - guard let listResponse = response as? [Any?] else { - completion(.failure(createConnectionError(withChannelName: channelName))) - return - } - if listResponse.count > 1 { - let code: String = listResponse[0] as! String - let message: String? = nilOrValue(listResponse[1]) - let details: String? = nilOrValue(listResponse[2]) - completion(.failure(PigeonError(code: code, message: message, details: details))) - } else { - completion(.success(Void())) + func onDownloadRemoved(event eventArg: DownloadRemovedEvent) async throws { + return try await withCheckedThrowingContinuation { continuation in + let channelName: String = "dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadRemoved\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([eventArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + continuation.resume(throwing: createConnectionError(withChannelName: channelName)) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + continuation.resume(throwing: PigeonError(code: code, message: message, details: details)) + } else { + continuation.resume() + } } } } - func onDownloadFailed(event eventArg: DownloadFailedEvent, completion: @escaping (Result) -> Void) { - let channelName: String = "dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadFailed\(messageChannelSuffix)" - let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) - channel.sendMessage([eventArg] as [Any?]) { response in - guard let listResponse = response as? [Any?] else { - completion(.failure(createConnectionError(withChannelName: channelName))) - return - } - if listResponse.count > 1 { - let code: String = listResponse[0] as! String - let message: String? = nilOrValue(listResponse[1]) - let details: String? = nilOrValue(listResponse[2]) - completion(.failure(PigeonError(code: code, message: message, details: details))) - } else { - completion(.success(Void())) + func onDownloadFailed(event eventArg: DownloadFailedEvent) async throws { + return try await withCheckedThrowingContinuation { continuation in + let channelName: String = "dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadFailed\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([eventArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + continuation.resume(throwing: createConnectionError(withChannelName: channelName)) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + continuation.resume(throwing: PigeonError(code: code, message: message, details: details)) + } else { + continuation.resume() + } } } } diff --git a/ios/Classes/Pigeon/PlaybackPlatformApi.h b/ios/Classes/Pigeon/PlaybackPlatformApi.h index af8b339..7c68934 100644 --- a/ios/Classes/Pigeon/PlaybackPlatformApi.h +++ b/ios/Classes/Pigeon/PlaybackPlatformApi.h @@ -1,7 +1,7 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon -#import +@import Foundation; @protocol FlutterBinaryMessenger; @protocol FlutterMessageCodec; diff --git a/ios/Classes/Pigeon/PlaybackPlatformApi.m b/ios/Classes/Pigeon/PlaybackPlatformApi.m index 0ef8dca..1bfb1d8 100644 --- a/ios/Classes/Pigeon/PlaybackPlatformApi.m +++ b/ios/Classes/Pigeon/PlaybackPlatformApi.m @@ -1,17 +1,104 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "PlaybackPlatformApi.h" #if TARGET_OS_OSX -#import +@import FlutterMacOS; #else -#import +@import Flutter; #endif -#if !__has_feature(objc_arc) -#error File requires ARC to be enabled. -#endif +static BOOL __attribute__((unused)) FLTPigeonDeepEquals(id _Nullable a, id _Nullable b) { + if (a == b) { + return YES; + } + if (a == nil) { + return b == [NSNull null]; + } + if (b == nil) { + return a == [NSNull null]; + } + if ([a isKindOfClass:[NSNumber class]] && [b isKindOfClass:[NSNumber class]]) { + return [a isEqual:b] || (isnan([(NSNumber *)a doubleValue]) && isnan([(NSNumber *)b doubleValue])); + } + if ([a isKindOfClass:[NSArray class]] && [b isKindOfClass:[NSArray class]]) { + NSArray *arrayA = (NSArray *)a; + NSArray *arrayB = (NSArray *)b; + if (arrayA.count != arrayB.count) { + return NO; + } + for (NSUInteger i = 0; i < arrayA.count; i++) { + if (!FLTPigeonDeepEquals(arrayA[i], arrayB[i])) { + return NO; + } + } + return YES; + } + if ([a isKindOfClass:[NSDictionary class]] && [b isKindOfClass:[NSDictionary class]]) { + NSDictionary *dictA = (NSDictionary *)a; + NSDictionary *dictB = (NSDictionary *)b; + if (dictA.count != dictB.count) { + return NO; + } + for (id keyA in dictA) { + id valueA = dictA[keyA]; + BOOL found = NO; + for (id keyB in dictB) { + if (FLTPigeonDeepEquals(keyA, keyB)) { + id valueB = dictB[keyB]; + if (FLTPigeonDeepEquals(valueA, valueB)) { + found = YES; + break; + } else { + return NO; + } + } + } + if (!found) { + return NO; + } + } + return YES; + } + return [a isEqual:b]; +} + +static NSUInteger __attribute__((unused)) FLTPigeonDeepHash(id _Nullable value) { + if (value == nil || value == (id)[NSNull null]) { + return 0; + } + if ([value isKindOfClass:[NSNumber class]]) { + NSNumber *n = (NSNumber *)value; + double d = n.doubleValue; + if (isnan(d)) { + // Normalize NaN to a consistent hash. + return (NSUInteger)0x7FF8000000000000; + } + if (d == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + d = 0.0; + } + return @(d).hash; + } + if ([value isKindOfClass:[NSArray class]]) { + NSUInteger result = 1; + for (id item in (NSArray *)value) { + result = result * 31 + FLTPigeonDeepHash(item); + } + return result; + } + if ([value isKindOfClass:[NSDictionary class]]) { + NSUInteger result = 0; + NSDictionary *dict = (NSDictionary *)value; + for (id key in dict) { + result += ((FLTPigeonDeepHash(key) * 31) ^ FLTPigeonDeepHash(dict[key])); + } + return result; + } + return [value hash]; +} + static NSArray *wrapResult(id result, FlutterError *error) { if (error) { @@ -232,6 +319,28 @@ + (nullable NpawConfig *)nullableFromList:(NSArray *)list { self.deviceIsAnonymous ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + NpawConfig *other = (NpawConfig *)object; + return FLTPigeonDeepEquals(self.appName, other.appName) && FLTPigeonDeepEquals(self.appReleaseVersion, other.appReleaseVersion) && FLTPigeonDeepEquals(self.accountCode, other.accountCode) && FLTPigeonDeepEquals(self.deviceIsAnonymous, other.deviceIsAnonymous); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.appName); + result = result * 31 + FLTPigeonDeepHash(self.appReleaseVersion); + result = result * 31 + FLTPigeonDeepHash(self.accountCode); + result = result * 31 + FLTPigeonDeepHash(self.deviceIsAnonymous); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"NpawConfig(appName: %@, appReleaseVersion: %@, accountCode: %@, deviceIsAnonymous: %@)", self.appName, self.appReleaseVersion, self.accountCode, self.deviceIsAnonymous]; +} @end @implementation AppConfig @@ -269,6 +378,29 @@ + (nullable AppConfig *)nullableFromList:(NSArray *)list { self.sessionId ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + AppConfig *other = (AppConfig *)object; + return FLTPigeonDeepEquals(self.appLanguage, other.appLanguage) && FLTPigeonDeepEquals(self.audioLanguages, other.audioLanguages) && FLTPigeonDeepEquals(self.subtitleLanguages, other.subtitleLanguages) && FLTPigeonDeepEquals(self.analyticsId, other.analyticsId) && FLTPigeonDeepEquals(self.sessionId, other.sessionId); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.appLanguage); + result = result * 31 + FLTPigeonDeepHash(self.audioLanguages); + result = result * 31 + FLTPigeonDeepHash(self.subtitleLanguages); + result = result * 31 + FLTPigeonDeepHash(self.analyticsId); + result = result * 31 + FLTPigeonDeepHash(self.sessionId); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"AppConfig(appLanguage: %@, audioLanguages: %@, subtitleLanguages: %@, analyticsId: %@, sessionId: %@)", self.appLanguage, self.audioLanguages, self.subtitleLanguages, self.analyticsId, self.sessionId]; +} @end @implementation User @@ -290,6 +422,25 @@ + (nullable User *)nullableFromList:(NSArray *)list { self.id ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + User *other = (User *)object; + return FLTPigeonDeepEquals(self.id, other.id); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.id); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"User(id: %@)", self.id]; +} @end @implementation SetUrlArgs @@ -319,6 +470,27 @@ + (nullable SetUrlArgs *)nullableFromList:(NSArray *)list { self.isLive ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + SetUrlArgs *other = (SetUrlArgs *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && FLTPigeonDeepEquals(self.url, other.url) && FLTPigeonDeepEquals(self.isLive, other.isLive); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + FLTPigeonDeepHash(self.url); + result = result * 31 + FLTPigeonDeepHash(self.isLive); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"SetUrlArgs(playerId: %@, url: %@, isLive: %@)", self.playerId, self.url, self.isLive]; +} @end @implementation MediaItem @@ -372,6 +544,33 @@ + (nullable MediaItem *)nullableFromList:(NSArray *)list { self.lastKnownSubtitleLanguage ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + MediaItem *other = (MediaItem *)object; + return FLTPigeonDeepEquals(self.id, other.id) && FLTPigeonDeepEquals(self.url, other.url) && FLTPigeonDeepEquals(self.mimeType, other.mimeType) && FLTPigeonDeepEquals(self.metadata, other.metadata) && FLTPigeonDeepEquals(self.isLive, other.isLive) && FLTPigeonDeepEquals(self.isOffline, other.isOffline) && FLTPigeonDeepEquals(self.playbackStartPositionMs, other.playbackStartPositionMs) && FLTPigeonDeepEquals(self.lastKnownAudioLanguage, other.lastKnownAudioLanguage) && FLTPigeonDeepEquals(self.lastKnownSubtitleLanguage, other.lastKnownSubtitleLanguage); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.id); + result = result * 31 + FLTPigeonDeepHash(self.url); + result = result * 31 + FLTPigeonDeepHash(self.mimeType); + result = result * 31 + FLTPigeonDeepHash(self.metadata); + result = result * 31 + FLTPigeonDeepHash(self.isLive); + result = result * 31 + FLTPigeonDeepHash(self.isOffline); + result = result * 31 + FLTPigeonDeepHash(self.playbackStartPositionMs); + result = result * 31 + FLTPigeonDeepHash(self.lastKnownAudioLanguage); + result = result * 31 + FLTPigeonDeepHash(self.lastKnownSubtitleLanguage); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"MediaItem(id: %@, url: %@, mimeType: %@, metadata: %@, isLive: %@, isOffline: %@, playbackStartPositionMs: %@, lastKnownAudioLanguage: %@, lastKnownSubtitleLanguage: %@)", self.id, self.url, self.mimeType, self.metadata, self.isLive, self.isOffline, self.playbackStartPositionMs, self.lastKnownAudioLanguage, self.lastKnownSubtitleLanguage]; +} @end @implementation MediaMetadata @@ -409,6 +608,29 @@ + (nullable MediaMetadata *)nullableFromList:(NSArray *)list { self.extras ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + MediaMetadata *other = (MediaMetadata *)object; + return FLTPigeonDeepEquals(self.artworkUri, other.artworkUri) && FLTPigeonDeepEquals(self.title, other.title) && FLTPigeonDeepEquals(self.artist, other.artist) && FLTPigeonDeepEquals(self.durationMs, other.durationMs) && FLTPigeonDeepEquals(self.extras, other.extras); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.artworkUri); + result = result * 31 + FLTPigeonDeepHash(self.title); + result = result * 31 + FLTPigeonDeepHash(self.artist); + result = result * 31 + FLTPigeonDeepHash(self.durationMs); + result = result * 31 + FLTPigeonDeepHash(self.extras); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"MediaMetadata(artworkUri: %@, title: %@, artist: %@, durationMs: %@, extras: %@)", self.artworkUri, self.title, self.artist, self.durationMs, self.extras]; +} @end @implementation PlayerStateSnapshot @@ -479,6 +701,37 @@ + (nullable PlayerStateSnapshot *)nullableFromList:(NSArray *)list { self.seekableRangeEndMs ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PlayerStateSnapshot *other = (PlayerStateSnapshot *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && self.playbackState == other.playbackState && self.isBuffering == other.isBuffering && self.isFullscreen == other.isFullscreen && (self.playbackSpeed == other.playbackSpeed || (isnan(self.playbackSpeed) && isnan(other.playbackSpeed))) && FLTPigeonDeepEquals(self.videoSize, other.videoSize) && FLTPigeonDeepEquals(self.currentMediaItem, other.currentMediaItem) && FLTPigeonDeepEquals(self.playbackPositionMs, other.playbackPositionMs) && FLTPigeonDeepEquals(self.textureId, other.textureId) && FLTPigeonDeepEquals(self.volume, other.volume) && FLTPigeonDeepEquals(self.error, other.error) && FLTPigeonDeepEquals(self.seekableRangeStartMs, other.seekableRangeStartMs) && FLTPigeonDeepEquals(self.seekableRangeEndMs, other.seekableRangeEndMs); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + @(self.playbackState).hash; + result = result * 31 + @(self.isBuffering).hash; + result = result * 31 + @(self.isFullscreen).hash; + result = result * 31 + (isnan(self.playbackSpeed) ? (NSUInteger)0x7FF8000000000000 : @(self.playbackSpeed).hash); + result = result * 31 + FLTPigeonDeepHash(self.videoSize); + result = result * 31 + FLTPigeonDeepHash(self.currentMediaItem); + result = result * 31 + FLTPigeonDeepHash(self.playbackPositionMs); + result = result * 31 + FLTPigeonDeepHash(self.textureId); + result = result * 31 + FLTPigeonDeepHash(self.volume); + result = result * 31 + FLTPigeonDeepHash(self.error); + result = result * 31 + FLTPigeonDeepHash(self.seekableRangeStartMs); + result = result * 31 + FLTPigeonDeepHash(self.seekableRangeEndMs); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PlayerStateSnapshot(playerId: %@, playbackState: %ld, isBuffering: %@, isFullscreen: %@, playbackSpeed: %f, videoSize: %@, currentMediaItem: %@, playbackPositionMs: %@, textureId: %@, volume: %@, error: %@, seekableRangeStartMs: %@, seekableRangeEndMs: %@)", self.playerId, (long)self.playbackState, self.isBuffering ? @"true" : @"false", self.isFullscreen ? @"true" : @"false", self.playbackSpeed, self.videoSize, self.currentMediaItem, self.playbackPositionMs, self.textureId, self.volume, self.error, self.seekableRangeStartMs, self.seekableRangeEndMs]; +} @end @implementation PlayerError @@ -504,6 +757,26 @@ + (nullable PlayerError *)nullableFromList:(NSArray *)list { self.message ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PlayerError *other = (PlayerError *)object; + return FLTPigeonDeepEquals(self.code, other.code) && FLTPigeonDeepEquals(self.message, other.message); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.code); + result = result * 31 + FLTPigeonDeepHash(self.message); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PlayerError(code: %@, message: %@)", self.code, self.message]; +} @end @implementation VideoSize @@ -529,6 +802,26 @@ + (nullable VideoSize *)nullableFromList:(NSArray *)list { @(self.height), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + VideoSize *other = (VideoSize *)object; + return self.width == other.width && self.height == other.height; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.width).hash; + result = result * 31 + @(self.height).hash; + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"VideoSize(width: %ld, height: %ld)", (long)self.width, (long)self.height]; +} @end @implementation ChromecastState @@ -555,6 +848,26 @@ + (nullable ChromecastState *)nullableFromList:(NSArray *)list { self.mediaItem ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + ChromecastState *other = (ChromecastState *)object; + return self.connectionState == other.connectionState && FLTPigeonDeepEquals(self.mediaItem, other.mediaItem); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + @(self.connectionState).hash; + result = result * 31 + FLTPigeonDeepHash(self.mediaItem); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"ChromecastState(connectionState: %ld, mediaItem: %@)", (long)self.connectionState, self.mediaItem]; +} @end @implementation MediaInfo @@ -584,6 +897,27 @@ + (nullable MediaInfo *)nullableFromList:(NSArray *)list { self.videoTracks ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + MediaInfo *other = (MediaInfo *)object; + return FLTPigeonDeepEquals(self.audioTracks, other.audioTracks) && FLTPigeonDeepEquals(self.textTracks, other.textTracks) && FLTPigeonDeepEquals(self.videoTracks, other.videoTracks); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.audioTracks); + result = result * 31 + FLTPigeonDeepHash(self.textTracks); + result = result * 31 + FLTPigeonDeepHash(self.videoTracks); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"MediaInfo(audioTracks: %@, textTracks: %@, videoTracks: %@)", self.audioTracks, self.textTracks, self.videoTracks]; +} @end @implementation PlayerTracksSnapshot @@ -617,6 +951,28 @@ + (nullable PlayerTracksSnapshot *)nullableFromList:(NSArray *)list { self.videoTracks ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PlayerTracksSnapshot *other = (PlayerTracksSnapshot *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && FLTPigeonDeepEquals(self.audioTracks, other.audioTracks) && FLTPigeonDeepEquals(self.textTracks, other.textTracks) && FLTPigeonDeepEquals(self.videoTracks, other.videoTracks); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + FLTPigeonDeepHash(self.audioTracks); + result = result * 31 + FLTPigeonDeepHash(self.textTracks); + result = result * 31 + FLTPigeonDeepHash(self.videoTracks); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PlayerTracksSnapshot(playerId: %@, audioTracks: %@, textTracks: %@, videoTracks: %@)", self.playerId, self.audioTracks, self.textTracks, self.videoTracks]; +} @end @implementation Track @@ -670,6 +1026,33 @@ + (nullable Track *)nullableFromList:(NSArray *)list { @(self.isSelected), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + Track *other = (Track *)object; + return FLTPigeonDeepEquals(self.id, other.id) && FLTPigeonDeepEquals(self.label, other.label) && FLTPigeonDeepEquals(self.language, other.language) && FLTPigeonDeepEquals(self.frameRate, other.frameRate) && FLTPigeonDeepEquals(self.bitrate, other.bitrate) && FLTPigeonDeepEquals(self.width, other.width) && FLTPigeonDeepEquals(self.height, other.height) && FLTPigeonDeepEquals(self.downloaded, other.downloaded) && self.isSelected == other.isSelected; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.id); + result = result * 31 + FLTPigeonDeepHash(self.label); + result = result * 31 + FLTPigeonDeepHash(self.language); + result = result * 31 + FLTPigeonDeepHash(self.frameRate); + result = result * 31 + FLTPigeonDeepHash(self.bitrate); + result = result * 31 + FLTPigeonDeepHash(self.width); + result = result * 31 + FLTPigeonDeepHash(self.height); + result = result * 31 + FLTPigeonDeepHash(self.downloaded); + result = result * 31 + @(self.isSelected).hash; + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"Track(id: %@, label: %@, language: %@, frameRate: %@, bitrate: %@, width: %@, height: %@, downloaded: %@, isSelected: %@)", self.id, self.label, self.language, self.frameRate, self.bitrate, self.width, self.height, self.downloaded, self.isSelected ? @"true" : @"false"]; +} @end @implementation PrimaryPlayerChangedEvent @@ -691,6 +1074,25 @@ + (nullable PrimaryPlayerChangedEvent *)nullableFromList:(NSArray *)list { self.playerId ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PrimaryPlayerChangedEvent *other = (PrimaryPlayerChangedEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PrimaryPlayerChangedEvent(playerId: %@)", self.playerId]; +} @end @implementation PlayerStateUpdateEvent @@ -716,6 +1118,26 @@ + (nullable PlayerStateUpdateEvent *)nullableFromList:(NSArray *)list { self.snapshot ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PlayerStateUpdateEvent *other = (PlayerStateUpdateEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && FLTPigeonDeepEquals(self.snapshot, other.snapshot); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + FLTPigeonDeepHash(self.snapshot); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PlayerStateUpdateEvent(playerId: %@, snapshot: %@)", self.playerId, self.snapshot]; +} @end @implementation PositionDiscontinuityEvent @@ -741,6 +1163,26 @@ + (nullable PositionDiscontinuityEvent *)nullableFromList:(NSArray *)list { self.playbackPositionMs ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PositionDiscontinuityEvent *other = (PositionDiscontinuityEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && FLTPigeonDeepEquals(self.playbackPositionMs, other.playbackPositionMs); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + FLTPigeonDeepHash(self.playbackPositionMs); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PositionDiscontinuityEvent(playerId: %@, playbackPositionMs: %@)", self.playerId, self.playbackPositionMs]; +} @end @implementation PlaybackStateChangedEvent @@ -771,6 +1213,27 @@ + (nullable PlaybackStateChangedEvent *)nullableFromList:(NSArray *)list { @(self.isBuffering), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PlaybackStateChangedEvent *other = (PlaybackStateChangedEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && self.playbackState == other.playbackState && self.isBuffering == other.isBuffering; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + @(self.playbackState).hash; + result = result * 31 + @(self.isBuffering).hash; + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PlaybackStateChangedEvent(playerId: %@, playbackState: %ld, isBuffering: %@)", self.playerId, (long)self.playbackState, self.isBuffering ? @"true" : @"false"]; +} @end @implementation PlaybackEndedEvent @@ -796,6 +1259,26 @@ + (nullable PlaybackEndedEvent *)nullableFromList:(NSArray *)list { self.mediaItem ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PlaybackEndedEvent *other = (PlaybackEndedEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && FLTPigeonDeepEquals(self.mediaItem, other.mediaItem); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + FLTPigeonDeepHash(self.mediaItem); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PlaybackEndedEvent(playerId: %@, mediaItem: %@)", self.playerId, self.mediaItem]; +} @end @implementation PictureInPictureModeChangedEvent @@ -821,6 +1304,26 @@ + (nullable PictureInPictureModeChangedEvent *)nullableFromList:(NSArray *)l @(self.isInPipMode), ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + PictureInPictureModeChangedEvent *other = (PictureInPictureModeChangedEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && self.isInPipMode == other.isInPipMode; +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + @(self.isInPipMode).hash; + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"PictureInPictureModeChangedEvent(playerId: %@, isInPipMode: %@)", self.playerId, self.isInPipMode ? @"true" : @"false"]; +} @end @implementation MediaItemTransitionEvent @@ -846,6 +1349,26 @@ + (nullable MediaItemTransitionEvent *)nullableFromList:(NSArray *)list { self.mediaItem ?: [NSNull null], ]; } +- (BOOL)isEqual:(id)object { + if (self == object) { + return YES; + } + if (![object isKindOfClass:[self class]]) { + return NO; + } + MediaItemTransitionEvent *other = (MediaItemTransitionEvent *)object; + return FLTPigeonDeepEquals(self.playerId, other.playerId) && FLTPigeonDeepEquals(self.mediaItem, other.mediaItem); +} + +- (NSUInteger)hash { + NSUInteger result = [self class].hash; + result = result * 31 + FLTPigeonDeepHash(self.playerId); + result = result * 31 + FLTPigeonDeepHash(self.mediaItem); + return result; +} +- (NSString *)description { + return [NSString stringWithFormat:@"MediaItemTransitionEvent(playerId: %@, mediaItem: %@)", self.playerId, self.mediaItem]; +} @end @interface nullPlaybackPlatformApiPigeonCodecReader : FlutterStandardReader diff --git a/ios/Classes/SwiftBccmPlayerPlugin.swift b/ios/Classes/SwiftBccmPlayerPlugin.swift index 343e86a..553de23 100644 --- a/ios/Classes/SwiftBccmPlayerPlugin.swift +++ b/ios/Classes/SwiftBccmPlayerPlugin.swift @@ -25,13 +25,13 @@ public class SwiftBccmPlayerPlugin: NSObject, FlutterPlugin { let downloader = Downloader() cancellables.append(contentsOf: [ downloader.changeEvents.sink { event in - downloaderListener.onDownloadStatusChanged(event: event) { _ in } + Task { try? await downloaderListener.onDownloadStatusChanged(event: event) } }, downloader.removeEvents.sink { event in - downloaderListener.onDownloadRemoved(event: event) { _ in } + Task { try? await downloaderListener.onDownloadRemoved(event: event) } }, downloader.failEvents.sink { event in - downloaderListener.onDownloadFailed(event: event) { _ in } + Task { try? await downloaderListener.onDownloadFailed(event: event) } } ]) diff --git a/lib/src/pigeon/chromecast_pigeon.g.dart b/lib/src/pigeon/chromecast_pigeon.g.dart index ff2e074..8db30ab 100644 --- a/lib/src/pigeon/chromecast_pigeon.g.dart +++ b/lib/src/pigeon/chromecast_pigeon.g.dart @@ -1,12 +1,13 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { @@ -17,6 +18,68 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } +bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + return a == b; +} + +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + class CastSessionUnavailableEvent { CastSessionUnavailableEvent({ @@ -25,18 +88,42 @@ class CastSessionUnavailableEvent { int? playbackPositionMs; - Object encode() { + List _toList() { return [ playbackPositionMs, ]; } + Object encode() { + return _toList(); } + static CastSessionUnavailableEvent decode(Object result) { result as List; return CastSessionUnavailableEvent( playbackPositionMs: result[0] as int?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! CastSessionUnavailableEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playbackPositionMs, other.playbackPositionMs); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'CastSessionUnavailableEvent(playbackPositionMs: $playbackPositionMs)'; + } } @@ -58,7 +145,7 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: + case 129: return CastSessionUnavailableEvent.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -95,7 +182,7 @@ abstract class ChromecastPigeon { static void setUp(ChromecastPigeon? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionEnded$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -114,7 +201,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionEnding$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -133,7 +220,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionResumeFailed$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -152,7 +239,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionResumed$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -171,7 +258,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionResuming$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -190,7 +277,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionStartFailed$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -209,7 +296,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionStarted$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -228,7 +315,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionStarting$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -247,7 +334,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onSessionSuspended$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -266,7 +353,7 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onCastSessionAvailable$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { @@ -285,21 +372,17 @@ abstract class ChromecastPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.ChromecastPigeon.onCastSessionUnavailable$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.ChromecastPigeon.onCastSessionUnavailable was null.'); - final List args = (message as List?)!; - final CastSessionUnavailableEvent? arg_event = (args[0] as CastSessionUnavailableEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.ChromecastPigeon.onCastSessionUnavailable was null, expected non-null CastSessionUnavailableEvent.'); + final List args = message! as List; + final CastSessionUnavailableEvent arg_event = args[0]! as CastSessionUnavailableEvent; try { - api.onCastSessionUnavailable(arg_event!); + api.onCastSessionUnavailable(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/lib/src/pigeon/downloader_pigeon.g.dart b/lib/src/pigeon/downloader_pigeon.g.dart index ca1b234..72c6a0b 100644 --- a/lib/src/pigeon/downloader_pigeon.g.dart +++ b/lib/src/pigeon/downloader_pigeon.g.dart @@ -1,20 +1,40 @@ -// Autogenerated from Pigeon (v22.3.0), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; - -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } + List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; @@ -24,6 +44,68 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } +bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + return a == b; +} + +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + enum DownloadStatus { downloading, @@ -56,7 +138,7 @@ class DownloadConfig { Map additionalData; - Object encode() { + List _toList() { return [ url, mimeType, @@ -67,17 +149,41 @@ class DownloadConfig { ]; } + Object encode() { + return _toList(); } + static DownloadConfig decode(Object result) { result as List; return DownloadConfig( url: result[0]! as String, mimeType: result[1]! as String, title: result[2]! as String, - audioTrackIds: (result[3] as List?)!.cast(), - videoTrackIds: (result[4] as List?)!.cast(), - additionalData: (result[5] as Map?)!.cast(), + audioTrackIds: (result[3]! as List).cast(), + videoTrackIds: (result[4]! as List).cast(), + additionalData: (result[5]! as Map).cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! DownloadConfig || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(url, other.url) && _deepEquals(mimeType, other.mimeType) && _deepEquals(title, other.title) && _deepEquals(audioTrackIds, other.audioTrackIds) && _deepEquals(videoTrackIds, other.videoTrackIds) && _deepEquals(additionalData, other.additionalData); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'DownloadConfig(url: $url, mimeType: $mimeType, title: $title, audioTrackIds: $audioTrackIds, videoTrackIds: $videoTrackIds, additionalData: $additionalData)'; + } } class Download { @@ -102,7 +208,7 @@ class Download { String? error; - Object encode() { + List _toList() { return [ key, config, @@ -113,6 +219,9 @@ class Download { ]; } + Object encode() { + return _toList(); } + static Download decode(Object result) { result as List; return Download( @@ -124,6 +233,27 @@ class Download { error: result[5] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! Download || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(key, other.key) && _deepEquals(config, other.config) && _deepEquals(offlineUrl, other.offlineUrl) && _deepEquals(fractionDownloaded, other.fractionDownloaded) && _deepEquals(status, other.status) && _deepEquals(error, other.error); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'Download(key: $key, config: $config, offlineUrl: $offlineUrl, fractionDownloaded: $fractionDownloaded, status: $status, error: $error)'; + } } class DownloadFailedEvent { @@ -136,13 +266,16 @@ class DownloadFailedEvent { String? error; - Object encode() { + List _toList() { return [ key, error, ]; } + Object encode() { + return _toList(); } + static DownloadFailedEvent decode(Object result) { result as List; return DownloadFailedEvent( @@ -150,6 +283,27 @@ class DownloadFailedEvent { error: result[1] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! DownloadFailedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(key, other.key) && _deepEquals(error, other.error); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'DownloadFailedEvent(key: $key, error: $error)'; + } } class DownloadRemovedEvent { @@ -159,18 +313,42 @@ class DownloadRemovedEvent { String key; - Object encode() { + List _toList() { return [ key, ]; } + Object encode() { + return _toList(); } + static DownloadRemovedEvent decode(Object result) { result as List; return DownloadRemovedEvent( key: result[0]! as String, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! DownloadRemovedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(key, other.key); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'DownloadRemovedEvent(key: $key)'; + } } class DownloadChangedEvent { @@ -180,18 +358,42 @@ class DownloadChangedEvent { Download download; - Object encode() { + List _toList() { return [ download, ]; } + Object encode() { + return _toList(); } + static DownloadChangedEvent decode(Object result) { result as List; return DownloadChangedEvent( download: result[0]! as Download, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! DownloadChangedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(download, other.download); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'DownloadChangedEvent(download: $download)'; + } } @@ -228,18 +430,18 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: - final int? value = readValue(buffer) as int?; + case 129: + final value = readValue(buffer) as int?; return value == null ? null : DownloadStatus.values[value]; - case 130: + case 130: return DownloadConfig.decode(readValue(buffer)!); - case 131: + case 131: return Download.decode(readValue(buffer)!); - case 132: + case 132: return DownloadFailedEvent.decode(readValue(buffer)!); - case 133: + case 133: return DownloadRemovedEvent.decode(readValue(buffer)!); - case 134: + case 134: return DownloadChangedEvent.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -249,8 +451,8 @@ class _PigeonCodec extends StandardMessageCodec { /// An API called by the native side to notify about chromecast changes class DownloaderPigeon { - /// Constructor for [DownloaderPigeon]. The [binaryMessenger] named argument is - /// available for dependency injection. If it is left null, the default + /// Constructor for [DownloaderPigeon]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. DownloaderPigeon({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, @@ -262,156 +464,117 @@ class DownloaderPigeon { final String pigeonVar_messageChannelSuffix; Future startDownload(DownloadConfig downloadConfig) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.startDownload$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.startDownload$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([downloadConfig]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as Download?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([downloadConfig]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as Download; } Future getDownloadStatus(String downloadKey) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownloadStatus$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownloadStatus$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([downloadKey]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as double?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([downloadKey]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as double; } - Future> getDownloads() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownloads$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + Future> getDownloads() async { + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownloads$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as List?)!.cast(); - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return (pigeonVar_replyValue! as List).cast(); } Future getDownload(String downloadKey) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownload$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getDownload$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([downloadKey]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return (pigeonVar_replyList[0] as Download?); - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([downloadKey]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; + return pigeonVar_replyValue as Download?; } Future removeDownload(String downloadKey) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.removeDownload$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.removeDownload$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([downloadKey]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([downloadKey]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } /// Returns free space in bytes Future getFreeDiskSpace() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getFreeDiskSpace$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.DownloaderPigeon.getFreeDiskSpace$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as double?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as double; } } @@ -427,21 +590,17 @@ abstract class DownloaderListenerPigeon { static void setUp(DownloaderListenerPigeon? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadStatusChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadStatusChanged was null.'); - final List args = (message as List?)!; - final DownloadChangedEvent? arg_event = (args[0] as DownloadChangedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadStatusChanged was null, expected non-null DownloadChangedEvent.'); + final List args = message! as List; + final DownloadChangedEvent arg_event = args[0]! as DownloadChangedEvent; try { - api.onDownloadStatusChanged(arg_event!); + api.onDownloadStatusChanged(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -452,21 +611,17 @@ abstract class DownloaderListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadRemoved$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadRemoved was null.'); - final List args = (message as List?)!; - final DownloadRemovedEvent? arg_event = (args[0] as DownloadRemovedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadRemoved was null, expected non-null DownloadRemovedEvent.'); + final List args = message! as List; + final DownloadRemovedEvent arg_event = args[0]! as DownloadRemovedEvent; try { - api.onDownloadRemoved(arg_event!); + api.onDownloadRemoved(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -477,21 +632,17 @@ abstract class DownloaderListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadFailed$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadFailed was null.'); - final List args = (message as List?)!; - final DownloadFailedEvent? arg_event = (args[0] as DownloadFailedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.DownloaderListenerPigeon.onDownloadFailed was null, expected non-null DownloadFailedEvent.'); + final List args = message! as List; + final DownloadFailedEvent arg_event = args[0]! as DownloadFailedEvent; try { - api.onDownloadFailed(arg_event!); + api.onDownloadFailed(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/lib/src/pigeon/playback_platform_pigeon.g.dart b/lib/src/pigeon/playback_platform_pigeon.g.dart index 3b16857..a1c48c4 100644 --- a/lib/src/pigeon/playback_platform_pigeon.g.dart +++ b/lib/src/pigeon/playback_platform_pigeon.g.dart @@ -1,20 +1,40 @@ -// Autogenerated from Pigeon (v22.7.4), do not edit directly. +// Autogenerated from Pigeon (v28.0.0), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers +// ignore_for_file: unused_import, unused_shown_name +// ignore_for_file: type=lint import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'dart:typed_data' show Float64List, Int32List, Int64List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; - -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); +import 'package:meta/meta.dart' show immutable, protected, visibleForTesting; + +Object? _extractReplyValueOrThrow( + List? replyList, + String channelName, { + required bool isNullValid, +}) { + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (!isNullValid && (replyList.isNotEmpty && replyList[0] == null)) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } + return replyList.firstOrNull; } + List wrapResponse({Object? result, PlatformException? error, bool empty = false}) { if (empty) { return []; @@ -24,6 +44,68 @@ List wrapResponse({Object? result, PlatformException? error, bool empty } return [error.code, error.message, error.details]; } +bool _deepEquals(Object? a, Object? b) { + if (identical(a, b)) { + return true; + } + if (a is double && b is double) { + if (a.isNaN && b.isNaN) { + return true; + } + return a == b; + } + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + if (a.length != b.length) { + return false; + } + for (final MapEntry entryA in a.entries) { + bool found = false; + for (final MapEntry entryB in b.entries) { + if (_deepEquals(entryA.key, entryB.key)) { + if (_deepEquals(entryA.value, entryB.value)) { + found = true; + break; + } else { + return false; + } + } + } + if (!found) { + return false; + } + } + return true; + } + return a == b; +} + +int _deepHash(Object? value) { + if (value is List) { + return Object.hashAll(value.map(_deepHash)); + } + if (value is Map) { + int result = 0; + for (final MapEntry entry in value.entries) { + result += (_deepHash(entry.key) * 31) ^ _deepHash(entry.value); + } + return result; + } + if (value is double && value.isNaN) { + // Normalize NaN to a consistent hash. + return 0x7FF8000000000000.hashCode; + } + if (value is double && value == 0.0) { + // Normalize -0.0 to 0.0 so they have the same hash code. + return 0.0.hashCode; + } + return value.hashCode; +} + enum BufferMode { standard, @@ -71,7 +153,7 @@ class NpawConfig { bool? deviceIsAnonymous; - Object encode() { + List _toList() { return [ appName, appReleaseVersion, @@ -80,6 +162,9 @@ class NpawConfig { ]; } + Object encode() { + return _toList(); } + static NpawConfig decode(Object result) { result as List; return NpawConfig( @@ -89,6 +174,27 @@ class NpawConfig { deviceIsAnonymous: result[3] as bool?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! NpawConfig || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(appName, other.appName) && _deepEquals(appReleaseVersion, other.appReleaseVersion) && _deepEquals(accountCode, other.accountCode) && _deepEquals(deviceIsAnonymous, other.deviceIsAnonymous); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'NpawConfig(appName: $appName, appReleaseVersion: $appReleaseVersion, accountCode: $accountCode, deviceIsAnonymous: $deviceIsAnonymous)'; + } } class AppConfig { @@ -110,7 +216,7 @@ class AppConfig { String? sessionId; - Object encode() { + List _toList() { return [ appLanguage, audioLanguages, @@ -120,16 +226,40 @@ class AppConfig { ]; } + Object encode() { + return _toList(); } + static AppConfig decode(Object result) { result as List; return AppConfig( appLanguage: result[0] as String?, - audioLanguages: (result[1] as List?)!.cast(), - subtitleLanguages: (result[2] as List?)!.cast(), + audioLanguages: (result[1]! as List).cast(), + subtitleLanguages: (result[2]! as List).cast(), analyticsId: result[3] as String?, sessionId: result[4] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! AppConfig || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(appLanguage, other.appLanguage) && _deepEquals(audioLanguages, other.audioLanguages) && _deepEquals(subtitleLanguages, other.subtitleLanguages) && _deepEquals(analyticsId, other.analyticsId) && _deepEquals(sessionId, other.sessionId); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'AppConfig(appLanguage: $appLanguage, audioLanguages: $audioLanguages, subtitleLanguages: $subtitleLanguages, analyticsId: $analyticsId, sessionId: $sessionId)'; + } } class User { @@ -139,18 +269,42 @@ class User { String? id; - Object encode() { + List _toList() { return [ id, ]; } + Object encode() { + return _toList(); } + static User decode(Object result) { result as List; return User( id: result[0] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! User || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(id, other.id); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'User(id: $id)'; + } } class SetUrlArgs { @@ -166,7 +320,7 @@ class SetUrlArgs { bool? isLive; - Object encode() { + List _toList() { return [ playerId, url, @@ -174,6 +328,9 @@ class SetUrlArgs { ]; } + Object encode() { + return _toList(); } + static SetUrlArgs decode(Object result) { result as List; return SetUrlArgs( @@ -182,6 +339,27 @@ class SetUrlArgs { isLive: result[2] as bool?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! SetUrlArgs || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(url, other.url) && _deepEquals(isLive, other.isLive); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'SetUrlArgs(playerId: $playerId, url: $url, isLive: $isLive)'; + } } class MediaItem { @@ -215,7 +393,7 @@ class MediaItem { String? lastKnownSubtitleLanguage; - Object encode() { + List _toList() { return [ id, url, @@ -229,6 +407,9 @@ class MediaItem { ]; } + Object encode() { + return _toList(); } + static MediaItem decode(Object result) { result as List; return MediaItem( @@ -243,6 +424,27 @@ class MediaItem { lastKnownSubtitleLanguage: result[8] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! MediaItem || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(id, other.id) && _deepEquals(url, other.url) && _deepEquals(mimeType, other.mimeType) && _deepEquals(metadata, other.metadata) && _deepEquals(isLive, other.isLive) && _deepEquals(isOffline, other.isOffline) && _deepEquals(playbackStartPositionMs, other.playbackStartPositionMs) && _deepEquals(lastKnownAudioLanguage, other.lastKnownAudioLanguage) && _deepEquals(lastKnownSubtitleLanguage, other.lastKnownSubtitleLanguage); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'MediaItem(id: $id, url: $url, mimeType: $mimeType, metadata: $metadata, isLive: $isLive, isOffline: $isOffline, playbackStartPositionMs: $playbackStartPositionMs, lastKnownAudioLanguage: $lastKnownAudioLanguage, lastKnownSubtitleLanguage: $lastKnownSubtitleLanguage)'; + } } class MediaMetadata { @@ -264,7 +466,7 @@ class MediaMetadata { Map? extras; - Object encode() { + List _toList() { return [ artworkUri, title, @@ -274,6 +476,9 @@ class MediaMetadata { ]; } + Object encode() { + return _toList(); } + static MediaMetadata decode(Object result) { result as List; return MediaMetadata( @@ -284,6 +489,27 @@ class MediaMetadata { extras: (result[4] as Map?)?.cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! MediaMetadata || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(artworkUri, other.artworkUri) && _deepEquals(title, other.title) && _deepEquals(artist, other.artist) && _deepEquals(durationMs, other.durationMs) && _deepEquals(extras, other.extras); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'MediaMetadata(artworkUri: $artworkUri, title: $title, artist: $artist, durationMs: $durationMs, extras: $extras)'; + } } class PlayerStateSnapshot { @@ -329,7 +555,7 @@ class PlayerStateSnapshot { double? seekableRangeEndMs; - Object encode() { + List _toList() { return [ playerId, playbackState, @@ -347,6 +573,9 @@ class PlayerStateSnapshot { ]; } + Object encode() { + return _toList(); } + static PlayerStateSnapshot decode(Object result) { result as List; return PlayerStateSnapshot( @@ -365,6 +594,27 @@ class PlayerStateSnapshot { seekableRangeEndMs: result[12] as double?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlayerStateSnapshot || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(playbackState, other.playbackState) && _deepEquals(isBuffering, other.isBuffering) && _deepEquals(isFullscreen, other.isFullscreen) && _deepEquals(playbackSpeed, other.playbackSpeed) && _deepEquals(videoSize, other.videoSize) && _deepEquals(currentMediaItem, other.currentMediaItem) && _deepEquals(playbackPositionMs, other.playbackPositionMs) && _deepEquals(textureId, other.textureId) && _deepEquals(volume, other.volume) && _deepEquals(error, other.error) && _deepEquals(seekableRangeStartMs, other.seekableRangeStartMs) && _deepEquals(seekableRangeEndMs, other.seekableRangeEndMs); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PlayerStateSnapshot(playerId: $playerId, playbackState: $playbackState, isBuffering: $isBuffering, isFullscreen: $isFullscreen, playbackSpeed: $playbackSpeed, videoSize: $videoSize, currentMediaItem: $currentMediaItem, playbackPositionMs: $playbackPositionMs, textureId: $textureId, volume: $volume, error: $error, seekableRangeStartMs: $seekableRangeStartMs, seekableRangeEndMs: $seekableRangeEndMs)'; + } } class PlayerError { @@ -377,13 +627,16 @@ class PlayerError { String? message; - Object encode() { + List _toList() { return [ code, message, ]; } + Object encode() { + return _toList(); } + static PlayerError decode(Object result) { result as List; return PlayerError( @@ -391,6 +644,27 @@ class PlayerError { message: result[1] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlayerError || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(code, other.code) && _deepEquals(message, other.message); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PlayerError(code: $code, message: $message)'; + } } class VideoSize { @@ -403,13 +677,16 @@ class VideoSize { int height; - Object encode() { + List _toList() { return [ width, height, ]; } + Object encode() { + return _toList(); } + static VideoSize decode(Object result) { result as List; return VideoSize( @@ -417,6 +694,27 @@ class VideoSize { height: result[1]! as int, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! VideoSize || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(width, other.width) && _deepEquals(height, other.height); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'VideoSize(width: $width, height: $height)'; + } } class ChromecastState { @@ -429,13 +727,16 @@ class ChromecastState { MediaItem? mediaItem; - Object encode() { + List _toList() { return [ connectionState, mediaItem, ]; } + Object encode() { + return _toList(); } + static ChromecastState decode(Object result) { result as List; return ChromecastState( @@ -443,6 +744,27 @@ class ChromecastState { mediaItem: result[1] as MediaItem?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ChromecastState || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(connectionState, other.connectionState) && _deepEquals(mediaItem, other.mediaItem); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'ChromecastState(connectionState: $connectionState, mediaItem: $mediaItem)'; + } } class MediaInfo { @@ -458,7 +780,7 @@ class MediaInfo { List videoTracks; - Object encode() { + List _toList() { return [ audioTracks, textTracks, @@ -466,14 +788,38 @@ class MediaInfo { ]; } + Object encode() { + return _toList(); } + static MediaInfo decode(Object result) { result as List; return MediaInfo( - audioTracks: (result[0] as List?)!.cast(), - textTracks: (result[1] as List?)!.cast(), - videoTracks: (result[2] as List?)!.cast(), + audioTracks: (result[0]! as List).cast(), + textTracks: (result[1]! as List).cast(), + videoTracks: (result[2]! as List).cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! MediaInfo || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(audioTracks, other.audioTracks) && _deepEquals(textTracks, other.textTracks) && _deepEquals(videoTracks, other.videoTracks); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'MediaInfo(audioTracks: $audioTracks, textTracks: $textTracks, videoTracks: $videoTracks)'; + } } class PlayerTracksSnapshot { @@ -492,7 +838,7 @@ class PlayerTracksSnapshot { List videoTracks; - Object encode() { + List _toList() { return [ playerId, audioTracks, @@ -501,15 +847,39 @@ class PlayerTracksSnapshot { ]; } + Object encode() { + return _toList(); } + static PlayerTracksSnapshot decode(Object result) { result as List; return PlayerTracksSnapshot( playerId: result[0]! as String, - audioTracks: (result[1] as List?)!.cast(), - textTracks: (result[2] as List?)!.cast(), - videoTracks: (result[3] as List?)!.cast(), + audioTracks: (result[1]! as List).cast(), + textTracks: (result[2]! as List).cast(), + videoTracks: (result[3]! as List).cast(), ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlayerTracksSnapshot || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(audioTracks, other.audioTracks) && _deepEquals(textTracks, other.textTracks) && _deepEquals(videoTracks, other.videoTracks); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PlayerTracksSnapshot(playerId: $playerId, audioTracks: $audioTracks, textTracks: $textTracks, videoTracks: $videoTracks)'; + } } class Track { @@ -543,7 +913,7 @@ class Track { bool isSelected; - Object encode() { + List _toList() { return [ id, label, @@ -557,6 +927,9 @@ class Track { ]; } + Object encode() { + return _toList(); } + static Track decode(Object result) { result as List; return Track( @@ -571,6 +944,27 @@ class Track { isSelected: result[8]! as bool, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! Track || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(id, other.id) && _deepEquals(label, other.label) && _deepEquals(language, other.language) && _deepEquals(frameRate, other.frameRate) && _deepEquals(bitrate, other.bitrate) && _deepEquals(width, other.width) && _deepEquals(height, other.height) && _deepEquals(downloaded, other.downloaded) && _deepEquals(isSelected, other.isSelected); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'Track(id: $id, label: $label, language: $language, frameRate: $frameRate, bitrate: $bitrate, width: $width, height: $height, downloaded: $downloaded, isSelected: $isSelected)'; + } } class PrimaryPlayerChangedEvent { @@ -580,18 +974,42 @@ class PrimaryPlayerChangedEvent { String? playerId; - Object encode() { + List _toList() { return [ playerId, ]; } + Object encode() { + return _toList(); } + static PrimaryPlayerChangedEvent decode(Object result) { result as List; return PrimaryPlayerChangedEvent( playerId: result[0] as String?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PrimaryPlayerChangedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PrimaryPlayerChangedEvent(playerId: $playerId)'; + } } class PlayerStateUpdateEvent { @@ -604,13 +1022,16 @@ class PlayerStateUpdateEvent { PlayerStateSnapshot snapshot; - Object encode() { + List _toList() { return [ playerId, snapshot, ]; } + Object encode() { + return _toList(); } + static PlayerStateUpdateEvent decode(Object result) { result as List; return PlayerStateUpdateEvent( @@ -618,6 +1039,27 @@ class PlayerStateUpdateEvent { snapshot: result[1]! as PlayerStateSnapshot, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlayerStateUpdateEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(snapshot, other.snapshot); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PlayerStateUpdateEvent(playerId: $playerId, snapshot: $snapshot)'; + } } class PositionDiscontinuityEvent { @@ -630,13 +1072,16 @@ class PositionDiscontinuityEvent { double? playbackPositionMs; - Object encode() { + List _toList() { return [ playerId, playbackPositionMs, ]; } + Object encode() { + return _toList(); } + static PositionDiscontinuityEvent decode(Object result) { result as List; return PositionDiscontinuityEvent( @@ -644,6 +1089,27 @@ class PositionDiscontinuityEvent { playbackPositionMs: result[1] as double?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PositionDiscontinuityEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(playbackPositionMs, other.playbackPositionMs); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PositionDiscontinuityEvent(playerId: $playerId, playbackPositionMs: $playbackPositionMs)'; + } } class PlaybackStateChangedEvent { @@ -659,7 +1125,7 @@ class PlaybackStateChangedEvent { bool isBuffering; - Object encode() { + List _toList() { return [ playerId, playbackState, @@ -667,6 +1133,9 @@ class PlaybackStateChangedEvent { ]; } + Object encode() { + return _toList(); } + static PlaybackStateChangedEvent decode(Object result) { result as List; return PlaybackStateChangedEvent( @@ -675,6 +1144,27 @@ class PlaybackStateChangedEvent { isBuffering: result[2]! as bool, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlaybackStateChangedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(playbackState, other.playbackState) && _deepEquals(isBuffering, other.isBuffering); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PlaybackStateChangedEvent(playerId: $playerId, playbackState: $playbackState, isBuffering: $isBuffering)'; + } } class PlaybackEndedEvent { @@ -687,13 +1177,16 @@ class PlaybackEndedEvent { MediaItem? mediaItem; - Object encode() { + List _toList() { return [ playerId, mediaItem, ]; } + Object encode() { + return _toList(); } + static PlaybackEndedEvent decode(Object result) { result as List; return PlaybackEndedEvent( @@ -701,6 +1194,27 @@ class PlaybackEndedEvent { mediaItem: result[1] as MediaItem?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PlaybackEndedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(mediaItem, other.mediaItem); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PlaybackEndedEvent(playerId: $playerId, mediaItem: $mediaItem)'; + } } class PictureInPictureModeChangedEvent { @@ -713,13 +1227,16 @@ class PictureInPictureModeChangedEvent { bool isInPipMode; - Object encode() { + List _toList() { return [ playerId, isInPipMode, ]; } + Object encode() { + return _toList(); } + static PictureInPictureModeChangedEvent decode(Object result) { result as List; return PictureInPictureModeChangedEvent( @@ -727,6 +1244,27 @@ class PictureInPictureModeChangedEvent { isInPipMode: result[1]! as bool, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! PictureInPictureModeChangedEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(isInPipMode, other.isInPipMode); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'PictureInPictureModeChangedEvent(playerId: $playerId, isInPipMode: $isInPipMode)'; + } } class MediaItemTransitionEvent { @@ -739,13 +1277,16 @@ class MediaItemTransitionEvent { MediaItem? mediaItem; - Object encode() { + List _toList() { return [ playerId, mediaItem, ]; } + Object encode() { + return _toList(); } + static MediaItemTransitionEvent decode(Object result) { result as List; return MediaItemTransitionEvent( @@ -753,6 +1294,27 @@ class MediaItemTransitionEvent { mediaItem: result[1] as MediaItem?, ); } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! MediaItemTransitionEvent || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(playerId, other.playerId) && _deepEquals(mediaItem, other.mediaItem); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => _deepHash([runtimeType, ..._toList()]); + + @override + String toString() { + return 'MediaItemTransitionEvent(playerId: $playerId, mediaItem: $mediaItem)'; + } } @@ -846,60 +1408,60 @@ class _PigeonCodec extends StandardMessageCodec { @override Object? readValueOfType(int type, ReadBuffer buffer) { switch (type) { - case 129: - final int? value = readValue(buffer) as int?; + case 129: + final value = readValue(buffer) as int?; return value == null ? null : BufferMode.values[value]; - case 130: - final int? value = readValue(buffer) as int?; + case 130: + final value = readValue(buffer) as int?; return value == null ? null : RepeatMode.values[value]; - case 131: - final int? value = readValue(buffer) as int?; + case 131: + final value = readValue(buffer) as int?; return value == null ? null : PlaybackState.values[value]; - case 132: - final int? value = readValue(buffer) as int?; + case 132: + final value = readValue(buffer) as int?; return value == null ? null : CastConnectionState.values[value]; - case 133: - final int? value = readValue(buffer) as int?; + case 133: + final value = readValue(buffer) as int?; return value == null ? null : TrackType.values[value]; - case 134: + case 134: return NpawConfig.decode(readValue(buffer)!); - case 135: + case 135: return AppConfig.decode(readValue(buffer)!); - case 136: + case 136: return User.decode(readValue(buffer)!); - case 137: + case 137: return SetUrlArgs.decode(readValue(buffer)!); - case 138: + case 138: return MediaItem.decode(readValue(buffer)!); - case 139: + case 139: return MediaMetadata.decode(readValue(buffer)!); - case 140: + case 140: return PlayerStateSnapshot.decode(readValue(buffer)!); - case 141: + case 141: return PlayerError.decode(readValue(buffer)!); - case 142: + case 142: return VideoSize.decode(readValue(buffer)!); - case 143: + case 143: return ChromecastState.decode(readValue(buffer)!); - case 144: + case 144: return MediaInfo.decode(readValue(buffer)!); - case 145: + case 145: return PlayerTracksSnapshot.decode(readValue(buffer)!); - case 146: + case 146: return Track.decode(readValue(buffer)!); - case 147: + case 147: return PrimaryPlayerChangedEvent.decode(readValue(buffer)!); - case 148: + case 148: return PlayerStateUpdateEvent.decode(readValue(buffer)!); - case 149: + case 149: return PositionDiscontinuityEvent.decode(readValue(buffer)!); - case 150: + case 150: return PlaybackStateChangedEvent.decode(readValue(buffer)!); - case 151: + case 151: return PlaybackEndedEvent.decode(readValue(buffer)!); - case 152: + case 152: return PictureInPictureModeChangedEvent.decode(readValue(buffer)!); - case 153: + case 153: return MediaItemTransitionEvent.decode(readValue(buffer)!); default: return super.readValueOfType(type, buffer); @@ -909,8 +1471,8 @@ class _PigeonCodec extends StandardMessageCodec { /// The main interface, used by the flutter side to control the player. class PlaybackPlatformPigeon { - /// Constructor for [PlaybackPlatformPigeon]. The [binaryMessenger] named argument is - /// available for dependency injection. If it is left null, the default + /// Constructor for [PlaybackPlatformPigeon]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. PlaybackPlatformPigeon({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) : pigeonVar_binaryMessenger = binaryMessenger, @@ -922,512 +1484,404 @@ class PlaybackPlatformPigeon { final String pigeonVar_messageChannelSuffix; Future attach() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.attach$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.attach$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future newPlayer(BufferMode? bufferMode, bool? disableNpaw) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.newPlayer$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.newPlayer$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([bufferMode, disableNpaw]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as String?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([bufferMode, disableNpaw]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as String; } Future createVideoTexture() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.createVideoTexture$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.createVideoTexture$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as int?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as int; } Future disposeVideoTexture(int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.disposeVideoTexture$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.disposeVideoTexture$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([textureId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as bool?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([textureId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as bool; } Future switchToVideoTexture(String playerId, int textureId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.switchToVideoTexture$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.switchToVideoTexture$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, textureId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as int?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, textureId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as int; } Future disposePlayer(String playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.disposePlayer$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.disposePlayer$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as bool?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as bool; } Future replaceCurrentMediaItem(String playerId, MediaItem mediaItem, bool? playbackPositionFromPrimary, bool? autoplay) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.replaceCurrentMediaItem$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.replaceCurrentMediaItem$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, mediaItem, playbackPositionFromPrimary, autoplay]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, mediaItem, playbackPositionFromPrimary, autoplay]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setPlayerViewVisibility(int viewId, bool visible) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setPlayerViewVisibility$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setPlayerViewVisibility$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([viewId, visible]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([viewId, visible]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setPrimary(String id) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setPrimary$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setPrimary$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([id]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([id]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future play(String playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.play$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.play$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future seekTo(String playerId, double positionMs) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.seekTo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.seekTo$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, positionMs]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, positionMs]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future seekToLive(String playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.seekToLive$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.seekToLive$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future pause(String playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.pause$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.pause$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future stop(String playerId, bool reset) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.stop$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.stop$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, reset]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, reset]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setVolume(String playerId, double volume) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setVolume$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setVolume$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, volume]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, volume]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setRepeatMode(String playerId, RepeatMode repeatMode) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setRepeatMode$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setRepeatMode$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, repeatMode]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, repeatMode]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setSelectedTrack(String playerId, TrackType type, String? trackId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setSelectedTrack$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setSelectedTrack$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, type, trackId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, type, trackId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setPlaybackSpeed(String playerId, double speed) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setPlaybackSpeed$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, speed]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, speed]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future exitFullscreen(String playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.exitFullscreen$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.exitFullscreen$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future enterFullscreen(String playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.enterFullscreen$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.enterFullscreen$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setMixWithOthers(String playerId, bool mixWithOthers) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setMixWithOthers$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setMixWithOthers$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, mixWithOthers]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, mixWithOthers]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setNpawConfig(NpawConfig? config) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setNpawConfig$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setNpawConfig$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([config]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([config]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } /// Ends the current NPAW view and starts a fresh one for [playerId]. When @@ -1436,211 +1890,170 @@ class PlaybackPlatformPigeon { /// Used to split a continuous live stream into one NPAW view per program, /// without replacing the media item. Future startNpawView(String playerId, MediaMetadata? metadata) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.startNpawView$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.startNpawView$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId, metadata]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId, metadata]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future setAppConfig(AppConfig? config) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setAppConfig$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.setAppConfig$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([config]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([config]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future getTracks(String? playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getTracks$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getTracks$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return (pigeonVar_replyList[0] as PlayerTracksSnapshot?); - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; + return pigeonVar_replyValue as PlayerTracksSnapshot?; } Future getPlayerState(String? playerId) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getPlayerState$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getPlayerState$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([playerId]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return (pigeonVar_replyList[0] as PlayerStateSnapshot?); - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([playerId]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; + return pigeonVar_replyValue as PlayerStateSnapshot?; } Future getChromecastState() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getChromecastState$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getChromecastState$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return (pigeonVar_replyList[0] as ChromecastState?); - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; + return pigeonVar_replyValue as ChromecastState?; } Future openExpandedCastController() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.openExpandedCastController$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.openExpandedCastController$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future openCastDialog() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.openCastDialog$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.openCastDialog$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: true, + ) + ; } Future fetchMediaInfo(String url, String? mimeType) async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.fetchMediaInfo$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.fetchMediaInfo$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send([url, mimeType]) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as MediaInfo?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send([url, mimeType]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as MediaInfo; } Future getAndroidPerformanceClass() async { - final String pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getAndroidPerformanceClass$pigeonVar_messageChannelSuffix'; - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channelName = 'dev.flutter.pigeon.bccm_player.PlaybackPlatformPigeon.getAndroidPerformanceClass$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( pigeonVar_channelName, pigeonChannelCodec, binaryMessenger: pigeonVar_binaryMessenger, ); - final List? pigeonVar_replyList = - await pigeonVar_channel.send(null) as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as int?)!; - } + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + + final Object? pigeonVar_replyValue = _extractReplyValueOrThrow( + pigeonVar_replyList, + pigeonVar_channelName, + isNullValid: false, + ) + ; + return pigeonVar_replyValue! as int; } } @@ -1656,22 +2069,18 @@ abstract class QueueManagerPigeon { static void setUp(QueueManagerPigeon? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.QueueManagerPigeon.handlePlaybackEnded$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.QueueManagerPigeon.handlePlaybackEnded was null.'); - final List args = (message as List?)!; - final String? arg_playerId = (args[0] as String?); - assert(arg_playerId != null, - 'Argument for dev.flutter.pigeon.bccm_player.QueueManagerPigeon.handlePlaybackEnded was null, expected non-null String.'); - final MediaItem? arg_current = (args[1] as MediaItem?); + final List args = message! as List; + final String arg_playerId = args[0]! as String; + final MediaItem? arg_current = args[1] as MediaItem?; try { - await api.handlePlaybackEnded(arg_playerId!, arg_current); + await api.handlePlaybackEnded(arg_playerId, arg_current); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1682,21 +2091,17 @@ abstract class QueueManagerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.QueueManagerPigeon.skipToNext$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.QueueManagerPigeon.skipToNext was null.'); - final List args = (message as List?)!; - final String? arg_playerId = (args[0] as String?); - assert(arg_playerId != null, - 'Argument for dev.flutter.pigeon.bccm_player.QueueManagerPigeon.skipToNext was null, expected non-null String.'); + final List args = message! as List; + final String arg_playerId = args[0]! as String; try { - await api.skipToNext(arg_playerId!); + await api.skipToNext(arg_playerId); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1707,21 +2112,17 @@ abstract class QueueManagerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.QueueManagerPigeon.skipToPrevious$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.QueueManagerPigeon.skipToPrevious was null.'); - final List args = (message as List?)!; - final String? arg_playerId = (args[0] as String?); - assert(arg_playerId != null, - 'Argument for dev.flutter.pigeon.bccm_player.QueueManagerPigeon.skipToPrevious was null, expected non-null String.'); + final List args = message! as List; + final String arg_playerId = args[0]! as String; try { - await api.skipToPrevious(arg_playerId!); + await api.skipToPrevious(arg_playerId); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1755,21 +2156,17 @@ abstract class PlaybackListenerPigeon { static void setUp(PlaybackListenerPigeon? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPrimaryPlayerChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPrimaryPlayerChanged was null.'); - final List args = (message as List?)!; - final PrimaryPlayerChangedEvent? arg_event = (args[0] as PrimaryPlayerChangedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPrimaryPlayerChanged was null, expected non-null PrimaryPlayerChangedEvent.'); + final List args = message! as List; + final PrimaryPlayerChangedEvent arg_event = args[0]! as PrimaryPlayerChangedEvent; try { - api.onPrimaryPlayerChanged(arg_event!); + api.onPrimaryPlayerChanged(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1780,21 +2177,17 @@ abstract class PlaybackListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPositionDiscontinuity$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPositionDiscontinuity was null.'); - final List args = (message as List?)!; - final PositionDiscontinuityEvent? arg_event = (args[0] as PositionDiscontinuityEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPositionDiscontinuity was null, expected non-null PositionDiscontinuityEvent.'); + final List args = message! as List; + final PositionDiscontinuityEvent arg_event = args[0]! as PositionDiscontinuityEvent; try { - api.onPositionDiscontinuity(arg_event!); + api.onPositionDiscontinuity(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1805,21 +2198,17 @@ abstract class PlaybackListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlayerStateUpdate$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlayerStateUpdate was null.'); - final List args = (message as List?)!; - final PlayerStateUpdateEvent? arg_event = (args[0] as PlayerStateUpdateEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlayerStateUpdate was null, expected non-null PlayerStateUpdateEvent.'); + final List args = message! as List; + final PlayerStateUpdateEvent arg_event = args[0]! as PlayerStateUpdateEvent; try { - api.onPlayerStateUpdate(arg_event!); + api.onPlayerStateUpdate(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1830,21 +2219,17 @@ abstract class PlaybackListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlaybackStateChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlaybackStateChanged was null.'); - final List args = (message as List?)!; - final PlaybackStateChangedEvent? arg_event = (args[0] as PlaybackStateChangedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlaybackStateChanged was null, expected non-null PlaybackStateChangedEvent.'); + final List args = message! as List; + final PlaybackStateChangedEvent arg_event = args[0]! as PlaybackStateChangedEvent; try { - api.onPlaybackStateChanged(arg_event!); + api.onPlaybackStateChanged(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1855,21 +2240,17 @@ abstract class PlaybackListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlaybackEnded$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlaybackEnded was null.'); - final List args = (message as List?)!; - final PlaybackEndedEvent? arg_event = (args[0] as PlaybackEndedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPlaybackEnded was null, expected non-null PlaybackEndedEvent.'); + final List args = message! as List; + final PlaybackEndedEvent arg_event = args[0]! as PlaybackEndedEvent; try { - api.onPlaybackEnded(arg_event!); + api.onPlaybackEnded(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1880,21 +2261,17 @@ abstract class PlaybackListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onMediaItemTransition$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onMediaItemTransition was null.'); - final List args = (message as List?)!; - final MediaItemTransitionEvent? arg_event = (args[0] as MediaItemTransitionEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onMediaItemTransition was null, expected non-null MediaItemTransitionEvent.'); + final List args = message! as List; + final MediaItemTransitionEvent arg_event = args[0]! as MediaItemTransitionEvent; try { - api.onMediaItemTransition(arg_event!); + api.onMediaItemTransition(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); @@ -1905,21 +2282,17 @@ abstract class PlaybackListenerPigeon { } } { - final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + final pigeonVar_channel = BasicMessageChannel( 'dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPictureInPictureModeChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { pigeonVar_channel.setMessageHandler(null); } else { pigeonVar_channel.setMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPictureInPictureModeChanged was null.'); - final List args = (message as List?)!; - final PictureInPictureModeChangedEvent? arg_event = (args[0] as PictureInPictureModeChangedEvent?); - assert(arg_event != null, - 'Argument for dev.flutter.pigeon.bccm_player.PlaybackListenerPigeon.onPictureInPictureModeChanged was null, expected non-null PictureInPictureModeChangedEvent.'); + final List args = message! as List; + final PictureInPictureModeChangedEvent arg_event = args[0]! as PictureInPictureModeChangedEvent; try { - api.onPictureInPictureModeChanged(arg_event!); + api.onPictureInPictureModeChanged(arg_event); return wrapResponse(empty: true); } on PlatformException catch (e) { return wrapResponse(error: e); diff --git a/pubspec.yaml b/pubspec.yaml index 9a710cb..6e2eeec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: bccm_player description: ExoPlayer/AVPlayer via platform views, with cast, PiP, background audio, audio selection, etc. -version: 2.0.0 +version: 2.1.0 documentation: https://bcc-code.github.io/bccm-player/ repository: https://github.com/bcc-code/bccm-player