[iOS] Add platform view to integration_test example (#164144)
Adds a platform view that simply renders a blue square to the integration_test example, and updates the screenshot capture code to capture all windows, ensuring that any native picture-in-picture, split views, etc. are catpured. Adds a screenshot test that captures the platform view alongside the existing test. Improved API for screenshot capture will land in a followup patch. Fixes: https://github.com/flutter/flutter/issues/164129 Issue: https://github.com/flutter/flutter/issues/51890 ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [X] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I added new tests to check the change I am making, or this PR is [test-exempt]. - [X] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [X] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
9af844c5c9
commit
45702a26ca
3
packages/integration_test/example/.gitignore
vendored
3
packages/integration_test/example/.gitignore
vendored
@ -46,4 +46,5 @@ app.*.map.json
|
|||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
# Golden images.
|
# Golden images.
|
||||||
integration_test/integration_test_matches_golden_file.png
|
integration_test/integration_test_widget_matches_golden_file.png
|
||||||
|
integration_test/integration_test_screen_matches_golden_file.png
|
||||||
|
@ -30,11 +30,22 @@ void main() {
|
|||||||
|
|
||||||
// TODO(matanlurey): Is this necessary?
|
// TODO(matanlurey): Is this necessary?
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
// TODO(cbracken): not only is it necessary, but so is this.
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
// Take a screenshot.
|
// Take a widget screenshot.
|
||||||
await expectLater(
|
await expectLater(
|
||||||
find.byType(MaterialApp),
|
find.byType(MaterialApp),
|
||||||
matchesGoldenFile('integration_test_matches_golden_file.png'),
|
matchesGoldenFile('integration_test_widget_matches_golden_file.png'),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Take a full-screen screenshot.
|
||||||
|
final List<int> screenshot = await IntegrationTestWidgetsFlutterBinding.instance.takeScreenshot(
|
||||||
|
'integration_test_screen_matches_golden_file',
|
||||||
|
);
|
||||||
|
await expectLater(
|
||||||
|
screenshot,
|
||||||
|
matchesGoldenFile('integration_test_screen_matches_golden_file.png'),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
3BA56D9B2D36D966004F0F1C /* SimplePlatformView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3BA56D9A2D36D960004F0F1C /* SimplePlatformView.m */; };
|
||||||
4DB404AC7CF2C89658A01173 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81BF64028CE7AE2E6196250D /* libPods-RunnerTests.a */; };
|
4DB404AC7CF2C89658A01173 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81BF64028CE7AE2E6196250D /* libPods-RunnerTests.a */; };
|
||||||
769541CB23A0351900E5C350 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 769541CA23A0351900E5C350 /* RunnerTests.m */; };
|
769541CB23A0351900E5C350 /* RunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 769541CA23A0351900E5C350 /* RunnerTests.m */; };
|
||||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
|
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
|
||||||
@ -48,6 +49,8 @@
|
|||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
|
3BA56D992D36D939004F0F1C /* SimplePlatformView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimplePlatformView.h; sourceTree = "<group>"; };
|
||||||
|
3BA56D9A2D36D960004F0F1C /* SimplePlatformView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SimplePlatformView.m; sourceTree = "<group>"; };
|
||||||
625A5A90428602E25C0DE2F6 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
625A5A90428602E25C0DE2F6 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
750225973AAB5D7832AFA60C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
750225973AAB5D7832AFA60C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
769541BF23A0337200E5C350 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
769541BF23A0337200E5C350 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
@ -145,6 +148,8 @@
|
|||||||
97C146F01CF9000F007C117D /* Runner */ = {
|
97C146F01CF9000F007C117D /* Runner */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3BA56D992D36D939004F0F1C /* SimplePlatformView.h */,
|
||||||
|
3BA56D9A2D36D960004F0F1C /* SimplePlatformView.m */,
|
||||||
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
|
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
|
||||||
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
|
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
|
||||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||||
@ -373,6 +378,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
|
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
|
||||||
|
3BA56D9B2D36D966004F0F1C /* SimplePlatformView.m in Sources */,
|
||||||
97C146F31CF9000F007C117D /* main.m in Sources */,
|
97C146F31CF9000F007C117D /* main.m in Sources */,
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||||
);
|
);
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
|
enableGPUValidationMode = "1"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<BuildableProductRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0">
|
||||||
|
@ -4,12 +4,19 @@
|
|||||||
|
|
||||||
#include "AppDelegate.h"
|
#include "AppDelegate.h"
|
||||||
#include "GeneratedPluginRegistrant.h"
|
#include "GeneratedPluginRegistrant.h"
|
||||||
|
#include "SimplePlatformView.h"
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application
|
- (BOOL)application:(UIApplication *)application
|
||||||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||||
[GeneratedPluginRegistrant registerWithRegistry:self];
|
[GeneratedPluginRegistrant registerWithRegistry:self];
|
||||||
|
|
||||||
|
// Register platform view factory.
|
||||||
|
NSObject<FlutterPluginRegistrar>* registrar = [self registrarForPlugin:@"spv-plugin"];
|
||||||
|
SimplePlatformViewFactory* factory = [[SimplePlatformViewFactory alloc] initWithMessenger:registrar.messenger];
|
||||||
|
[registrar registerViewFactory:factory withId:@"simple-platform-view"];
|
||||||
|
|
||||||
// Override point for customization after application launch.
|
// Override point for customization after application launch.
|
||||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#import <Flutter/Flutter.h>
|
||||||
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@interface SimplePlatformViewFactory : NSObject<FlutterPlatformViewFactory>
|
||||||
|
|
||||||
|
- (instancetype _Nullable)initWithMessenger:(NSObject<FlutterBinaryMessenger>* _Nonnull)messenger;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface SimplePlatformView : NSObject<FlutterPlatformView>
|
||||||
|
|
||||||
|
- (instancetype _Nullable)initWithFrame:(CGRect)frame
|
||||||
|
viewIdentifier:(int64_t)viewId
|
||||||
|
arguments:(id _Nullable)args
|
||||||
|
binaryMessenger:(NSObject<FlutterBinaryMessenger>* _Nonnull)messenger;
|
||||||
|
|
||||||
|
- (UIView* _Nonnull)view;
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "SimplePlatformView.h"
|
||||||
|
|
||||||
|
@implementation SimplePlatformViewFactory {
|
||||||
|
NSObject<FlutterBinaryMessenger>* _messenger;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype _Nullable)initWithMessenger:(NSObject<FlutterBinaryMessenger>* _Nonnull)messenger {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_messenger = messenger;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nonnull NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
|
||||||
|
viewIdentifier:(int64_t)viewId
|
||||||
|
arguments:(id _Nullable)args {
|
||||||
|
return [[SimplePlatformView alloc] initWithFrame:frame
|
||||||
|
viewIdentifier:viewId
|
||||||
|
arguments:args
|
||||||
|
binaryMessenger:_messenger];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSObject<FlutterMessageCodec>*)createArgsCodec {
|
||||||
|
return [FlutterStandardMessageCodec sharedInstance];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SimplePlatformView {
|
||||||
|
UIView* _view;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype _Nullable)initWithFrame:(CGRect)frame
|
||||||
|
viewIdentifier:(int64_t)viewId
|
||||||
|
arguments:(id _Nullable)args
|
||||||
|
binaryMessenger:(NSObject<FlutterBinaryMessenger>* _Nonnull)messenger {
|
||||||
|
if (self = [super init]) {
|
||||||
|
_view = [[UIView alloc] initWithFrame:frame];
|
||||||
|
_view.backgroundColor = UIColor.blueColor;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIView* _Nonnull)view {
|
||||||
|
return _view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -2,8 +2,9 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'simple_platform_view.dart';
|
||||||
|
|
||||||
// ignore_for_file: public_member_api_docs
|
// ignore_for_file: public_member_api_docs
|
||||||
|
|
||||||
@ -22,7 +23,12 @@ class _MyAppState extends State<MyApp> {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
appBar: AppBar(title: const Text('Plugin example app')),
|
appBar: AppBar(title: const Text('Plugin example app')),
|
||||||
body: Center(child: Text('Platform: ${Platform.operatingSystem}\n')),
|
body: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text('Platform: ${Platform.operatingSystem}\n'),
|
||||||
|
const Expanded(child: SimplePlatformView()),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// A platform view that displays a blue fill.
|
||||||
|
class SimplePlatformView extends StatelessWidget {
|
||||||
|
/// Creates a platform view that displays a blue fill.
|
||||||
|
const SimplePlatformView({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
switch (defaultTargetPlatform) {
|
||||||
|
case TargetPlatform.android:
|
||||||
|
// TODO(cbracken): Implement. https://github.com/flutter/flutter/issues/164130
|
||||||
|
return Container();
|
||||||
|
case TargetPlatform.iOS:
|
||||||
|
return const UiKitView(viewType: 'simple-platform-view');
|
||||||
|
case TargetPlatform.fuchsia:
|
||||||
|
case TargetPlatform.linux:
|
||||||
|
case TargetPlatform.macOS:
|
||||||
|
case TargetPlatform.windows:
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -50,6 +50,11 @@ static NSString *const kMethodRevertImage = @"revertFlutterImage";
|
|||||||
[registrar addMethodCallDelegate:[self instance] channel:channel];
|
[registrar addMethodCallDelegate:[self instance] channel:channel];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle method calls from Dart code:
|
||||||
|
/// - allTestsFinished: Populate NSString* testResults property with a string summary of test run.
|
||||||
|
/// - captureScreenshot: Capture a screenshot. Populate capturedScreenshotsByName["name"] with image.
|
||||||
|
/// - convertSurfaceToImage: Android-only. Not implemented on iOS.
|
||||||
|
/// - revertFlutterImage: Android-only. Not implemented on iOS.
|
||||||
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
|
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
|
||||||
if ([call.method isEqualToString:kMethodTestFinished]) {
|
if ([call.method isEqualToString:kMethodTestFinished]) {
|
||||||
self.testResults = call.arguments[@"results"];
|
self.testResults = call.arguments[@"results"];
|
||||||
@ -73,18 +78,23 @@ static NSString *const kMethodRevertImage = @"revertFlutterImage";
|
|||||||
}
|
}
|
||||||
|
|
||||||
- (UIImage *)capturePngScreenshot {
|
- (UIImage *)capturePngScreenshot {
|
||||||
UIWindow *window = [UIApplication.sharedApplication.windows
|
// Get all windows in the app
|
||||||
filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"keyWindow = YES"]].firstObject;
|
NSArray<UIWindow *> *windows = [UIApplication sharedApplication].windows;
|
||||||
CGRect screenshotBounds = window.bounds;
|
|
||||||
UIImage *image;
|
|
||||||
|
|
||||||
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithBounds:screenshotBounds];
|
// Find the overall bounding rect for all windows
|
||||||
|
CGRect screenBounds = [UIScreen mainScreen].bounds;
|
||||||
|
|
||||||
image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
|
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithBounds:screenBounds];
|
||||||
[window drawViewHierarchyInRect:screenshotBounds afterScreenUpdates:YES];
|
UIImage *screenshot =
|
||||||
}];
|
[renderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
|
||||||
|
for (UIWindow *window in windows) {
|
||||||
|
if (!window.hidden) { // Render only visible windows
|
||||||
|
[window drawViewHierarchyInRect:window.frame afterScreenUpdates:YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
return image;
|
return screenshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user