From 425f5b3c58b9fece34890675e8235b025e7d338b Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Sat, 15 Nov 2025 15:13:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0iOS=E7=94=BB=E4=B8=AD?= =?UTF-8?q?=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- local_plugins/native_plugin/.gitignore | 33 +++++ local_plugins/native_plugin/.metadata | 33 +++++ local_plugins/native_plugin/CHANGELOG.md | 3 + local_plugins/native_plugin/LICENSE | 1 + local_plugins/native_plugin/README.md | 15 +++ .../native_plugin/analysis_options.yaml | 4 + .../native_plugin/android/.gitignore | 9 ++ .../native_plugin/android/build.gradle | 52 ++++++++ .../native_plugin/android/settings.gradle | 1 + .../android/src/main/AndroidManifest.xml | 3 + .../example/native_plugin/NativePlugin.java | 38 ++++++ .../native_plugin/NativePluginTest.java | 29 +++++ local_plugins/native_plugin/ios/.gitignore | 38 ++++++ .../native_plugin/ios/Assets/.gitkeep | 0 .../native_plugin/ios/Classes/FLNativeView.h | 15 +++ .../native_plugin/ios/Classes/FLNativeView.m | 83 +++++++++++++ .../native_plugin/ios/Classes/NativePlugin.h | 4 + .../native_plugin/ios/Classes/NativePlugin.m | 114 ++++++++++++++++++ .../native_plugin/ios/Classes/PlayerView.h | 15 +++ .../native_plugin/ios/Classes/PlayerView.m | 100 +++++++++++++++ .../ios/Resources/PrivacyInfo.xcprivacy | 14 +++ .../native_plugin/ios/native_plugin.podspec | 29 +++++ .../native_plugin/lib/native_plugin.dart | 19 +++ .../lib/native_plugin_method_channel.dart | 37 ++++++ .../lib/native_plugin_platform_interface.dart | 47 ++++++++ local_plugins/native_plugin/pubspec.yaml | 72 +++++++++++ pubspec.yaml | 3 + 27 files changed, 811 insertions(+) create mode 100644 local_plugins/native_plugin/.gitignore create mode 100644 local_plugins/native_plugin/.metadata create mode 100644 local_plugins/native_plugin/CHANGELOG.md create mode 100644 local_plugins/native_plugin/LICENSE create mode 100644 local_plugins/native_plugin/README.md create mode 100644 local_plugins/native_plugin/analysis_options.yaml create mode 100644 local_plugins/native_plugin/android/.gitignore create mode 100644 local_plugins/native_plugin/android/build.gradle create mode 100644 local_plugins/native_plugin/android/settings.gradle create mode 100644 local_plugins/native_plugin/android/src/main/AndroidManifest.xml create mode 100644 local_plugins/native_plugin/android/src/main/java/com/example/native_plugin/NativePlugin.java create mode 100644 local_plugins/native_plugin/android/src/test/java/com/example/native_plugin/NativePluginTest.java create mode 100644 local_plugins/native_plugin/ios/.gitignore create mode 100644 local_plugins/native_plugin/ios/Assets/.gitkeep create mode 100644 local_plugins/native_plugin/ios/Classes/FLNativeView.h create mode 100644 local_plugins/native_plugin/ios/Classes/FLNativeView.m create mode 100644 local_plugins/native_plugin/ios/Classes/NativePlugin.h create mode 100644 local_plugins/native_plugin/ios/Classes/NativePlugin.m create mode 100644 local_plugins/native_plugin/ios/Classes/PlayerView.h create mode 100644 local_plugins/native_plugin/ios/Classes/PlayerView.m create mode 100644 local_plugins/native_plugin/ios/Resources/PrivacyInfo.xcprivacy create mode 100644 local_plugins/native_plugin/ios/native_plugin.podspec create mode 100644 local_plugins/native_plugin/lib/native_plugin.dart create mode 100644 local_plugins/native_plugin/lib/native_plugin_method_channel.dart create mode 100644 local_plugins/native_plugin/lib/native_plugin_platform_interface.dart create mode 100644 local_plugins/native_plugin/pubspec.yaml diff --git a/local_plugins/native_plugin/.gitignore b/local_plugins/native_plugin/.gitignore new file mode 100644 index 000000000..e7d347d9d --- /dev/null +++ b/local_plugins/native_plugin/.gitignore @@ -0,0 +1,33 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +build/ diff --git a/local_plugins/native_plugin/.metadata b/local_plugins/native_plugin/.metadata new file mode 100644 index 000000000..a5700b901 --- /dev/null +++ b/local_plugins/native_plugin/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "35c388afb57ef061d06a39b537336c87e0e3d1b1" + channel: "stable" + +project_type: plugin + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: android + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + - platform: ios + create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/local_plugins/native_plugin/CHANGELOG.md b/local_plugins/native_plugin/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/local_plugins/native_plugin/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/local_plugins/native_plugin/LICENSE b/local_plugins/native_plugin/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/local_plugins/native_plugin/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/local_plugins/native_plugin/README.md b/local_plugins/native_plugin/README.md new file mode 100644 index 000000000..bc8261f01 --- /dev/null +++ b/local_plugins/native_plugin/README.md @@ -0,0 +1,15 @@ +# native_plugin + +A new Flutter plugin project. + +## Getting Started + +This project is a starting point for a Flutter +[plug-in package](https://flutter.dev/to/develop-plugins), +a specialized package that includes platform-specific implementation code for +Android and/or iOS. + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev), which offers tutorials, +samples, guidance on mobile development, and a full API reference. + diff --git a/local_plugins/native_plugin/analysis_options.yaml b/local_plugins/native_plugin/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/local_plugins/native_plugin/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/local_plugins/native_plugin/android/.gitignore b/local_plugins/native_plugin/android/.gitignore new file mode 100644 index 000000000..161bdcdaf --- /dev/null +++ b/local_plugins/native_plugin/android/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.cxx diff --git a/local_plugins/native_plugin/android/build.gradle b/local_plugins/native_plugin/android/build.gradle new file mode 100644 index 000000000..948c5ef6f --- /dev/null +++ b/local_plugins/native_plugin/android/build.gradle @@ -0,0 +1,52 @@ +group = "com.example.native_plugin" +version = "1.0" + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.7.0") + } +} + +rootProject.allprojects { + repositories { + google() + mavenCentral() + } +} + +apply plugin: "com.android.library" + +android { + namespace = "com.example.native_plugin" + + compileSdk = 35 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + defaultConfig { + minSdk = 21 + } + + dependencies { + testImplementation("junit:junit:4.13.2") + testImplementation("org.mockito:mockito-core:5.0.0") + } + + testOptions { + unitTests.all { + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } +} diff --git a/local_plugins/native_plugin/android/settings.gradle b/local_plugins/native_plugin/android/settings.gradle new file mode 100644 index 000000000..9f756a98c --- /dev/null +++ b/local_plugins/native_plugin/android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'native_plugin' diff --git a/local_plugins/native_plugin/android/src/main/AndroidManifest.xml b/local_plugins/native_plugin/android/src/main/AndroidManifest.xml new file mode 100644 index 000000000..5153b5dc6 --- /dev/null +++ b/local_plugins/native_plugin/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/local_plugins/native_plugin/android/src/main/java/com/example/native_plugin/NativePlugin.java b/local_plugins/native_plugin/android/src/main/java/com/example/native_plugin/NativePlugin.java new file mode 100644 index 000000000..265796e9d --- /dev/null +++ b/local_plugins/native_plugin/android/src/main/java/com/example/native_plugin/NativePlugin.java @@ -0,0 +1,38 @@ +package com.example.native_plugin; + +import androidx.annotation.NonNull; + +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import io.flutter.plugin.common.MethodChannel.MethodCallHandler; +import io.flutter.plugin.common.MethodChannel.Result; + +/** NativePlugin */ +public class NativePlugin implements FlutterPlugin, MethodCallHandler { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity + private MethodChannel channel; + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "native_plugin"); + channel.setMethodCallHandler(this); + } + + @Override + public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { + if (call.method.equals("getPlatformVersion")) { + result.success("Android " + android.os.Build.VERSION.RELEASE); + } else { + result.notImplemented(); + } + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + channel.setMethodCallHandler(null); + } +} diff --git a/local_plugins/native_plugin/android/src/test/java/com/example/native_plugin/NativePluginTest.java b/local_plugins/native_plugin/android/src/test/java/com/example/native_plugin/NativePluginTest.java new file mode 100644 index 000000000..8034030e9 --- /dev/null +++ b/local_plugins/native_plugin/android/src/test/java/com/example/native_plugin/NativePluginTest.java @@ -0,0 +1,29 @@ +package com.example.native_plugin; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; +import org.junit.Test; + +/** + * This demonstrates a simple unit test of the Java portion of this plugin's implementation. + * + * Once you have built the plugin's example app, you can run these tests from the command + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or + * you can run them directly from IDEs that support JUnit such as Android Studio. + */ + +public class NativePluginTest { + @Test + public void onMethodCall_getPlatformVersion_returnsExpectedValue() { + NativePlugin plugin = new NativePlugin(); + + final MethodCall call = new MethodCall("getPlatformVersion", null); + MethodChannel.Result mockResult = mock(MethodChannel.Result.class); + plugin.onMethodCall(call, mockResult); + + verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE); + } +} diff --git a/local_plugins/native_plugin/ios/.gitignore b/local_plugins/native_plugin/ios/.gitignore new file mode 100644 index 000000000..034771fc9 --- /dev/null +++ b/local_plugins/native_plugin/ios/.gitignore @@ -0,0 +1,38 @@ +.idea/ +.vagrant/ +.sconsign.dblite +.svn/ + +.DS_Store +*.swp +profile + +DerivedData/ +build/ +GeneratedPluginRegistrant.h +GeneratedPluginRegistrant.m + +.generated/ + +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 + +!default.pbxuser +!default.mode1v3 +!default.mode2v3 +!default.perspectivev3 + +xcuserdata + +*.moved-aside + +*.pyc +*sync/ +Icon? +.tags* + +/Flutter/Generated.xcconfig +/Flutter/ephemeral/ +/Flutter/flutter_export_environment.sh diff --git a/local_plugins/native_plugin/ios/Assets/.gitkeep b/local_plugins/native_plugin/ios/Assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/local_plugins/native_plugin/ios/Classes/FLNativeView.h b/local_plugins/native_plugin/ios/Classes/FLNativeView.h new file mode 100644 index 000000000..4c49e00f4 --- /dev/null +++ b/local_plugins/native_plugin/ios/Classes/FLNativeView.h @@ -0,0 +1,15 @@ +#import + +@interface FLNativeViewFactory : NSObject +- (instancetype)initWithMessenger:(NSObject *)messenger; +@end + +@interface FLNativeView : NSObject + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject *)messenger; + +- (UIView *)view; +@end diff --git a/local_plugins/native_plugin/ios/Classes/FLNativeView.m b/local_plugins/native_plugin/ios/Classes/FLNativeView.m new file mode 100644 index 000000000..848785a6b --- /dev/null +++ b/local_plugins/native_plugin/ios/Classes/FLNativeView.m @@ -0,0 +1,83 @@ +#import "FLNativeView.h" +#import "PlayerView.h" + +@implementation FLNativeViewFactory { + NSObject *_messenger; +} + +- (instancetype)initWithMessenger: + (NSObject *)messenger { + self = [super init]; + if (self) { + _messenger = messenger; + } + return self; +} + +- (NSObject *)createWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args { + return [[FLNativeView alloc] initWithFrame:frame + viewIdentifier:viewId + arguments:args + binaryMessenger:_messenger]; +} + +/// Implementing this method is only necessary when the `arguments` in +/// `createWithFrame` is not `nil`. +- (NSObject *)createArgsCodec { + return [FlutterStandardMessageCodec sharedInstance]; +} + +@end + +@implementation FLNativeView { + PlayerView *_view; + FlutterMethodChannel *_methodChannel; +} + +- (instancetype)initWithFrame:(CGRect)frame + viewIdentifier:(int64_t)viewId + arguments:(id _Nullable)args + binaryMessenger:(NSObject *)messenger { + if (self = [super init]) { + _view = [[PlayerView alloc] init]; + _methodChannel = [FlutterMethodChannel + methodChannelWithName: + [NSString + stringWithFormat:@"native_plugin/native_view_%lld", viewId] + binaryMessenger:messenger]; + __weak typeof(self) weakSelf = self; + [_methodChannel setMethodCallHandler:^(FlutterMethodCall *_Nonnull call, + FlutterResult _Nonnull result) { + typeof(self) strongSelf = weakSelf; + if (strongSelf != nil) { + [strongSelf onMethodCall:call result:result]; + } + }]; + } + return self; +} + +- (UIView *)view { + return _view; +} + +- (void)onMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { + if ([@"getInternalView" isEqualToString:call.method]) { + result(@((uint64_t)(_view))); + } else if ([@"updateContent" isEqualToString:call.method]) { + NSDictionary *data = call.arguments; + NSString *lang = data[@"lang"]; + NSString *cn = data[@"cn"]; + NSString *en = data[@"en"]; + dispatch_async(dispatch_get_main_queue(), ^{ + [_view updateWithLang:lang cn:cn en:en]; + }); + result(nil); + } else { + result(@(0)); + } +} + +@end diff --git a/local_plugins/native_plugin/ios/Classes/NativePlugin.h b/local_plugins/native_plugin/ios/Classes/NativePlugin.h new file mode 100644 index 000000000..2d0b3293e --- /dev/null +++ b/local_plugins/native_plugin/ios/Classes/NativePlugin.h @@ -0,0 +1,4 @@ +#import + +@interface NativePlugin : NSObject +@end diff --git a/local_plugins/native_plugin/ios/Classes/NativePlugin.m b/local_plugins/native_plugin/ios/Classes/NativePlugin.m new file mode 100644 index 000000000..bee63b337 --- /dev/null +++ b/local_plugins/native_plugin/ios/Classes/NativePlugin.m @@ -0,0 +1,114 @@ +#import "NativePlugin.h" +#import "FLNativeView.h" +#import "PlayerView.h" + +#import + +@interface NativePlugin () + +@property(nonatomic, strong) NSMutableArray *_Nonnull pipContentViewArray; + +@end + +@implementation NativePlugin + ++ (void)updateAudioSession { + AVAudioSession *audioSession = [AVAudioSession sharedInstance]; + NSError *categoryError = nil; + if (@available(iOS 14.5, *)) { + [audioSession + setCategory:AVAudioSessionCategoryPlayback + mode:AVAudioSessionModeMoviePlayback + options: + AVAudioSessionCategoryOptionOverrideMutedMicrophoneInterruption + error:&categoryError]; + } else { + // Fallback on earlier versions + } + if (categoryError) { + NSLog(@"Set audio session category error: %@", + categoryError.localizedDescription); + } + NSError *activeError = nil; + [audioSession setActive:YES error:&activeError]; + if (activeError) { + NSLog(@"Set audio session active error: %@", + activeError.localizedDescription); + } +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + // must call this, otherwise pip will not possible + // https://github.com/jazzychad/PiPBugDemo + [NativePlugin updateAudioSession]; + + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:@"native_plugin" + binaryMessenger:[registrar messenger]]; + NativePlugin *instance = [[NativePlugin alloc] init]; + + FLNativeViewFactory *factory = + [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger]; + [registrar registerViewFactory:factory withId:@"native_view"]; + + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _pipContentViewArray = [NSMutableArray array]; + } + return self; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call + result:(FlutterResult)result { + if ([@"getPlatformVersion" isEqualToString:call.method]) { + result([@"iOS " + stringByAppendingString:[[UIDevice currentDevice] systemVersion]]); + } else if ([@"createPipContentView" isEqualToString:call.method]) { + PlayerView *playerView = [[PlayerView alloc] init]; + + // should control the play state by event of pip, optimize it later + [playerView play]; + + [self.pipContentViewArray addObject:playerView]; + + result(@((uint64_t)playerView)); + } else if ([@"disposePipContentView" isEqualToString:call.method]) { + uint64_t viewId = [call.arguments unsignedLongLongValue]; + for (PlayerView *playerView in self.pipContentViewArray) { + if ((uint64_t)playerView == viewId) { + [playerView pause]; + [self.pipContentViewArray removeObject:playerView]; + break; + } + } + } else if ([@"updatePipContentView" isEqualToString:call.method]) { + NSDictionary *data = call.arguments; + NSString *lang = data[@"lang"]; + NSString *cn = data[@"cn"]; + NSString *en = data[@"en"]; + NSNumber *viewIdNum = data[@"viewId"]; + dispatch_async(dispatch_get_main_queue(), ^{ + if (viewIdNum != nil) { + uint64_t targetId = viewIdNum.unsignedLongLongValue; + for (PlayerView *playerView in self.pipContentViewArray) { + if ((uint64_t)playerView == targetId) { + [playerView updateWithLang:lang cn:cn en:en]; + break; + } + } + } else { + PlayerView *playerView = [self.pipContentViewArray lastObject]; + [playerView updateWithLang:lang cn:cn en:en]; + } + }); + result(nil); + } else { + result(FlutterMethodNotImplemented); + } +} + +@end diff --git a/local_plugins/native_plugin/ios/Classes/PlayerView.h b/local_plugins/native_plugin/ios/Classes/PlayerView.h new file mode 100644 index 000000000..50044ae41 --- /dev/null +++ b/local_plugins/native_plugin/ios/Classes/PlayerView.h @@ -0,0 +1,15 @@ +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface PlayerView : UIView + +- (void)play; + +- (void)pause; + +- (void)updateWithLang:(NSString *)lang cn:(NSString *)cn en:(NSString *)en; + +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/local_plugins/native_plugin/ios/Classes/PlayerView.m b/local_plugins/native_plugin/ios/Classes/PlayerView.m new file mode 100644 index 000000000..e456fb91d --- /dev/null +++ b/local_plugins/native_plugin/ios/Classes/PlayerView.m @@ -0,0 +1,100 @@ +#import "PlayerView.h" + +@interface PlayerView () + +@property(nonatomic, strong) UILabel *titleLabel; +@property(nonatomic, strong) UILabel *langLabel; +@property(nonatomic, strong) UILabel *cnLabel; +@property(nonatomic, strong) UILabel *enLabel; +@property(nonatomic, strong) UIStackView *stackView; + +@end + +@implementation PlayerView + +- (instancetype)init { + if (self = [super init]) { + self.backgroundColor = [UIColor whiteColor]; + + UILabel *title = [[UILabel alloc] init]; + title.text = @"实时翻译"; + title.font = [UIFont boldSystemFontOfSize:22]; + title.textColor = [UIColor blackColor]; + title.textAlignment = NSTextAlignmentCenter; + title.numberOfLines = 1; + self.titleLabel = title; + + UILabel *lang = [[UILabel alloc] init]; + lang.text = @"中文(简体) -> 英语"; + lang.font = [UIFont systemFontOfSize:14]; + lang.textColor = [UIColor darkGrayColor]; + lang.textAlignment = NSTextAlignmentLeft; + lang.numberOfLines = 1; + self.langLabel = lang; + + UILabel *cn = [[UILabel alloc] init]; + cn.font = [UIFont systemFontOfSize:18]; + cn.textColor = [UIColor blackColor]; + cn.textAlignment = NSTextAlignmentLeft; + cn.numberOfLines = 0; + self.cnLabel = cn; + + UILabel *en = [[UILabel alloc] init]; + en.font = [UIFont systemFontOfSize:18]; + en.textColor = [UIColor colorWithRed:0.24 green:0.54 blue:0.90 alpha:1.0]; + en.textAlignment = NSTextAlignmentLeft; + en.numberOfLines = 0; + self.enLabel = en; + + NSMutableParagraphStyle *para = [[NSMutableParagraphStyle alloc] init]; + para.lineSpacing = 4; + + self.cnLabel.attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:@{NSParagraphStyleAttributeName: para}]; + + self.enLabel.attributedText = [[NSAttributedString alloc] initWithString:@"" attributes:@{NSParagraphStyleAttributeName: para}]; + + UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[self.titleLabel, self.langLabel, self.cnLabel, self.enLabel]]; + stack.axis = UILayoutConstraintAxisVertical; + stack.spacing = 12; + stack.alignment = UIStackViewAlignmentFill; + stack.distribution = UIStackViewDistributionFill; + stack.translatesAutoresizingMaskIntoConstraints = NO; + self.stackView = stack; + + self.layoutMargins = UIEdgeInsetsMake(16, 16, 16, 16); + [self addSubview:self.stackView]; + + UILayoutGuide *guide = self.layoutMarginsGuide; + [NSLayoutConstraint activateConstraints:@[ + [self.stackView.topAnchor constraintEqualToAnchor:guide.topAnchor], + [self.stackView.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor], + [self.stackView.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor], + [self.stackView.bottomAnchor constraintLessThanOrEqualToAnchor:guide.bottomAnchor] + ]]; + } + return self; +} + +- (void)play { + +} + +- (void)pause { + +} + +- (void)updateWithLang:(NSString *)lang cn:(NSString *)cn en:(NSString *)en { + if (lang != nil) { + self.langLabel.text = lang; + } + NSMutableParagraphStyle *para = [[NSMutableParagraphStyle alloc] init]; + para.lineSpacing = 4; + if (cn != nil) { + self.cnLabel.attributedText = [[NSAttributedString alloc] initWithString:cn attributes:@{NSParagraphStyleAttributeName: para}]; + } + if (en != nil) { + self.enLabel.attributedText = [[NSAttributedString alloc] initWithString:en attributes:@{NSParagraphStyleAttributeName: para}]; + } +} + +@end diff --git a/local_plugins/native_plugin/ios/Resources/PrivacyInfo.xcprivacy b/local_plugins/native_plugin/ios/Resources/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..a34b7e2e6 --- /dev/null +++ b/local_plugins/native_plugin/ios/Resources/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ + + + + + NSPrivacyTrackingDomains + + NSPrivacyAccessedAPITypes + + NSPrivacyCollectedDataTypes + + NSPrivacyTracking + + + diff --git a/local_plugins/native_plugin/ios/native_plugin.podspec b/local_plugins/native_plugin/ios/native_plugin.podspec new file mode 100644 index 000000000..c84e9ffba --- /dev/null +++ b/local_plugins/native_plugin/ios/native_plugin.podspec @@ -0,0 +1,29 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint native_plugin.podspec` to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'native_plugin' + s.version = '0.0.1' + s.summary = 'A new Flutter plugin project.' + s.description = <<-DESC +A new Flutter plugin project. + DESC + s.homepage = 'http://example.com' + s.license = { :file => '../LICENSE' } + s.author = { 'Your Company' => 'email@example.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.public_header_files = 'Classes/**/*.h' + s.dependency 'Flutter' + s.platform = :ios, '12.0' + + # Flutter.framework does not contain a i386 slice. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } + + # If your plugin requires a privacy manifest, for example if it uses any + # required reason APIs, update the PrivacyInfo.xcprivacy file to describe your + # plugin's privacy impact, and then uncomment this line. For more information, + # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files + # s.resource_bundles = {'native_plugin_privacy' => ['Resources/PrivacyInfo.xcprivacy']} +end diff --git a/local_plugins/native_plugin/lib/native_plugin.dart b/local_plugins/native_plugin/lib/native_plugin.dart new file mode 100644 index 000000000..c54721f1f --- /dev/null +++ b/local_plugins/native_plugin/lib/native_plugin.dart @@ -0,0 +1,19 @@ +import 'native_plugin_platform_interface.dart'; + +class NativePlugin { + Future getPlatformVersion() { + return NativePluginPlatform.instance.getPlatformVersion(); + } + + Future createPipContentView() { + return NativePluginPlatform.instance.createPipContentView(); + } + + Future updatePipContentView(Map data) { + return NativePluginPlatform.instance.updatePipContentView(data); + } + + Future disposePipContentView(int viewId) { + return NativePluginPlatform.instance.disposePipContentView(viewId); + } +} diff --git a/local_plugins/native_plugin/lib/native_plugin_method_channel.dart b/local_plugins/native_plugin/lib/native_plugin_method_channel.dart new file mode 100644 index 000000000..ec8e18ac5 --- /dev/null +++ b/local_plugins/native_plugin/lib/native_plugin_method_channel.dart @@ -0,0 +1,37 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +import 'native_plugin_platform_interface.dart'; + +/// An implementation of [NativePluginPlatform] that uses method channels. +class MethodChannelNativePlugin extends NativePluginPlatform { + /// The method channel used to interact with the native platform. + @visibleForTesting + final methodChannel = const MethodChannel('native_plugin'); + + @override + Future getPlatformVersion() async { + final version = await methodChannel.invokeMethod( + 'getPlatformVersion', + ); + return version; + } + + @override + Future createPipContentView() async { + final viewId = await methodChannel.invokeMethod( + 'createPipContentView', + ); + return viewId ?? 0; + } + + @override + Future updatePipContentView(Map data) async { + await methodChannel.invokeMethod('updatePipContentView', data); + } + + @override + Future disposePipContentView(int viewId) async { + await methodChannel.invokeMethod('disposePipContentView', viewId); + } +} diff --git a/local_plugins/native_plugin/lib/native_plugin_platform_interface.dart b/local_plugins/native_plugin/lib/native_plugin_platform_interface.dart new file mode 100644 index 000000000..c26495c2f --- /dev/null +++ b/local_plugins/native_plugin/lib/native_plugin_platform_interface.dart @@ -0,0 +1,47 @@ +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'native_plugin_method_channel.dart'; + +abstract class NativePluginPlatform extends PlatformInterface { + /// Constructs a NativePluginPlatform. + NativePluginPlatform() : super(token: _token); + + static final Object _token = Object(); + + static NativePluginPlatform _instance = MethodChannelNativePlugin(); + + /// The default instance of [NativePluginPlatform] to use. + /// + /// Defaults to [MethodChannelNativePlugin]. + static NativePluginPlatform get instance => _instance; + + /// Platform-specific implementations should set this with their own + /// platform-specific class that extends [NativePluginPlatform] when + /// they register themselves. + static set instance(NativePluginPlatform instance) { + PlatformInterface.verifyToken(instance, _token); + _instance = instance; + } + + Future getPlatformVersion() { + throw UnimplementedError('platformVersion() has not been implemented.'); + } + + Future createPipContentView() { + throw UnimplementedError( + 'createPipContentView() has not been implemented.', + ); + } + + Future updatePipContentView(Map data) { + throw UnimplementedError( + 'updatePipContentView() has not been implemented.', + ); + } + + Future disposePipContentView(int viewId) { + throw UnimplementedError( + 'disposePipContentView() has not been implemented.', + ); + } +} diff --git a/local_plugins/native_plugin/pubspec.yaml b/local_plugins/native_plugin/pubspec.yaml new file mode 100644 index 000000000..a284bfce8 --- /dev/null +++ b/local_plugins/native_plugin/pubspec.yaml @@ -0,0 +1,72 @@ +name: native_plugin +description: "A new Flutter plugin project." +version: 0.0.1 +homepage: + +environment: + sdk: ^3.7.0 + flutter: '>=3.3.0' + +dependencies: + flutter: + sdk: flutter + plugin_platform_interface: ^2.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + 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: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.) + # which should be registered in the plugin registry. This is required for + # using method channels. + # The Android 'package' specifies package in which the registered class is. + # This is required for using method channels on Android. + # The 'ffiPlugin' specifies that native code should be built and bundled. + # This is required for using `dart:ffi`. + # All these are used by the tooling to maintain consistency when + # adding or updating assets for this project. + plugin: + platforms: + android: + package: com.example.native_plugin + pluginClass: NativePlugin + ios: + pluginClass: NativePlugin + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your plugin package, 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 in packages, see + # https://flutter.dev/to/font-from-package diff --git a/pubspec.yaml b/pubspec.yaml index 47a634b42..d58b22f96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -76,6 +76,7 @@ dependencies: dio: ^5.8.0+1 sqflite: ^2.4.2 path: ^1.9.1 + pip: ^0.0.3 connectivity_plus: ^6.1.5 # 网络连接检测依赖 speech: path: local_plugins/speech @@ -111,6 +112,8 @@ dependencies: path: local_plugins/user_portrait qq_music: path: local_plugins/qq_music + native_plugin: + path: local_plugins/native_plugin test: path: local_plugins/test in_app_purchase: ^3.2.3 # 应用内购买插件