diff --git a/.github/workflows/run_test.yml b/.github/workflows/run_test.yml index 892dd54b1..9b9f2d8db 100644 --- a/.github/workflows/run_test.yml +++ b/.github/workflows/run_test.yml @@ -6,6 +6,12 @@ concurrency: on: workflow_call: + inputs: + flutter-versions: + description: 'Flutter versions to test (JSON array)' + required: false + default: '["3.32.5"]' + type: string secrets: APP_ID: required: true @@ -71,6 +77,7 @@ jobs: with: name: agora_rtc_engine_docs.zip path: agora_rtc_engine_docs.zip + pub_publish_check: name: pub publish check if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} @@ -84,12 +91,12 @@ jobs: - run: bash ci/dart_pub_publish_check.sh integration_test_android: - name: Run Flutter Android Integration Tests + name: Run Flutter Android Integration Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.0", "3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest timeout-minutes: 120 env: @@ -122,12 +129,12 @@ jobs: script: bash ci/run_flutter_integration_test_android.sh integration_test_ios: - name: Run Flutter iOS Integration Tests + name: Run Flutter iOS Integration Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.0", "3.16"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: macos-latest timeout-minutes: 120 env: @@ -164,12 +171,12 @@ jobs: path: logs-ios/* integration_test_macos: - name: Run Flutter macOS Integration Tests + name: Run Flutter macOS Integration Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.0"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: macos-latest timeout-minutes: 120 env: @@ -209,14 +216,42 @@ jobs: name: iris-logs-macos-${{ matrix.version }} path: iris-logs-macos/* + integration_test_swiftpm: + name: Run Flutter SwiftPM Integration Tests (${{ matrix.os }}) + if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + fail-fast: false + matrix: + os: ["macos", "ios"] + runs-on: macos-latest + timeout-minutes: 120 + steps: + - uses: actions/checkout@v3 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + cache: true + - name: Run flutter integration test swiftpm for ${{ matrix.os }} + if: ${{ matrix.os == 'macos' }} + run: | + flutter pub get + flutter build ${{ matrix.os }} + working-directory: test_shard/integration_test_swiftpm + - name: Run flutter integration test swiftpm for ${{ matrix.os }} + if: ${{ matrix.os == 'ios' }} + run: | + flutter pub get + flutter build ${{ matrix.os }} --no-codesign + working-directory: test_shard/integration_test_swiftpm + integration_test_windows: - name: Run Flutter Windows Integration Tests + name: Run Flutter Windows Integration Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.0", "3.24.5"] - runs-on: windows-2019 + version: ${{ fromJSON(inputs.flutter-versions) }} + runs-on: windows-2022 timeout-minutes: 120 env: TEST_APP_ID: ${{ secrets.APP_ID }} @@ -243,11 +278,12 @@ jobs: path: ./CrashDumps/* integration_test_web: - name: Run Flutter Web Integration Tests + name: Run Flutter Web Integration Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest timeout-minutes: 60 env: @@ -265,12 +301,12 @@ jobs: bash ci/run_flutter_integration_test_web.sh build_android_ubuntu: - name: Build Android on Ubuntu + name: Build Android on Ubuntu ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.12", "3.24.5"] # Need 3.7.12 to build with Gradle 8.x https://github.com/flutter/flutter/issues/124838 + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -289,13 +325,13 @@ jobs: working-directory: example build_android_windows: - name: Build Android on Windows + name: Build Android on Windows ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.12", "3.24.5"] # Need 3.7.12 to build with Gradle 8.x https://github.com/flutter/flutter/issues/124838 - runs-on: windows-2019 + version: ${{ fromJSON(inputs.flutter-versions) }} + runs-on: windows-2022 steps: - uses: actions/checkout@v3 - name: Install JDK @@ -313,12 +349,12 @@ jobs: working-directory: example build_ios: - name: Build iOS + name: Build iOS ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.0"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: macos-latest timeout-minutes: 120 steps: @@ -334,11 +370,12 @@ jobs: # This job aim to cover https://github.com/flutter/flutter/issues/135739 build_ios_xcode_15: - name: Build iOS with xcode 15.x + name: Build iOS with xcode 15.x ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: macos-13 timeout-minutes: 120 steps: @@ -359,12 +396,12 @@ jobs: working-directory: example build_web: - name: Build Web + name: Build Web ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: fail-fast: false matrix: - version: ["3.7.0", "3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest timeout-minutes: 120 steps: @@ -381,10 +418,16 @@ jobs: rendering_test_android: name: Run Flutter Android Rendering Tests if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest timeout-minutes: 120 env: TEST_APP_ID: ${{ secrets.APP_ID }} + GRADLE_OPTS: "-Xmx16384M -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" + ORG_GRADLE_PROJECT_GRADLE_OPTS: "-Xmx8192M -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" steps: - uses: actions/checkout@v3 - name: Install JDK @@ -394,7 +437,7 @@ jobs: java-version: '17' - uses: subosito/flutter-action@v2 with: - flutter-version: "3.24.5" + flutter-version: ${{ matrix.version }} cache: true - name: Enable KVM run: | @@ -407,9 +450,9 @@ jobs: api-level: 31 arch: x86_64 profile: pixel_5 - ram-size: 2048M - heap-size: 4096M - disk-size: 8192M + ram-size: 4096M + heap-size: 8192M + disk-size: 16384M script: bash ci/rendering_test_android.sh - uses: actions/upload-artifact@v4 @@ -419,11 +462,12 @@ jobs: path: test_shard/rendering_test/screenshot/*.debug.png rendering_test_ios: - name: Run Flutter iOS Rendering Tests + name: Run Flutter iOS Rendering Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: macos-13 # Rendering test on ios simulator need macos 13+ timeout-minutes: 60 env: @@ -446,11 +490,12 @@ jobs: path: test_shard/rendering_test/screenshot/*.debug.png rendering_test_macos: - name: Run Flutter macOS Rendering Tests + name: Run Flutter macOS Rendering Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: macos-latest timeout-minutes: 120 env: @@ -473,12 +518,13 @@ jobs: path: test_shard/rendering_test/screenshot/*.debug.png rendering_test_windows: - name: Run Flutter Windows Rendering Tests + name: Run Flutter Windows Rendering Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] - runs-on: windows-2019 + version: ${{ fromJSON(inputs.flutter-versions) }} + runs-on: windows-2022 timeout-minutes: 120 env: TEST_APP_ID: ${{ secrets.APP_ID }} @@ -502,11 +548,12 @@ jobs: path: test_shard/rendering_test/screenshot/*.debug.png rendering_test_web: - name: Run Flutter Web Rendering Tests + name: Run Flutter Web Rendering Tests ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest timeout-minutes: 60 env: @@ -532,11 +579,12 @@ jobs: path: test_shard/rendering_test/screenshot/*.debug.png check_android15_16k_page_alignment: - name: Check android15 16k page size alignment + name: Check android15 16k page size alignment ( ${{ matrix.version }}) if: ${{ !contains(github.event.pull_request.labels.*.name, 'ci:skip') }} strategy: + fail-fast: false matrix: - version: ["3.24.5"] + version: ${{ fromJSON(inputs.flutter-versions) }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index d6d489e28..9b8b68cb9 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ third_party/ test_shard/**/scripts/iris_web_version.js test_shard/**/web/iris_web_version.js -artifacts \ No newline at end of file +artifacts +DerivedData \ No newline at end of file diff --git a/.pubignore b/.pubignore index c59a4a52b..86263a312 100644 --- a/.pubignore +++ b/.pubignore @@ -1,3 +1,6 @@ integration_test_app/ tool/ -test_shard/ \ No newline at end of file +test_shard/ +docs/ +ci/ +scripts/ \ No newline at end of file diff --git a/README.md b/README.md index ef4a21127..00a6234e3 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,10 @@ Please refer to the [Flutter documentation](https://docs.flutter.dev/platform-in * [Windows](https://api-ref.agora.io/en/video-sdk/cpp/4.x/API/rtc_api_overview_ng.html) * [Web](https://api-ref.agora.io/en/video-sdk/web/4.x/index.html) +## Integration document + +* [Picture-in-Picture](docs/integration/Picture-in-Picture.md) + ## Feedback If you have any problems or suggestions regarding the sample projects, feel free to file an [issue](https://github.com/AgoraIO-Community/agora_rtc_engine/issues) OR pull request. diff --git a/android/build.gradle b/android/build.gradle index fc0cdd45a..67e9d24bc 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -58,7 +58,7 @@ dependencies { api fileTree(dir: "libs", include: ["*.jar"]) } else { // iris dependencies start - api 'io.agora.rtc:iris-rtc:4.5.2-build.1' + api 'io.agora.rtc:iris-rtc:4.5.2.140-build.4' // iris dependencies end // native dependencies start diff --git a/android/src/main/cpp/iris_rtc_rendering_android.cc b/android/src/main/cpp/iris_rtc_rendering_android.cc index ff4d6e171..adaf4133f 100644 --- a/android/src/main/cpp/iris_rtc_rendering_android.cc +++ b/android/src/main/cpp/iris_rtc_rendering_android.cc @@ -701,7 +701,6 @@ class NativeTextureRenderer final env->DeleteLocalRef(j_caller_class); native_windows_ = ANativeWindow_fromSurface(env, surface_jni); - gl_context_ = std::make_shared(native_windows_); IrisRtcVideoFrameConfig config; config.uid = uid; @@ -739,6 +738,13 @@ class NativeTextureRenderer final NotifySizeChangeCallback(video_frame->width, video_frame->height); width_ = video_frame->width; height_ = video_frame->height; + + rendering_op_.reset(); + gl_context_.reset(); + } + + if (!gl_context_) { + gl_context_ = std::make_shared(native_windows_); } if (!gl_context_->SetupSurface(native_windows_)) { diff --git a/android/src/main/java/io/agora/agora_rtc_ng/AgoraPIPFlutterActivity.java b/android/src/main/java/io/agora/agora_rtc_ng/AgoraPIPFlutterActivity.java new file mode 100644 index 000000000..fa522e1b6 --- /dev/null +++ b/android/src/main/java/io/agora/agora_rtc_ng/AgoraPIPFlutterActivity.java @@ -0,0 +1,106 @@ +package io.agora.agora_rtc_ng; + +import io.flutter.embedding.android.FlutterActivity; + +import android.app.PictureInPictureParams; +import android.app.PictureInPictureUiState; +import android.content.Context; +import android.content.res.Configuration; + +import java.lang.ref.WeakReference; + +import io.agora.iris.pip.AgoraPIPActivityProxy; +import io.agora.iris.pip.AgoraPIPActivityListener; + +public class AgoraPIPFlutterActivity extends FlutterActivity implements AgoraPIPActivityProxy { + private WeakReference mListener; + + @Override + public Context getApplicationContext() { + return super.getApplicationContext(); + } + + @Override + public void setAgoraPIPActivityListener(AgoraPIPActivityListener listener) { + mListener = new WeakReference<>(listener); + } + + @Override + public boolean isInPictureInPictureMode() { + return super.isInPictureInPictureMode(); + } + + @Override + public void setPictureInPictureParams(PictureInPictureParams params) { + super.setPictureInPictureParams(params); + } + + @Override + public boolean enterPictureInPictureMode(PictureInPictureParams params) { + return super.enterPictureInPictureMode(params); + } + + @Override + public void enterPictureInPictureMode() { + super.enterPictureInPictureMode(); + } + + @Override + public boolean moveTaskToBack(boolean nonRoot) { + return super.moveTaskToBack(nonRoot); + } + + @Override + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); + if (mListener == null) { + return; + } + + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + listener.onPictureInPictureModeChanged(isInPictureInPictureMode, + newConfig); + } + } + + @Override + public boolean onPictureInPictureRequested() { + if (mListener != null) { + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + return listener.onPictureInPictureRequested(); + } + } + + + return super.onPictureInPictureRequested(); + } + + @Override + public void onPictureInPictureUiStateChanged(PictureInPictureUiState state) { + super.onPictureInPictureUiStateChanged(state); + if (mListener == null) { + return; + } + + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + listener.onPictureInPictureUiStateChanged(state); + } + } + + @Override + public void onUserLeaveHint() { + super.onUserLeaveHint(); + if (mListener == null) { + return; + } + + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + listener.onUserLeaveHint(); + } + } +} diff --git a/android/src/main/java/io/agora/agora_rtc_ng/AgoraPIPFlutterFragmentActivity.java b/android/src/main/java/io/agora/agora_rtc_ng/AgoraPIPFlutterFragmentActivity.java new file mode 100644 index 000000000..cf020aa9a --- /dev/null +++ b/android/src/main/java/io/agora/agora_rtc_ng/AgoraPIPFlutterFragmentActivity.java @@ -0,0 +1,106 @@ +package io.agora.agora_rtc_ng; + +import io.flutter.embedding.android.FlutterFragmentActivity; + +import android.app.PictureInPictureParams; +import android.app.PictureInPictureUiState; +import android.content.Context; +import android.content.res.Configuration; + +import java.lang.ref.WeakReference; + +import io.agora.iris.pip.AgoraPIPActivityProxy; +import io.agora.iris.pip.AgoraPIPActivityListener; + +public class AgoraPIPFlutterFragmentActivity extends FlutterFragmentActivity implements AgoraPIPActivityProxy { + private WeakReference mListener; + + @Override + public Context getApplicationContext() { + return super.getApplicationContext(); + } + + @Override + public void setAgoraPIPActivityListener(AgoraPIPActivityListener listener) { + mListener = new WeakReference<>(listener); + } + + @Override + public boolean isInPictureInPictureMode() { + return super.isInPictureInPictureMode(); + } + + @Override + public void setPictureInPictureParams(PictureInPictureParams params) { + super.setPictureInPictureParams(params); + } + + @Override + public boolean enterPictureInPictureMode(PictureInPictureParams params) { + return super.enterPictureInPictureMode(params); + } + + @Override + public void enterPictureInPictureMode() { + super.enterPictureInPictureMode(); + } + + @Override + public boolean moveTaskToBack(boolean nonRoot) { + return super.moveTaskToBack(nonRoot); + } + + @Override + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); + if (mListener == null) { + return; + } + + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + listener.onPictureInPictureModeChanged(isInPictureInPictureMode, + newConfig); + } + } + + @Override + public boolean onPictureInPictureRequested() { + if (mListener != null) { + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + return listener.onPictureInPictureRequested(); + } + } + + + return super.onPictureInPictureRequested(); + } + + @Override + public void onPictureInPictureUiStateChanged(PictureInPictureUiState state) { + super.onPictureInPictureUiStateChanged(state); + if (mListener == null) { + return; + } + + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + listener.onPictureInPictureUiStateChanged(state); + } + } + + @Override + public void onUserLeaveHint() { + super.onUserLeaveHint(); + if (mListener == null) { + return; + } + + AgoraPIPActivityListener listener = mListener.get(); + if (listener != null) { + listener.onUserLeaveHint(); + } + } +} diff --git a/android/src/main/java/io/agora/agora_rtc_ng/AgoraRtcNgPlugin.java b/android/src/main/java/io/agora/agora_rtc_ng/AgoraRtcNgPlugin.java index 958248a2a..60d2b5a27 100644 --- a/android/src/main/java/io/agora/agora_rtc_ng/AgoraRtcNgPlugin.java +++ b/android/src/main/java/io/agora/agora_rtc_ng/AgoraRtcNgPlugin.java @@ -1,23 +1,33 @@ package io.agora.agora_rtc_ng; +import android.app.Activity; import android.content.Context; +import android.graphics.Rect; +import android.os.Build; +import android.util.Rational; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.HashMap; - +import io.agora.iris.pip.AgoraPIPActivityProxy; +import io.agora.iris.pip.AgoraPIPController; import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.embedding.engine.plugins.activity.ActivityAware; +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -public class AgoraRtcNgPlugin implements FlutterPlugin, MethodChannel.MethodCallHandler { +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; + +public class AgoraRtcNgPlugin implements FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware { private MethodChannel channel; private WeakReference flutterPluginBindingRef; private VideoViewController videoViewController; + private AgoraPIPController pipController; @Nullable private Context applicationContext; @@ -65,6 +75,8 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result System.loadLibrary("AgoraRtcWrapper"); result.success(true); + } else if (call.method.startsWith("pip")) { + handlePipMethodCall(call, result); } else { result.notImplemented(); } @@ -94,4 +106,140 @@ private void getAssetAbsolutePath(MethodCall call, MethodChannel.Result result) } result.error("IllegalArgumentException", "The parameter should not be null", null); } + + private void initPipController(@NonNull ActivityPluginBinding binding) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + + Activity activity = binding.getActivity(); + if (!(activity instanceof AgoraPIPActivityProxy)) { + return; + } + + if (pipController != null) { + pipController.dispose(); + } + + pipController = new AgoraPIPController( + (AgoraPIPActivityProxy) activity, + new AgoraPIPController.PIPStateChangedListener() { + @Override + public void onPIPStateChangedListener( + AgoraPIPController.PIPState state, String error) { + // put state into a json object + channel.invokeMethod("pipStateChanged", + new HashMap() { + { + put("state", state.getValue()); + put("error", error != null ? error : ""); + } + }); + } + }); + } + } + + private void handlePipMethodCall(@NonNull MethodCall call, + @NonNull MethodChannel.Result result) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || + pipController == null) { + result.error("IllegalStateException", "PiP is not supported", + "Picture-in-Picture mode is not available on this device (requires Android 8.0 or higher) or the main activity does not implement AgoraPIPActivityProxy"); + return; + } + + try { + switch (call.method) { + case "pipIsSupported": + result.success(pipController.isSupported()); + break; + case "pipIsAutoEnterSupported": + result.success(pipController.isAutoEnterSupported()); + break; + case "pipIsActivated": + result.success(pipController.isActivated()); + break; + case "pipSetup": + final Map args = (Map) call.arguments; + Rational aspectRatio = null; + if (args.get("aspectRatioX") != null && + args.get("aspectRatioY") != null) { + aspectRatio = new Rational((int) args.get("aspectRatioX"), + (int) args.get("aspectRatioY")); + } + Boolean autoEnterEnabled = null; + if (args.get("autoEnterEnabled") != null) { + autoEnterEnabled = (boolean) args.get("autoEnterEnabled"); + } + Rect sourceRectHint = null; + if (args.get("sourceRectHintLeft") != null && + args.get("sourceRectHintTop") != null && + args.get("sourceRectHintRight") != null && + args.get("sourceRectHintBottom") != null) { + sourceRectHint = + new Rect((int) args.get("sourceRectHintLeft"), + (int) args.get("sourceRectHintTop"), + (int) args.get("sourceRectHintRight"), + (int) args.get("sourceRectHintBottom")); + } + Boolean seamlessResizeEnabled = null; + if (args.get("seamlessResizeEnabled") != null) { + seamlessResizeEnabled = + (boolean) args.get("seamlessResizeEnabled"); + } + Boolean useExternalStateMonitor = null; + if (args.get("useExternalStateMonitor") != null) { + useExternalStateMonitor = + (boolean) args.get("useExternalStateMonitor"); + } + Integer externalStateMonitorInterval = null; + if (args.get("externalStateMonitorInterval") != null) { + externalStateMonitorInterval = + (int) args.get("externalStateMonitorInterval"); + } + + result.success(pipController.setup( + aspectRatio, autoEnterEnabled, sourceRectHint, + seamlessResizeEnabled, useExternalStateMonitor, + externalStateMonitorInterval)); + break; + case "pipStart": + result.success(pipController.start()); + break; + case "pipStop": + pipController.stop(); + result.success(null); + break; + case "pipDispose": + pipController.dispose(); + result.success(null); + break; + default: + result.notImplemented(); + } + } catch (Exception e) { + result.error(e.getClass().getSimpleName(), e.getMessage(), + e.getCause()); + } + } + + @Override + public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) { + initPipController(binding); + } + + @Override + public void onDetachedFromActivityForConfigChanges() { + // do nothing + } + + @Override + public void onReattachedToActivityForConfigChanges( + @NonNull ActivityPluginBinding binding) { + initPipController(binding); + } + + @Override + public void onDetachedFromActivity() { + // do nothing + } } diff --git a/docs/integration/Picture-in-Picture.md b/docs/integration/Picture-in-Picture.md new file mode 100644 index 000000000..33c9085a0 --- /dev/null +++ b/docs/integration/Picture-in-Picture.md @@ -0,0 +1,317 @@ +# Picture-in-Picture (PiP) + +## Overview + +The Picture-in-Picture (PiP) feature allows you to display video content in a small floating window while users interact with other parts of your app. You can show local and remote video streams in the PiP window, or display your own custom UI content. This feature is supported on Android and iOS platforms. + +## Features + +- Custom control style for PiP window (iOS only) +- Automatic PiP mode activation when app goes to background +- Customizable PiP window size and aspect ratio +- Dynamic size/aspect ratio adjustment during active PiP mode +- Support for multiple video streams in PiP mode +- Custom content view integration with PiP window +- Flexible layout configuration for multiple video streams in PiP mode + +## Platform Support + +- iOS: 15.0 and above +- Android: 8.0 and above + +## Integration Guide + +### Android Setup + +1. **Declare PiP Support in AndroidManifest.xml** + + > For detailed information, see [Add videos using picture-in-picture (PiP)](https://developer.android.com/develop/ui/views/picture-in-picture#declaring) + + ```xml + According to the [Switch your activity to PiP](https://developer.android.com/develop/ui/views/picture-in-picture#pip_button), automatic PiP mode entry when the app goes to background is only supported on Android 12 and above. For earlier Android versions, you need to explicitly call `enterPictureInPictureMode()` in `onUserLeaveHint()`. This functionality is already implemented in `AgoraPIPFlutterActivity` and `AgoraPIPFlutterFragmentActivity`, so you don't need to implement it yourself. However, if you want to customize the behavior, you can implement `AgoraPIPActivityProxy` interface and override its methods in your own activity. + + ```kotlin + import io.agora.agora_rtc_ng.AgoraPIPFlutterActivity + + class MainActivity: AgoraPIPFlutterActivity() { + ... + } + ``` + + or + + ```kotlin + import io.agora.agora_rtc_ng.AgoraPIPFlutterFragmentActivity + + class MainActivity: AgoraPIPFlutterFragmentActivity() { + ... + } + ``` + + Example: [MainActivity.kt](../../example/android/app/src/main/kotlin/io/agora/agora_rtc_flutter_example/MainActivity.kt#L11) + +### iOS Setup + +1. **Configure Media Playback Capability** + + > For detailed information, see [Configuring your app for media playback](https://developer.apple.com/documentation/avfoundation/configuring-your-app-for-media-playback?language=objc) + + Steps in Xcode: + + 1. Select your app's target and go to Signing & Capabilities tab + 2. Click + Capability button + 3. Add Background Modes capability + 4. Select "Audio, AirPlay, and Picture in Picture" under Background Modes + + Additional Resources: + + - [Background Execution Modes](https://developer.apple.com/documentation/xcode/configuring-background-execution-modes#Specify-the-background-modes-your-app-requires) + - [Adding Capabilities](https://developer.apple.com/documentation/xcode/adding-capabilities-to-your-app#Add-a-capability) + +2. **Camera Access in Multitasking Mode (Optional)** + > Note: You can skip this step if your app doesn't require camera access during multitasking (for example, if you don't need to show the local video stream in the PiP window). + + > When your app enters a multitasking mode, you should have [com.apple.developer.avfoundation.multitasking-camera-access](https://developer.apple.com/documentation/BundleResources/Entitlements/com.apple.developer.avfoundation.multitasking-camera-access?language=objc) entitlement or set `multitaskingCameraAccessEnabled` to `true` of the capture session. Multitasking modes include Slide Over, Split View, and Picture in Picture (PiP). + + > To learn about best practices for using the camera while multitasking, see [Accessing the camera while multitasking on iPad](https://developer.apple.com/documentation/avkit/accessing-the-camera-while-multitasking-on-ipad?language=objc). + + Requirements: + + - iOS < 16: Requires [com.apple.developer.avfoundation.multitasking-camera-access](https://developer.apple.com/documentation/BundleResources/Entitlements/com.apple.developer.avfoundation.multitasking-camera-access?language=objc) entitlement + - [Contact Apple](https://developer.apple.com/contact/request/multitasking-camera-access/) for permission + - iOS ≥ 16: Set `multitaskingCameraAccessEnabled` to `true` in capture session (coming soon) + +### Flutter Implementation + +> Complete example: [picture_in_picture.dart](../../example/lib/examples/advanced/picture_in_picture/picture_in_picture.dart) + +1. **Basic Setup** + + ```dart + import 'package:agora_rtc_engine/agora_rtc_engine.dart'; + + // Declare controllers + late final RtcEngine _engine; + late final AgoraPipController _pipController; + + // Create and initialize RtcEngine + _engine = createAgoraRtcEngine(); + await _engine.initialize(RtcEngineContext( + // ... configuration + )); + + // Create PiP controller + _pipController = _engine.createPipController(); + ``` + +2. **Configure PiP State Observer** + + ```dart + _pipController.registerPipStateChangedObserver(AgoraPipStateChangedObserver( + onPipStateChanged: (state, error) { + // Handle state changes + }, + )); + + // Check PiP support + var isPipSupported = await _pipController.pipIsSupported(); + var isPipAutoEnterSupported = await _pipController.pipIsAutoEnterSupported(); + ``` + +3. **Configure PiP Options** + + **Android Configuration:** + + ```dart + AgoraPipOptions options = AgoraPipOptions( + // Setting autoEnterEnabled to true enables seamless transition to PiP mode when the app enters background, + // providing the best user experience recommended by both Android and iOS platforms. + autoEnterEnabled: isPipAutoEnterSupported, + + // Keep the aspect ratio same as the video view. The aspectRatioX and aspectRatioY values + // should match your video dimensions for optimal display. For example, for 1080p video, + // use 16:9 ratio (1920:1080 simplified to 16:9). + aspectRatioX: 16, + aspectRatioY: 9, + + // According to https://developer.android.com/develop/ui/views/picture-in-picture#set-sourcerecthint + // The sourceRectHint defines the initial position and size of the PiP window during the transition animation. + // Setting proper values helps create a smooth animation from your video view to the PiP window. + // If not set correctly, the system may apply a default content overlay, resulting in a jarring transition. + sourceRectHintLeft: 0, + sourceRectHintTop: 0, + sourceRectHintRight: 0, + sourceRectHintBottom: 0, + + // According to https://developer.android.com/develop/ui/views/picture-in-picture#seamless-resizing + // The seamlessResizeEnabled flag enables smooth resizing of the PiP window. + // Set this to true for video content to allow continuous playback during resizing. + // Set this to false for non-video content where seamless resizing isn't needed. + seamlessResizeEnabled: true, + + // The external state monitor checks the PiP view state at the interval specified by externalStateMonitorInterval (100ms). + // This is necessary because FlutterActivity does not forward PiP state change events to the Flutter side. + // Even if your Activity is a subclass of AgoraPIPFlutterActivity, you can still use the external state monitor to track PiP state changes. + useExternalStateMonitor: false, + externalStateMonitorInterval: 100, + ); + ``` + + **iOS Configuration:** + + ```dart + AgoraPipOptions options = AgoraPipOptions( + // Setting autoEnterEnabled to true enables seamless transition to PiP mode when the app enters background, + // providing the best user experience recommended by both Android and iOS platforms. + autoEnterEnabled: isPipAutoEnterSupported, + + // Use preferredContentWidth and preferredContentHeight to set the size of the PIP window. + // These values determine the initial dimensions and can be adjusted while PIP is active. + // For optimal user experience, we recommend matching these dimensions to your video view size. + // The system may adjust the final window size to maintain system constraints. + preferredContentWidth: 1080, + preferredContentHeight: 720, + + // The sourceContentView determines the source frame for the PiP animation and restore target. + // Pass 0 to use the app's root view. For optimal animation, set this to the view containing + // your video content. The system uses this view for the PiP enter/exit animations and as the + // restore target when returning to the app or stopping PiP. + sourceContentView: 0, + + // The contentView determines which view will be displayed in the PIP window. + // If you pass 0, the PIP controller will automatically manage and display all video streams. + // If you pass a specific view ID, you become responsible for managing the content shown in the PIP window. + contentView: 0, // force to use native view + + // The contentViewLayout determines the layout of video streams in the PIP window. + // You can customize the grid layout by specifying: + // - padding: Space between the window edge and content (in pixels) + // - spacing: Space between video streams (in pixels) + // - row: Number of rows in the grid layout + // - column: Number of columns in the grid layout + // + // The SDK provides a basic grid layout system that arranges video streams in a row x column matrix. + // For example: + // - row=2, column=2: Up to 4 video streams in a 2x2 grid + // - row=1, column=2: Up to 2 video streams side by side + // - row=2, column=1: Up to 2 video streams stacked vertically + // + // Note: + // - This layout configuration only takes effect when contentView is 0 (using native view) + // - The grid layout is filled from left-to-right, top-to-bottom + // - Empty cells will be left blank if there are fewer streams than grid spaces + // - For custom layouts beyond the grid system, set contentView to your own view ID + contentViewLayout: AgoraPipContentViewLayout( + padding: 0, + spacing: 2, + row: 2, + column: 2, + ), + + // The videoStreams array specifies which video streams to display in the PIP window. + // Each stream can be configured with properties like uid, sourceType, setupMode, and renderMode. + // Note: + // - This configuration only takes effect when contentView is set to 0 (native view mode). + // - The streams will be laid out according to the contentViewLayout grid configuration. + // - The order of the video streams in the array determines the display order in the PIP window. + // - The SDK will automatically create and manage native views for each video stream. + // - The view property in VideoCanvas will be replaced by the SDK-managed native view. + // - You can customize the rendering of each stream using properties like renderMode and mirrorMode. + videoStreams: [ + AgoraPipVideoStream( + connection: RtcConnection( + channelId: 'channelId', + localUid: 0, + ), + canvas: const VideoCanvas( + uid: 0, + view: 0, // will be replaced by native view + sourceType: VideoSourceType.videoSourceCamera, + setupMode: VideoViewSetupMode.videoViewSetupAdd, + renderMode: RenderModeType.renderModeHidden, + // ... other properties + ), + ), + ..._remoteUsers.entries.map((entry) => AgoraPipVideoStream( + connection: entry.value, + canvas: VideoCanvas( + uid: entry.key, + view: 0, // will be replaced by native view + sourceType: VideoSourceType.videoSourceRemote, + setupMode: VideoViewSetupMode.videoViewSetupAdd, + renderMode: RenderModeType.renderModeHidden), + // ... other properties + )), + ], + + // The controlStyle property determines which controls are visible in the PiP window. + // Available styles: + // * 0: Show all system controls (default) - includes play/pause, forward/backward, close and restore buttons + // * 1: Hide forward and backward buttons - shows only play/pause, close and restore buttons + // * 2: Hide play/pause button and progress bar - shows only close and restore buttons (recommended) + // * 3: Hide all system controls - no buttons visible, including close and restore + // + // Note: For most video conferencing use cases, style 2 is recommended since playback controls + // are not relevant and may confuse users. The close and restore buttons provide essential + // window management functionality. + // Note: We do not handle the event of other controls, so the recommended style is 2 or 3. + controlStyle: 2, + ); + ``` + +4. **PiP Lifecycle Management** + + ```dart + // Setup PiP + await _pipController.pipSetup(options); + + // Start PiP (iOS: Must be user-initiated) + // Important: On iOS, Picture-in-Picture playback must only be initiated in response to explicit user actions (e.g. tapping a button). + // Starting PiP programmatically or automatically may result in App Store rejection. + // For more details, see Apple's guidelines on [Handle User-Initiated Requests](https://developer.apple.com/documentation/avkit/adopting-picture-in-picture-in-a-custom-player?language=objc#Handle-User-Initiated-Requests). + await _pipController.pipStart(); + + // Stop PiP + await _pipController.pipStop(); + + // Cleanup PiP resources + // This will stop PiP mode and dispose any native views created by the PiP controller. + // To use PiP functionality again, you'll need to call pipSetup with new options. + // Note that this method only cleans up PiP-related resources - it does not dispose the controller itself. + await _pipController.pipDispose(); + + // Dispose controller (prevent memory leaks) + // This will stop PiP mode, dispose any native views created by the PiP controller, + // and clean up associated resources. After calling dispose(), this AgoraPipController + // instance becomes invalid and should not be used again. Create a new instance if you + // need to use PiP functionality again. + await _pipController.dispose(); + ``` + +## Important Notes + +1. **iOS User Initiation Requirement** + + > PiP must be initiated by user action on iOS. Programmatic or automatic activation may result in App Store rejection. See [Handle User-Initiated Requests](https://developer.apple.com/documentation/avkit/adopting-picture-in-picture-in-a-custom-player?language=objc#Handle-User-Initiated-Requests) + +2. **Memory Management** + + - Always dispose `AgoraPipController` when no longer needed + - Failure to dispose may result in memory leaks + +3. **Control Styles (iOS)** + - 0: All system controls (default) + - 1: Hide forward/backward buttons + - 2: Hide play/pause and progress bar (recommended for video conferencing) + - 3: Hide all controls diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index fd7ed07e6..bf75a4527 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,3 +1,8 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -21,14 +26,10 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { namespace "io.agora.agora_rtc_ng_example" - compileSdkVersion 34 + compileSdkVersion 35 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -47,7 +48,7 @@ android { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "io.agora.agora_rtc_ng_example" minSdkVersion 21 - targetSdkVersion flutter.targetSdkVersion + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 57c1fe42e..6ad7ff15e 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" + android:supportsPictureInPicture="true" android:windowSoftInputMode="adjustResize"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_swiftpm/ios/Runner/Base.lproj/Main.storyboard b/test_shard/integration_test_swiftpm/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 000000000..f3c28516f --- /dev/null +++ b/test_shard/integration_test_swiftpm/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_swiftpm/ios/Runner/Info.plist b/test_shard/integration_test_swiftpm/ios/Runner/Info.plist new file mode 100644 index 000000000..7d0aeaee9 --- /dev/null +++ b/test_shard/integration_test_swiftpm/ios/Runner/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Integration Test Swiftpm + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + integration_test_swiftpm + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/test_shard/integration_test_swiftpm/ios/Runner/Runner-Bridging-Header.h b/test_shard/integration_test_swiftpm/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 000000000..308a2a560 --- /dev/null +++ b/test_shard/integration_test_swiftpm/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/test_shard/integration_test_swiftpm/ios/RunnerTests/RunnerTests.swift b/test_shard/integration_test_swiftpm/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..86a7c3b1b --- /dev/null +++ b/test_shard/integration_test_swiftpm/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/test_shard/integration_test_swiftpm/lib/main.dart b/test_shard/integration_test_swiftpm/lib/main.dart new file mode 100644 index 000000000..7b7f5b6f3 --- /dev/null +++ b/test_shard/integration_test_swiftpm/lib/main.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // TRY THIS: Try running your application with "flutter run". You'll see + // the application has a purple toolbar. Then, without quitting the app, + // try changing the seedColor in the colorScheme below to Colors.green + // and then invoke "hot reload" (save your changes or press the "hot + // reload" button in a Flutter-supported IDE, or press "r" if you used + // the command line to start the app). + // + // Notice that the counter didn't reset back to zero; the application + // state is not lost during the reload. To reset the state, use hot + // restart instead. + // + // This works for code too, not just values: Most code changes can be + // tested with just a hot reload. + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + // + // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" + // action in the IDE, or press "p" in the console), to see the + // wireframe for each widget. + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('You have pushed the button this many times:'), + Text( + '$_counter', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/test_shard/integration_test_swiftpm/macos/.gitignore b/test_shard/integration_test_swiftpm/macos/.gitignore new file mode 100644 index 000000000..746adbb6b --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/test_shard/integration_test_swiftpm/macos/Flutter/Flutter-Debug.xcconfig b/test_shard/integration_test_swiftpm/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/test_shard/integration_test_swiftpm/macos/Flutter/Flutter-Release.xcconfig b/test_shard/integration_test_swiftpm/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..c2efd0b60 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/project.pbxproj b/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..4cd6ad0cd --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,727 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* integration_test_swiftpm.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "integration_test_swiftpm.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* integration_test_swiftpm.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* integration_test_swiftpm.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.integrationTestSwiftpm.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/integration_test_swiftpm.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/integration_test_swiftpm"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.integrationTestSwiftpm.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/integration_test_swiftpm.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/integration_test_swiftpm"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.integrationTestSwiftpm.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/integration_test_swiftpm.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/integration_test_swiftpm"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..7032703e9 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner.xcworkspace/contents.xcworkspacedata b/test_shard/integration_test_swiftpm/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1d526a16e --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/test_shard/integration_test_swiftpm/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner/AppDelegate.swift b/test_shard/integration_test_swiftpm/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..b3c176141 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 000000000..82b6f9d9a Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 000000000..13b35eba5 Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 000000000..0a3f5fa40 Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 000000000..bdb57226d Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 000000000..f083318e0 Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 000000000..326c0e72c Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 000000000..2f1632cfd Binary files /dev/null and b/test_shard/integration_test_swiftpm/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Base.lproj/MainMenu.xib b/test_shard/integration_test_swiftpm/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..80e867a4e --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Configs/AppInfo.xcconfig b/test_shard/integration_test_swiftpm/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..6e4d4f791 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = integration_test_swiftpm + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.integrationTestSwiftpm + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 com.example. All rights reserved. diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Configs/Debug.xcconfig b/test_shard/integration_test_swiftpm/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Configs/Release.xcconfig b/test_shard/integration_test_swiftpm/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Configs/Warnings.xcconfig b/test_shard/integration_test_swiftpm/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/test_shard/integration_test_swiftpm/macos/Runner/DebugProfile.entitlements b/test_shard/integration_test_swiftpm/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..dddb8a30c --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Info.plist b/test_shard/integration_test_swiftpm/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/test_shard/integration_test_swiftpm/macos/Runner/MainFlutterWindow.swift b/test_shard/integration_test_swiftpm/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..3cc05eb23 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/test_shard/integration_test_swiftpm/macos/Runner/Release.entitlements b/test_shard/integration_test_swiftpm/macos/Runner/Release.entitlements new file mode 100644 index 000000000..852fa1a47 --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/test_shard/integration_test_swiftpm/macos/RunnerTests/RunnerTests.swift b/test_shard/integration_test_swiftpm/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 000000000..61f3bd1fc --- /dev/null +++ b/test_shard/integration_test_swiftpm/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/test_shard/integration_test_swiftpm/pubspec.yaml b/test_shard/integration_test_swiftpm/pubspec.yaml new file mode 100644 index 000000000..46912c3ba --- /dev/null +++ b/test_shard/integration_test_swiftpm/pubspec.yaml @@ -0,0 +1,92 @@ +name: integration_test_swiftpm +description: "A new Flutter project." +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.8.1 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + + agora_rtc_engine: + path: ../../ + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^5.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/test_shard/integration_test_swiftpm/test/widget_test.dart b/test_shard/integration_test_swiftpm/test/widget_test.dart new file mode 100644 index 000000000..1604579c6 --- /dev/null +++ b/test_shard/integration_test_swiftpm/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:integration_test_swiftpm/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/test_shard/iris_tester/android/build.gradle b/test_shard/iris_tester/android/build.gradle index 9ed52b4e5..083346138 100644 --- a/test_shard/iris_tester/android/build.gradle +++ b/test_shard/iris_tester/android/build.gradle @@ -26,7 +26,10 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 34 + if (project.android.hasProperty("namespace")) { + namespace 'com.example.iris_tester' + } + compileSdkVersion 35 compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/test_shard/iris_tester/example/android/app/build.gradle b/test_shard/iris_tester/example/android/app/build.gradle index ed16e9786..221bb00bc 100644 --- a/test_shard/iris_tester/example/android/app/build.gradle +++ b/test_shard/iris_tester/example/android/app/build.gradle @@ -1,3 +1,8 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -21,10 +26,6 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion diff --git a/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties b/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties index cc5527d78..514d9bb54 100644 --- a/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/test_shard/iris_tester/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip diff --git a/test_shard/iris_tester/example/android/settings.gradle b/test_shard/iris_tester/example/android/settings.gradle index 44e62bcf0..73ec5fcd8 100644 --- a/test_shard/iris_tester/example/android/settings.gradle +++ b/test_shard/iris_tester/example/android/settings.gradle @@ -1,11 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.10" apply false +} + +include ':app' -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/test_shard/rendering_test/android/app/build.gradle b/test_shard/rendering_test/android/app/build.gradle index ad8cc0050..a728bd699 100644 --- a/test_shard/rendering_test/android/app/build.gradle +++ b/test_shard/rendering_test/android/app/build.gradle @@ -1,3 +1,8 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -21,11 +26,12 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - android { + // Conditional for compatibility with AGP <4.2. + if (project.android.hasProperty("namespace")) { + namespace 'com.example.rendering_test' + } + compileSdkVersion flutter.compileSdkVersion ndkVersion flutter.ndkVersion diff --git a/test_shard/rendering_test/android/gradle/wrapper/gradle-wrapper.properties b/test_shard/rendering_test/android/gradle/wrapper/gradle-wrapper.properties index cc5527d78..514d9bb54 100644 --- a/test_shard/rendering_test/android/gradle/wrapper/gradle-wrapper.properties +++ b/test_shard/rendering_test/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip diff --git a/test_shard/rendering_test/android/settings.gradle b/test_shard/rendering_test/android/settings.gradle index 44e62bcf0..73ec5fcd8 100644 --- a/test_shard/rendering_test/android/settings.gradle +++ b/test_shard/rendering_test/android/settings.gradle @@ -1,11 +1,27 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + } + settings.ext.flutterSdkPath = flutterSdkPath() + + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.9.10" apply false +} + +include ':app' -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart b/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart index 4f40fbc93..a87c9528d 100644 --- a/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart +++ b/test_shard/rendering_test/integration_test/agora_video_view_render_test.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:agora_rtc_engine/src/impl/media_player_controller_impl.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; @@ -26,7 +27,23 @@ class TestMediaPlayerController extends MediaPlayerControllerImpl { @override bool get shouldHandlerRenderMode => true; } - +/// 辅助函数:执行截图操作并延迟dispose +Future takeScreenshotAndDelayDispose( + IntegrationTestWidgetsFlutterBinding binding, + WidgetTester tester, + String screenshotName, +) async { + await binding.takeScreenshot(screenshotName); + + // 构建截图文件路径 + final screenshotPath = 'screenshot/$screenshotName.png'; + debugPrint('Screenshot saved to: $screenshotPath'); + + // 延迟一段时间再执行dispose,确保截图操作完成 + await Future.delayed(const Duration(seconds: 2)); + + return screenshotPath; +} void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -38,6 +55,10 @@ void main() { testWidgets( 'render mode default', (WidgetTester tester) async { + debugPrint( + 'Test started: Android flutter texture local rendering with default render mode', + ); // 新增:测试开始日志 + final onFrameCompleter = Completer(); final RtcEngineEx rtcEngine = createAgoraRtcEngineEx(); @@ -56,6 +77,9 @@ void main() { ); }, onFirstFrame: () async { + debugPrint( + 'onFirstFrame triggered at ${DateTime.now()}', + ); // 新增:onFirstFrame 回调触发日志 if (!onFrameCompleter.isCompleted) { await rtcEngine.startPreview( sourceType: VideoSourceType.videoSourceCustom); @@ -73,10 +97,23 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.local.with_default_rendermode'); + debugPrint( + 'Taking screenshot: android.agora_video_view_render.texture.local.with_default_rendermode', + ); // 新增:截图前日志 + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.local.with_default_rendermode', + ); + debugPrint('Screenshot saved to: $screenshotPath'); + debugPrint('waitDisposed start'); await waitDisposed(tester, binding); + debugPrint('waitDisposed end'); + + debugPrint( + 'Test ended: Android flutter texture local rendering with default render mode', + ); // 新增:测试结束日志 }, ); @@ -119,8 +156,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.local.with_rendermodehidden'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.local.with_rendermodehidden', + ); await waitDisposed(tester, binding); }, @@ -165,8 +205,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.local.with_rendermodefit'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.local.with_rendermodefit', + ); await waitDisposed(tester, binding); }, @@ -211,8 +254,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.local.with_rendermodeadaptive'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.local.with_rendermodeadaptive', + ); await waitDisposed(tester, binding); }, @@ -257,8 +303,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.local.with_default_rendermode.with_videomirrormodedisabled'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.local.with_default_rendermode.with_videomirrormodedisabled', + ); await waitDisposed(tester, binding); }, @@ -303,8 +352,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.remote.with_default_rendermode'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.remote.with_default_rendermode', + ); await waitDisposed(tester, binding); }, @@ -348,8 +400,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.remote.with_rendermodehidden'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.remote.with_rendermodehidden', + ); await waitDisposed(tester, binding); }, @@ -393,8 +448,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.remote.with_rendermodefit'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.remote.with_rendermodefit', + ); await waitDisposed(tester, binding); }, @@ -438,8 +496,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.remote.with_rendermodeadaptive'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.remote.with_rendermodeadaptive', + ); await waitDisposed(tester, binding); }, @@ -484,8 +545,11 @@ void main() { // Trigger a frame. await tester.pumpAndSettle(); - await binding.takeScreenshot( - 'android.agora_video_view_render.texture.remote.with_default_rendermodede.with_videoMirrorModeEnabled'); + final screenshotPath = await takeScreenshotAndDelayDispose( + binding, + tester, + 'android.agora_video_view_render.texture.remote.with_default_rendermodede.with_videoMirrorModeEnabled', + ); await waitDisposed(tester, binding); }, diff --git a/test_shard/rendering_test/integration_test/common/screenshot_matcher_ext.dart b/test_shard/rendering_test/integration_test/common/screenshot_matcher_ext.dart index 48f0c42f4..81c5d6529 100644 --- a/test_shard/rendering_test/integration_test/common/screenshot_matcher_ext.dart +++ b/test_shard/rendering_test/integration_test/common/screenshot_matcher_ext.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:image/image.dart' as img; diff --git a/test_shard/rendering_test/test_driver/integration_test.dart b/test_shard/rendering_test/test_driver/integration_test.dart index ec614f715..e51b07b38 100644 --- a/test_shard/rendering_test/test_driver/integration_test.dart +++ b/test_shard/rendering_test/test_driver/integration_test.dart @@ -63,6 +63,7 @@ Future main() async { // // see if we can reduce the result later return result < 0.01; + return result < 0.05; }, ); }