From 9788b63e045502aba8a62e8d625fd240a28b9035 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 13:32:14 -0300 Subject: [PATCH 01/69] Web VTT Supported Added --- .../video_player/example/assets/sample.vtt | 40 +++++ .../video_player/example/pubspec.yaml | 7 +- .../lib/src/closed_caption_file.dart | 4 + .../video_player/lib/src/web_vtt.dart | 168 ++++++++++++++++++ .../video_player/video_player/pubspec.yaml | 3 +- .../video_player/test/web_vtt.test.dart | 119 +++++++++++++ 6 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 packages/video_player/video_player/example/assets/sample.vtt create mode 100644 packages/video_player/video_player/lib/src/web_vtt.dart create mode 100644 packages/video_player/video_player/test/web_vtt.test.dart diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt new file mode 100644 index 000000000000..2e5c750a7cee --- /dev/null +++ b/packages/video_player/video_player/example/assets/sample.vtt @@ -0,0 +1,40 @@ +WEBVTT + +00:00.500 --> 00:01.000 +We are in New York City + +00:01.200 --> 00:02.000 +We’re actually at the Lucern Hotel, just down the street + +00:16.000 --> 00:18.000 +from the American Museum of Natural History + +00:18.000 --> 00:20.000 +And with me is Neil deGrasse Tyson + +00:20.000 --> 00:22.000 +Astrophysicist, Director of the Hayden Planetarium + +00:22.000 --> 00:24.000 +at the AMNH. + +00:24.000 --> 00:26.000 +Thank you for walking down here. + +00:27.000 --> 00:30.000 +And I want to do a follow-up on the last conversation we did. + +00:30.000 --> 00:31.500 align:right size:50% +When we e-mailed— + +00:30.500 --> 00:32.500 align:left size:50% +Didn’t we talk about enough in that conversation? + +00:32.000 --> 00:35.500 align:right size:50% +No! No no no no; 'cos 'cos obviously 'cos + +00:32.500 --> 00:33.500 align:left size:50% +Laughs + +00:35.500 --> 00:38.000 +You know I’m so excited my glasses are falling off here. \ No newline at end of file diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 29f1c91e4af3..283a484be711 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -19,6 +19,7 @@ dev_dependencies: flutter: uses-material-design: true assets: - - assets/flutter-mark-square-64.png - - assets/Butterfly-209.mp4 - - assets/bumble_bee_captions.srt + - assets/flutter-mark-square-64.png + - assets/Butterfly-209.mp4 + - assets/bumble_bee_captions.srt + - assets/sample.vtt diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 2d9242a675d5..8cbe5dee896f 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -5,6 +5,9 @@ import 'sub_rip.dart'; export 'sub_rip.dart' show SubRipCaptionFile; +import 'web_vtt.dart'; +export 'web_vtt.dart' show WebVTTCaptionFile; + /// A structured representation of a parsed closed caption file. /// /// A closed caption file includes a list of captions, each with a start and end @@ -15,6 +18,7 @@ export 'sub_rip.dart' show SubRipCaptionFile; /// /// See: /// * [SubRipCaptionFile]. +/// * [WebVTTCaptionFile]. abstract class ClosedCaptionFile { /// The full list of captions from a given file. /// diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart new file mode 100644 index 000000000000..d9978c259148 --- /dev/null +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -0,0 +1,168 @@ +// Copyright 2020 The Chromium 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 'dart:convert'; + +import 'closed_caption_file.dart'; +import 'package:html/parser.dart'; + +/// Represents a [ClosedCaptionFile], parsed from the WebVTT file format. +/// See: https://en.wikipedia.org/wiki/WebVTT +class WebVTTCaptionFile extends ClosedCaptionFile { + /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in + /// the WebVTT file format. + /// * See: https://en.wikipedia.org/wiki/WebVTT + WebVTTCaptionFile(this.fileContents) + : _captions = _parseCaptionsFromWebVTTString(fileContents); + + /// The entire body of the SubRip file. + final String fileContents; + + @override + List get captions => _captions; + + final List _captions; +} + +List _parseCaptionsFromWebVTTString(String file) { + final List captions = []; + int number = 1; + for (List captionLines in _readWebVTTFile(file)) { + if (captionLines.length < 2) continue; + print(captionLines); + + final int captionNumber = number; + final _StartAndEnd startAndEnd = + _StartAndEnd.fromWebVTTString(captionLines[0]); + + final String text = captionLines.sublist(1).join('\n'); + + //TODO: Handle text format + final String textWithoutFormat = _parseHtmlString(text); + + final Caption newCaption = Caption( + number: captionNumber, + start: startAndEnd.start, + end: startAndEnd.end, + text: textWithoutFormat, + ); + + if (newCaption.start != null && newCaption.end != null) { + captions.add(newCaption); + number++; + } + } + + return captions; +} + +class _StartAndEnd { + final Duration start; + final Duration end; + + _StartAndEnd(this.start, this.end); + + // Assumes format from an VTT file. + // For example: + // 00:09.000 --> 00:11.000 + static _StartAndEnd fromWebVTTString(String line) { + final RegExp format = + RegExp(_webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp); + + if (!format.hasMatch(line)) { + return _StartAndEnd(null, null); + } + + final List times = line.split(_webVTTArrow); + + final Duration start = _parseWebVTTTimestamp(times[0]); + final Duration end = _parseWebVTTTimestamp(times[1]); + + return _StartAndEnd(start, end); + } +} + +String _parseHtmlString(String htmlString) { + var document = parse(htmlString); + String parsedString = parse(document.body.text).documentElement.text; + return parsedString; +} + +// Parses a time stamp in an VTT file into a Duration. +// For example: +// +// _parseWebVTTimestamp('00:01:08.430') +// returns +// Duration(hours: 0, minutes: 1, seconds: 8, milliseconds: 430) +Duration _parseWebVTTTimestamp(String timestampString) { + if (!RegExp(_webVTTTimeStamp).hasMatch(timestampString)) { + return null; + } + + final List dotSections = timestampString.split('.'); + final List hoursMinutesSeconds = dotSections[0].split(':'); + + int hours = 0; + int minutes = 0; + int seconds = 0; + List styles; + + if (hoursMinutesSeconds.length > 2) { + // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] + hours = int.parse(hoursMinutesSeconds[0]); + minutes = int.parse(hoursMinutesSeconds[1]); + seconds = int.parse(hoursMinutesSeconds[2]); + } else if (int.parse(hoursMinutesSeconds[0]) > 59) { + // Timestamp takes the form of [hours]:[minutes].[milliseconds] + // First position is hours as it's over 59. + hours = int.parse(hoursMinutesSeconds[0]); + minutes = int.parse(hoursMinutesSeconds[1]); + } else { + // Timestamp takes the form of [minutes]:[seconds].[milliseconds] + minutes = int.parse(hoursMinutesSeconds[0]); + seconds = int.parse(hoursMinutesSeconds[1]); + } + + List milisecondsStyles = dotSections[1].split(" "); + //TODO: Handle styles data on timestamp + if (milisecondsStyles.length > 1) { + styles = milisecondsStyles.sublist(1); + } + int milliseconds = int.parse(milisecondsStyles[0]); + + return Duration( + hours: hours, + minutes: minutes, + seconds: seconds, + milliseconds: milliseconds, + ); +} + +// Reads on SubRip file and splits it into Lists of strings where each list is one +// caption. +List> _readWebVTTFile(String file) { + final List lines = LineSplitter.split(file).toList(); + + final List> captionStrings = >[]; + List currentCaption = []; + int lineIndex = 0; + for (final String line in lines) { + final bool isLineBlank = line.trim().isEmpty; + if (!isLineBlank) { + currentCaption.add(line); + } + + if (isLineBlank || lineIndex == lines.length - 1) { + captionStrings.add(currentCaption); + currentCaption = []; + } + + lineIndex += 1; + } + + return captionStrings; +} + +const String _webVTTTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; +const String _webVTTArrow = r' --> '; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 03f71bf4f412..b88edcde5d3c 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -26,7 +26,8 @@ dependencies: # validation, so we set a ^ constraint. # TODO(amirh): Revisit this (either update this part in the design or the pub tool). # https://github.com/flutter/flutter/issues/46264 - video_player_web: '>=0.1.1 <2.0.0' + video_player_web: ">=0.1.1 <2.0.0" + html: ^0.14.0+3 flutter: sdk: flutter diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt.test.dart new file mode 100644 index 000000000000..a9e4cae2e8ed --- /dev/null +++ b/packages/video_player/video_player/test/web_vtt.test.dart @@ -0,0 +1,119 @@ +// Copyright 2020 The Chromium 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_test/flutter_test.dart'; +import 'package:video_player/src/closed_caption_file.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + test('Parses VTT file', () { + final WebVTTCaptionFile parsedFile = WebVTTCaptionFile(_validVTT); + + expect(parsedFile.captions.length, 13); + + final Caption firstCaption = parsedFile.captions.first; + expect(firstCaption.number, 1); + expect(firstCaption.start, Duration(seconds: 9)); + expect(firstCaption.end, Duration(seconds: 11, milliseconds: 430)); + expect(firstCaption.text, 'We are in New York City'); + + final Caption secondCaption = parsedFile.captions[1]; + expect(secondCaption.number, 2); + expect( + secondCaption.start, + Duration(minutes: 0, seconds: 13, milliseconds: 0), + ); + expect( + secondCaption.end, + Duration(minutes: 0, seconds: 16, milliseconds: 0), + ); + expect(secondCaption.text, + "We're actually at the Lucern Hotel, just down the street"); + + //With styles on timestamp + final Caption lastCaption = parsedFile.captions[12]; + expect(lastCaption.number, 13); + expect( + lastCaption.start, + Duration(minutes: 0, seconds: 35, milliseconds: 500), + ); + expect( + lastCaption.end, + Duration(minutes: 0, seconds: 38, milliseconds: 0), + ); + expect(lastCaption.text, + "You know I'm so excited my glasses are falling off here."); + }); + + test('Parses VTT file with malformed input', () { + final ClosedCaptionFile parsedFile = WebVTTCaptionFile(_malformedVTT); + + expect(parsedFile.captions.length, 1); + + final Caption firstCaption = parsedFile.captions.single; + expect(firstCaption.number, 1); + expect(firstCaption.start, Duration(seconds: 13)); + expect(firstCaption.end, Duration(seconds: 16, milliseconds: 0)); + expect(firstCaption.text, 'Valid'); + }); +} + +const String _validVTT = ''' +WEBVTT Kind: captions; Language: en + +00:09.000 --> 00:11.430 +We are in New York City + +00:13.000 --> 00:16.000 +We're actually at the Lucern Hotel, just down the street + +00:16.000 --> 00:18.000 +from the American Museum of Natural History + +00:18.000 --> 00:20.000 +And with me is Neil deGrasse Tyson + +00:20.000 --> 00:22.000 +Astrophysicist, Director of the Hayden Planetarium + +00:22.000 --> 00:24.000 +at the AMNH. + +00:24.000 --> 00:26.000 +Thank you for walking down here. + +00:27.000 --> 00:30.000 +And I want to do a follow-up on the last conversation we did. + +00:30.000 --> 00:31.500 align:end size:50% +When we e-mailed— + +00:30.500 --> 00:32.500 align:start size:50% +Didn't we talk about enough in that conversation? + +00:32.000 --> 00:35.500 align:end size:50% +No! No no no no; 'cos 'cos obviously 'cos + +00:32.500 --> 00:33.500 align:start size:50% +Laughs + +00:35.500 --> 00:38.000 align:start size:50% +You know I'm so excited my glasses are falling off here. + +'''; + +const String _malformedVTT = ''' + +WEBVTT Kind: captions; Language: en + +00:09.000--> 00:11.430 +This one should be ignored because the arrow needs a space. + +00:13.000 --> 00:16.000 +Valid + +00:16.000 --> 00:8.000 +This one should be ignored because the time is missing a digit. + +'''; From a66f5d75faf7fbe068580e59b37f5703c2de3b72 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 14:10:19 -0300 Subject: [PATCH 02/69] Fixes --- packages/video_player/video_player/lib/src/web_vtt.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index d9978c259148..1b7305f1766e 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -16,7 +16,7 @@ class WebVTTCaptionFile extends ClosedCaptionFile { WebVTTCaptionFile(this.fileContents) : _captions = _parseCaptionsFromWebVTTString(fileContents); - /// The entire body of the SubRip file. + /// The entire body of the VTT file. final String fileContents; @override @@ -139,7 +139,7 @@ Duration _parseWebVTTTimestamp(String timestampString) { ); } -// Reads on SubRip file and splits it into Lists of strings where each list is one +// Reads on VTT file and splits it into Lists of strings where each list is one // caption. List> _readWebVTTFile(String file) { final List lines = LineSplitter.split(file).toList(); From 64813317c949d86314a3b0c069d000b9e64dfc64 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 15:24:14 -0300 Subject: [PATCH 03/69] VTT with header added --- .../video_player/example/assets/sample.vtt | 49 +++++-------------- .../example/ios/Flutter/Flutter.podspec | 18 +++++++ .../ios/Runner.xcodeproj/project.pbxproj | 17 ++----- .../video_player/example/lib/main.dart | 6 +-- .../video_player/lib/src/web_vtt.dart | 13 +++-- 5 files changed, 46 insertions(+), 57 deletions(-) create mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt index 2e5c750a7cee..2d00e17e82b4 100644 --- a/packages/video_player/video_player/example/assets/sample.vtt +++ b/packages/video_player/video_player/example/assets/sample.vtt @@ -1,40 +1,15 @@ -WEBVTT +WEBVTT - This file has cues. -00:00.500 --> 00:01.000 -We are in New York City +14 +00:00:01.815 --> 00:00:02.114 +- What? +- Where are we now? -00:01.200 --> 00:02.000 -We’re actually at the Lucern Hotel, just down the street +15 +00:01:18.171 --> 00:01:20.991 +- This is big bat country. -00:16.000 --> 00:18.000 -from the American Museum of Natural History - -00:18.000 --> 00:20.000 -And with me is Neil deGrasse Tyson - -00:20.000 --> 00:22.000 -Astrophysicist, Director of the Hayden Planetarium - -00:22.000 --> 00:24.000 -at the AMNH. - -00:24.000 --> 00:26.000 -Thank you for walking down here. - -00:27.000 --> 00:30.000 -And I want to do a follow-up on the last conversation we did. - -00:30.000 --> 00:31.500 align:right size:50% -When we e-mailed— - -00:30.500 --> 00:32.500 align:left size:50% -Didn’t we talk about enough in that conversation? - -00:32.000 --> 00:35.500 align:right size:50% -No! No no no no; 'cos 'cos obviously 'cos - -00:32.500 --> 00:33.500 align:left size:50% -Laughs - -00:35.500 --> 00:38.000 -You know I’m so excited my glasses are falling off here. \ No newline at end of file +16 +00:01:21.058 --> 00:01:23.868 +- [ Bats Screeching ] +- They won't get in your hair. They're after the bugs. \ No newline at end of file diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec new file mode 100644 index 000000000000..5ca30416bac0 --- /dev/null +++ b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.description = <<-DESC +Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. + DESC + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + s.vendored_frameworks = 'Flutter.framework' +end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 9f0a7ef189b9..8e17be05e07b 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,13 +35,11 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -63,8 +55,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -92,9 +82,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -229,7 +217,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -237,9 +225,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index bfe81b9056fb..e7e29f7c7780 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -209,9 +209,9 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { VideoPlayerController _controller; Future _loadCaptions() async { - final String fileContents = await DefaultAssetBundle.of(context) - .loadString('assets/bumble_bee_captions.srt'); - return SubRipCaptionFile(fileContents); + final String fileContents = + await DefaultAssetBundle.of(context).loadString('assets/sample.vtt'); + return WebVTTCaptionFile(fileContents); } @override diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 1b7305f1766e..22abc081943a 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -30,13 +30,18 @@ List _parseCaptionsFromWebVTTString(String file) { int number = 1; for (List captionLines in _readWebVTTFile(file)) { if (captionLines.length < 2) continue; - print(captionLines); + + // Caption has header + bool hasHeader = captionLines.length > 2; + if (hasHeader) { + number = int.parse(captionLines[0]); + } final int captionNumber = number; - final _StartAndEnd startAndEnd = - _StartAndEnd.fromWebVTTString(captionLines[0]); + final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVTTString( + hasHeader ? captionLines[1] : captionLines[0]); - final String text = captionLines.sublist(1).join('\n'); + final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); //TODO: Handle text format final String textWithoutFormat = _parseHtmlString(text); From 8f8b095356828091fd61fcb343a6ebdebecb3ad0 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 15:25:09 -0300 Subject: [PATCH 04/69] VTT with header added --- .../video_player/example/assets/sample.vtt | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt index 2d00e17e82b4..2e5c750a7cee 100644 --- a/packages/video_player/video_player/example/assets/sample.vtt +++ b/packages/video_player/video_player/example/assets/sample.vtt @@ -1,15 +1,40 @@ -WEBVTT - This file has cues. +WEBVTT -14 -00:00:01.815 --> 00:00:02.114 -- What? -- Where are we now? +00:00.500 --> 00:01.000 +We are in New York City -15 -00:01:18.171 --> 00:01:20.991 -- This is big bat country. +00:01.200 --> 00:02.000 +We’re actually at the Lucern Hotel, just down the street -16 -00:01:21.058 --> 00:01:23.868 -- [ Bats Screeching ] -- They won't get in your hair. They're after the bugs. \ No newline at end of file +00:16.000 --> 00:18.000 +from the American Museum of Natural History + +00:18.000 --> 00:20.000 +And with me is Neil deGrasse Tyson + +00:20.000 --> 00:22.000 +Astrophysicist, Director of the Hayden Planetarium + +00:22.000 --> 00:24.000 +at the AMNH. + +00:24.000 --> 00:26.000 +Thank you for walking down here. + +00:27.000 --> 00:30.000 +And I want to do a follow-up on the last conversation we did. + +00:30.000 --> 00:31.500 align:right size:50% +When we e-mailed— + +00:30.500 --> 00:32.500 align:left size:50% +Didn’t we talk about enough in that conversation? + +00:32.000 --> 00:35.500 align:right size:50% +No! No no no no; 'cos 'cos obviously 'cos + +00:32.500 --> 00:33.500 align:left size:50% +Laughs + +00:35.500 --> 00:38.000 +You know I’m so excited my glasses are falling off here. \ No newline at end of file From da1c6e4c56510b54a951cf74a4b3c96399f455db Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 15:25:41 -0300 Subject: [PATCH 05/69] VTT with header added --- packages/video_player/video_player/example/lib/main.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index e7e29f7c7780..bfe81b9056fb 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -209,9 +209,9 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { VideoPlayerController _controller; Future _loadCaptions() async { - final String fileContents = - await DefaultAssetBundle.of(context).loadString('assets/sample.vtt'); - return WebVTTCaptionFile(fileContents); + final String fileContents = await DefaultAssetBundle.of(context) + .loadString('assets/bumble_bee_captions.srt'); + return SubRipCaptionFile(fileContents); } @override From 1a21621c1568841d14a180ea1831a99d196ac53c Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 16:14:12 -0300 Subject: [PATCH 06/69] VTT with header added --- .../example/ios/Flutter/Flutter.podspec | 18 ----- .../ios/Runner.xcodeproj/project.pbxproj | 17 ++++- .../video_player/lib/src/web_vtt.dart | 74 +++++++++++-------- 3 files changed, 56 insertions(+), 53 deletions(-) delete mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec deleted file mode 100644 index 5ca30416bac0..000000000000 --- a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.description = <<-DESC -Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. - DESC - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' - s.vendored_frameworks = 'Flutter.framework' -end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 8e17be05e07b..9f0a7ef189b9 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -24,6 +28,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -35,11 +41,13 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -55,6 +63,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -82,7 +92,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -217,7 +229,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -225,12 +237,9 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 22abc081943a..b09289098950 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -4,19 +4,21 @@ import 'dart:convert'; +import 'package:html/dom.dart'; + import 'closed_caption_file.dart'; -import 'package:html/parser.dart'; +import 'package:html/parser.dart' as html_parser; -/// Represents a [ClosedCaptionFile], parsed from the WebVTT file format. -/// See: https://en.wikipedia.org/wiki/WebVTT -class WebVTTCaptionFile extends ClosedCaptionFile { +/// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. +/// See: https://en.wikipedia.org/wiki/WebVtt +class WebVttCaptionFile extends ClosedCaptionFile { /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in - /// the WebVTT file format. - /// * See: https://en.wikipedia.org/wiki/WebVTT - WebVTTCaptionFile(this.fileContents) - : _captions = _parseCaptionsFromWebVTTString(fileContents); + /// the WebVtt file format. + /// * See: https://en.wikipedia.org/wiki/WebVtt + WebVttCaptionFile(this.fileContents) + : _captions = _parseCaptionsFromWebVttString(fileContents); - /// The entire body of the VTT file. + /// The entire body of the Vtt file. final String fileContents; @override @@ -25,10 +27,10 @@ class WebVTTCaptionFile extends ClosedCaptionFile { final List _captions; } -List _parseCaptionsFromWebVTTString(String file) { +List _parseCaptionsFromWebVttString(String file) { final List captions = []; int number = 1; - for (List captionLines in _readWebVTTFile(file)) { + for (List captionLines in _readWebVttFile(file)) { if (captionLines.length < 2) continue; // Caption has header @@ -38,12 +40,17 @@ List _parseCaptionsFromWebVTTString(String file) { } final int captionNumber = number; - final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVTTString( - hasHeader ? captionLines[1] : captionLines[0]); + final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVttString( + hasHeader ? captionLines[1] : captionLines[0], + ); final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); - //TODO: Handle text format + /// TODO: Handle text formats + /// Some captions comes with anotations (information about who is speaking) and styles tags. + /// E.g: + + /// Laughs final String textWithoutFormat = _parseHtmlString(text); final Caption newCaption = Caption( @@ -68,40 +75,41 @@ class _StartAndEnd { _StartAndEnd(this.start, this.end); - // Assumes format from an VTT file. + // Assumes format from an Vtt file. // For example: // 00:09.000 --> 00:11.000 - static _StartAndEnd fromWebVTTString(String line) { + static _StartAndEnd fromWebVttString(String line) { final RegExp format = - RegExp(_webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp); + RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); if (!format.hasMatch(line)) { return _StartAndEnd(null, null); } - final List times = line.split(_webVTTArrow); + final List times = line.split(_webVttArrow); - final Duration start = _parseWebVTTTimestamp(times[0]); - final Duration end = _parseWebVTTTimestamp(times[1]); + final Duration start = _parseWebVttTimestamp(times[0]); + final Duration end = _parseWebVttTimestamp(times[1]); return _StartAndEnd(start, end); } } String _parseHtmlString(String htmlString) { - var document = parse(htmlString); - String parsedString = parse(document.body.text).documentElement.text; + final Document document = html_parser.parse(htmlString); + final String parsedString = + html_parser.parse(document.body.text).documentElement.text; return parsedString; } -// Parses a time stamp in an VTT file into a Duration. +// Parses a time stamp in an Vtt file into a Duration. // For example: // -// _parseWebVTTimestamp('00:01:08.430') +// _parseWebVttimestamp('00:01:08.430') // returns // Duration(hours: 0, minutes: 1, seconds: 8, milliseconds: 430) -Duration _parseWebVTTTimestamp(String timestampString) { - if (!RegExp(_webVTTTimeStamp).hasMatch(timestampString)) { +Duration _parseWebVttTimestamp(String timestampString) { + if (!RegExp(_webVttTimeStamp).hasMatch(timestampString)) { return null; } @@ -130,7 +138,11 @@ Duration _parseWebVTTTimestamp(String timestampString) { } List milisecondsStyles = dotSections[1].split(" "); - //TODO: Handle styles data on timestamp + + /// TODO: Handle styles + /// Some captions comes with styles about where/how the caption should be rendered. + /// E.g: + /// 00:32.500 --> 00:33.500 align:left size:50% if (milisecondsStyles.length > 1) { styles = milisecondsStyles.sublist(1); } @@ -144,9 +156,9 @@ Duration _parseWebVTTTimestamp(String timestampString) { ); } -// Reads on VTT file and splits it into Lists of strings where each list is one +// Reads on Vtt file and splits it into Lists of strings where each list is one // caption. -List> _readWebVTTFile(String file) { +List> _readWebVttFile(String file) { final List lines = LineSplitter.split(file).toList(); final List> captionStrings = >[]; @@ -169,5 +181,5 @@ List> _readWebVTTFile(String file) { return captionStrings; } -const String _webVTTTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; -const String _webVTTArrow = r' --> '; +const String _webVttTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; +const String _webVttArrow = r' --> '; From 0d3549e37cf6a40a478d705ec01b5c49b4897c0d Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 17:06:46 -0300 Subject: [PATCH 07/69] New tests added --- .../example/assets/bumble_bee_captions.vtt | 7 + .../video_player/example/assets/sample.vtt | 40 ----- .../lib/src/closed_caption_file.dart | 4 +- .../video_player/lib/src/web_vtt.dart | 10 +- .../video_player/test/web_vtt.test.dart | 149 ++++++++++++------ 5 files changed, 120 insertions(+), 90 deletions(-) create mode 100644 packages/video_player/video_player/example/assets/bumble_bee_captions.vtt delete mode 100644 packages/video_player/video_player/example/assets/sample.vtt diff --git a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt new file mode 100644 index 000000000000..f077a18f289b --- /dev/null +++ b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt @@ -0,0 +1,7 @@ +WEBVTT + +00:00:00,200 --> 00:00:01,750 +[ Birds chirping ] + +00:00:02,300 --> 00:00:05,000 +[ Buzzing ] diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt deleted file mode 100644 index 2e5c750a7cee..000000000000 --- a/packages/video_player/video_player/example/assets/sample.vtt +++ /dev/null @@ -1,40 +0,0 @@ -WEBVTT - -00:00.500 --> 00:01.000 -We are in New York City - -00:01.200 --> 00:02.000 -We’re actually at the Lucern Hotel, just down the street - -00:16.000 --> 00:18.000 -from the American Museum of Natural History - -00:18.000 --> 00:20.000 -And with me is Neil deGrasse Tyson - -00:20.000 --> 00:22.000 -Astrophysicist, Director of the Hayden Planetarium - -00:22.000 --> 00:24.000 -at the AMNH. - -00:24.000 --> 00:26.000 -Thank you for walking down here. - -00:27.000 --> 00:30.000 -And I want to do a follow-up on the last conversation we did. - -00:30.000 --> 00:31.500 align:right size:50% -When we e-mailed— - -00:30.500 --> 00:32.500 align:left size:50% -Didn’t we talk about enough in that conversation? - -00:32.000 --> 00:35.500 align:right size:50% -No! No no no no; 'cos 'cos obviously 'cos - -00:32.500 --> 00:33.500 align:left size:50% -Laughs - -00:35.500 --> 00:38.000 -You know I’m so excited my glasses are falling off here. \ No newline at end of file diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 8cbe5dee896f..6f146f5d0d38 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -6,7 +6,7 @@ import 'sub_rip.dart'; export 'sub_rip.dart' show SubRipCaptionFile; import 'web_vtt.dart'; -export 'web_vtt.dart' show WebVTTCaptionFile; +export 'web_vtt.dart' show WebVttCaptionFile; /// A structured representation of a parsed closed caption file. /// @@ -18,7 +18,7 @@ export 'web_vtt.dart' show WebVTTCaptionFile; /// /// See: /// * [SubRipCaptionFile]. -/// * [WebVTTCaptionFile]. +/// * [WebVttCaptionFile]. abstract class ClosedCaptionFile { /// The full list of captions from a given file. /// diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index b09289098950..d1a5985d53e7 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -29,10 +29,17 @@ class WebVttCaptionFile extends ClosedCaptionFile { List _parseCaptionsFromWebVttString(String file) { final List captions = []; + + /// Ignore metadata + List metadata = ['HEADER', 'NOTE', 'REGION', 'WEBVTT']; + int number = 1; for (List captionLines in _readWebVttFile(file)) { if (captionLines.length < 2) continue; + String metadaType = captionLines[0]?.split(' ')[0]; + if (metadata.contains(metadaType)) continue; + // Caption has header bool hasHeader = captionLines.length > 2; if (hasHeader) { @@ -47,9 +54,8 @@ List _parseCaptionsFromWebVttString(String file) { final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); /// TODO: Handle text formats - /// Some captions comes with anotations (information about who is speaking) and styles tags. + /// Some captions comes with anotations (information about who/how is the speech being delivered) and styles tags. /// E.g: - /// Laughs final String textWithoutFormat = _parseHtmlString(text); diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt.test.dart index a9e4cae2e8ed..5718b1eb23f5 100644 --- a/packages/video_player/video_player/test/web_vtt.test.dart +++ b/packages/video_player/video_player/test/web_vtt.test.dart @@ -8,46 +8,101 @@ import 'package:video_player/video_player.dart'; void main() { test('Parses VTT file', () { - final WebVTTCaptionFile parsedFile = WebVTTCaptionFile(_validVTT); + final WebVttCaptionFile parsedFile = WebVttCaptionFile(_validVTT); - expect(parsedFile.captions.length, 13); + expect(parsedFile.captions.length, 7); + //[minutes]:[seconds].[milliseconds] final Caption firstCaption = parsedFile.captions.first; expect(firstCaption.number, 1); - expect(firstCaption.start, Duration(seconds: 9)); - expect(firstCaption.end, Duration(seconds: 11, milliseconds: 430)); + expect(firstCaption.start, Duration(seconds: 1)); + expect(firstCaption.end, Duration(seconds: 2, milliseconds: 500)); expect(firstCaption.text, 'We are in New York City'); + //With multiline final Caption secondCaption = parsedFile.captions[1]; expect(secondCaption.number, 2); expect( secondCaption.start, - Duration(minutes: 0, seconds: 13, milliseconds: 0), + Duration(minutes: 0, seconds: 2, milliseconds: 800), ); expect( secondCaption.end, - Duration(minutes: 0, seconds: 16, milliseconds: 0), + Duration(minutes: 0, seconds: 3, milliseconds: 283), ); expect(secondCaption.text, - "We're actually at the Lucern Hotel, just down the street"); + "— It will perforate your stomach.\n— You could die."); - //With styles on timestamp - final Caption lastCaption = parsedFile.captions[12]; - expect(lastCaption.number, 13); + //With Long Text + final Caption thirdCaption = parsedFile.captions[2]; + expect(thirdCaption.number, 3); + expect( + thirdCaption.start, + Duration(minutes: 0, seconds: 4, milliseconds: 0), + ); + expect( + thirdCaption.end, + Duration(minutes: 0, seconds: 4, milliseconds: 900), + ); + expect(thirdCaption.text, + "The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided."); + + //With styles on html style tags + final Caption fourthCaption = parsedFile.captions[3]; + expect(fourthCaption.number, 4); + expect( + fourthCaption.start, + Duration(minutes: 0, seconds: 5, milliseconds: 200), + ); + expect( + fourthCaption.end, + Duration(minutes: 0, seconds: 6, milliseconds: 0), + ); + expect(fourthCaption.text, + "You know I'm so excited my glasses are falling off here."); + + //With format [hours]:[minutes]:[seconds].[milliseconds] + final Caption fifthCaption = parsedFile.captions[4]; + expect(fifthCaption.number, 5); + expect( + fifthCaption.start, + Duration(minutes: 0, seconds: 6, milliseconds: 050), + ); + expect( + fifthCaption.end, + Duration(minutes: 0, seconds: 6, milliseconds: 150), + ); + expect(fifthCaption.text, "I have a different time!"); + + //With custom html tag + final Caption sixthCaption = parsedFile.captions[5]; + expect(sixthCaption.number, 6); + expect( + sixthCaption.start, + Duration(minutes: 0, seconds: 6, milliseconds: 200), + ); + expect( + sixthCaption.end, + Duration(minutes: 0, seconds: 6, milliseconds: 900), + ); + expect(sixthCaption.text, "This is yellow text on a blue background"); + + //With format [hours]:[minutes].[milliseconds] + final Caption lastCaption = parsedFile.captions[6]; + expect(lastCaption.number, 7); expect( lastCaption.start, - Duration(minutes: 0, seconds: 35, milliseconds: 500), + Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 000), ); expect( lastCaption.end, - Duration(minutes: 0, seconds: 38, milliseconds: 0), + Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 900), ); - expect(lastCaption.text, - "You know I'm so excited my glasses are falling off here."); + expect(lastCaption.text, "Hour"); }); test('Parses VTT file with malformed input', () { - final ClosedCaptionFile parsedFile = WebVTTCaptionFile(_malformedVTT); + final ClosedCaptionFile parsedFile = WebVttCaptionFile(_malformedVTT); expect(parsedFile.captions.length, 1); @@ -62,44 +117,46 @@ void main() { const String _validVTT = ''' WEBVTT Kind: captions; Language: en -00:09.000 --> 00:11.430 +REGION +id:bill +width:40% +lines:3 +regionanchor:100%,100% +viewportanchor:90%,90% +scroll:up + +NOTE +This file was written by Jill. I hope +you enjoy reading it. Some things to +bear in mind: +- I was lip-reading, so the cues may +not be 100% accurate +- I didn’t pay too close attention to +when the cues should start or end. + +1 +00:01.000 --> 00:02.500 We are in New York City -00:13.000 --> 00:16.000 -We're actually at the Lucern Hotel, just down the street - -00:16.000 --> 00:18.000 -from the American Museum of Natural History - -00:18.000 --> 00:20.000 -And with me is Neil deGrasse Tyson - -00:20.000 --> 00:22.000 -Astrophysicist, Director of the Hayden Planetarium - -00:22.000 --> 00:24.000 -at the AMNH. - -00:24.000 --> 00:26.000 -Thank you for walking down here. - -00:27.000 --> 00:30.000 -And I want to do a follow-up on the last conversation we did. +2 +00:02.800 --> 00:03.283 +— It will perforate your stomach. +— You could die. -00:30.000 --> 00:31.500 align:end size:50% -When we e-mailed— +00:04.000 --> 00:04.900 +The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided. -00:30.500 --> 00:32.500 align:start size:50% -Didn't we talk about enough in that conversation? +00:05.200 --> 00:06.000 align:start size:50% +You know I'm so excited my glasses are falling off here. -00:32.000 --> 00:35.500 align:end size:50% -No! No no no no; 'cos 'cos obviously 'cos +00:00:06.050 --> 00:00:06.150 +I have a different time! -00:32.500 --> 00:33.500 align:start size:50% -Laughs +00:06.200 --> 00:06.900 +This is yellow text on a blue background -00:35.500 --> 00:38.000 align:start size:50% -You know I'm so excited my glasses are falling off here. +60:01.000 --> 60:01.900 +Hour '''; From b018ea94842e42e1776dd79752f6628f903be272 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:13:33 -0300 Subject: [PATCH 08/69] tests organized by group --- .../video_player/lib/src/web_vtt.dart | 11 +- .../video_player/test/web_vtt.test.dart | 230 ++++++++++-------- 2 files changed, 139 insertions(+), 102 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index d1a5985d53e7..ff76c3034df1 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -33,20 +33,21 @@ List _parseCaptionsFromWebVttString(String file) { /// Ignore metadata List metadata = ['HEADER', 'NOTE', 'REGION', 'WEBVTT']; - int number = 1; + int captionNumber = 1; for (List captionLines in _readWebVttFile(file)) { + // if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; + // if caption has header equal metadata, ignore String metadaType = captionLines[0]?.split(' ')[0]; if (metadata.contains(metadaType)) continue; // Caption has header bool hasHeader = captionLines.length > 2; - if (hasHeader) { - number = int.parse(captionLines[0]); + if (hasHeader && int.tryParse(captionLines[0]) != null) { + captionNumber = int.parse(captionLines[0]); } - final int captionNumber = number; final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVttString( hasHeader ? captionLines[1] : captionLines[0], ); @@ -68,7 +69,7 @@ List _parseCaptionsFromWebVttString(String file) { if (newCaption.start != null && newCaption.end != null) { captions.add(newCaption); - number++; + captionNumber++; } } diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt.test.dart index 5718b1eb23f5..69925da0258d 100644 --- a/packages/video_player/video_player/test/web_vtt.test.dart +++ b/packages/video_player/video_player/test/web_vtt.test.dart @@ -7,98 +7,82 @@ import 'package:video_player/src/closed_caption_file.dart'; import 'package:video_player/video_player.dart'; void main() { - test('Parses VTT file', () { - final WebVttCaptionFile parsedFile = WebVttCaptionFile(_validVTT); - - expect(parsedFile.captions.length, 7); - - //[minutes]:[seconds].[milliseconds] - final Caption firstCaption = parsedFile.captions.first; - expect(firstCaption.number, 1); - expect(firstCaption.start, Duration(seconds: 1)); - expect(firstCaption.end, Duration(seconds: 2, milliseconds: 500)); - expect(firstCaption.text, 'We are in New York City'); - - //With multiline - final Caption secondCaption = parsedFile.captions[1]; - expect(secondCaption.number, 2); - expect( - secondCaption.start, - Duration(minutes: 0, seconds: 2, milliseconds: 800), - ); - expect( - secondCaption.end, - Duration(minutes: 0, seconds: 3, milliseconds: 283), - ); - expect(secondCaption.text, - "— It will perforate your stomach.\n— You could die."); - - //With Long Text - final Caption thirdCaption = parsedFile.captions[2]; - expect(thirdCaption.number, 3); - expect( - thirdCaption.start, - Duration(minutes: 0, seconds: 4, milliseconds: 0), - ); - expect( - thirdCaption.end, - Duration(minutes: 0, seconds: 4, milliseconds: 900), - ); - expect(thirdCaption.text, - "The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided."); - - //With styles on html style tags - final Caption fourthCaption = parsedFile.captions[3]; - expect(fourthCaption.number, 4); - expect( - fourthCaption.start, - Duration(minutes: 0, seconds: 5, milliseconds: 200), - ); - expect( - fourthCaption.end, - Duration(minutes: 0, seconds: 6, milliseconds: 0), - ); - expect(fourthCaption.text, - "You know I'm so excited my glasses are falling off here."); - - //With format [hours]:[minutes]:[seconds].[milliseconds] - final Caption fifthCaption = parsedFile.captions[4]; - expect(fifthCaption.number, 5); - expect( - fifthCaption.start, - Duration(minutes: 0, seconds: 6, milliseconds: 050), - ); - expect( - fifthCaption.end, - Duration(minutes: 0, seconds: 6, milliseconds: 150), - ); - expect(fifthCaption.text, "I have a different time!"); - - //With custom html tag - final Caption sixthCaption = parsedFile.captions[5]; - expect(sixthCaption.number, 6); - expect( - sixthCaption.start, - Duration(minutes: 0, seconds: 6, milliseconds: 200), - ); - expect( - sixthCaption.end, - Duration(minutes: 0, seconds: 6, milliseconds: 900), - ); - expect(sixthCaption.text, "This is yellow text on a blue background"); - - //With format [hours]:[minutes].[milliseconds] - final Caption lastCaption = parsedFile.captions[6]; - expect(lastCaption.number, 7); - expect( - lastCaption.start, - Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 000), - ); - expect( - lastCaption.end, - Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 900), - ); - expect(lastCaption.text, "Hour"); + group('Parse VTT file', () { + WebVttCaptionFile parsedFile; + + test('with Metadata', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_metadata); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].start, Duration(seconds: 1)); + expect( + parsedFile.captions[0].end, Duration(seconds: 2, milliseconds: 500)); + expect(parsedFile.captions[0].text, 'We are in New York City'); + }); + + test('with Multiline', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_multiline); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].start, + Duration(seconds: 2, milliseconds: 800)); + expect( + parsedFile.captions[0].end, Duration(seconds: 3, milliseconds: 283)); + expect(parsedFile.captions[0].text, + "— It will perforate your stomach.\n— You could die."); + }); + + test('with styles tags', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_styles); + expect(parsedFile.captions.length, 3); + + expect(parsedFile.captions[0].start, + Duration(seconds: 5, milliseconds: 200)); + expect( + parsedFile.captions[0].end, Duration(seconds: 6, milliseconds: 000)); + expect(parsedFile.captions[0].text, + "You know I'm so excited my glasses are falling off here."); + }); + + test('with subtitling features', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_subtitling_features); + expect(parsedFile.captions.length, 3); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(seconds: 4)); + expect(parsedFile.captions.last.end, Duration(seconds: 5)); + expect(parsedFile.captions.last.text, "Transcrit par Célestes™"); + }); + + test('with [hours]:[minutes]:[seconds].[milliseconds]', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_hours); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(seconds: 1)); + expect(parsedFile.captions.last.end, Duration(seconds: 2)); + expect(parsedFile.captions.last.text, "This is a test."); + }); + + test('with [hours]:[minutes].[milliseconds]', () { + parsedFile = WebVttCaptionFile(_valid_vtt_without_seconds); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(hours: 60)); + expect(parsedFile.captions.last.end, Duration(hours: 60, minutes: 2)); + expect(parsedFile.captions.last.text, "This is a test."); + }); + + test('with [minutes]:[seconds].[milliseconds]', () { + parsedFile = WebVttCaptionFile(_valid_vtt_without_hours); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(seconds: 3)); + expect(parsedFile.captions.last.end, Duration(seconds: 4)); + expect(parsedFile.captions.last.text, "This is a test."); + }); }); test('Parses VTT file with malformed input', () { @@ -114,7 +98,8 @@ void main() { }); } -const String _validVTT = ''' +/// See https://www.w3.org/TR/webvtt1/#introduction-comments +const String _valid_vtt_with_metadata = ''' WEBVTT Kind: captions; Language: en REGION @@ -137,14 +122,22 @@ when the cues should start or end. 1 00:01.000 --> 00:02.500 We are in New York City +'''; + +/// See https://www.w3.org/TR/webvtt1/#introduction-multiple-lines +const String _valid_vtt_with_multiline = ''' +WEBVTT 2 00:02.800 --> 00:03.283 — It will perforate your stomach. — You could die. -00:04.000 --> 00:04.900 -The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided. +'''; + +/// See https://www.w3.org/TR/webvtt1/#styling +const String _valid_vtt_with_styles = ''' +WEBVTT 00:05.200 --> 00:06.000 align:start size:50% You know I'm so excited my glasses are falling off here. @@ -155,8 +148,51 @@ The Organisation for Sample Public Service Announcements accepts no liability fo 00:06.200 --> 00:06.900 This is yellow text on a blue background -60:01.000 --> 60:01.900 -Hour +'''; + +//See https://www.w3.org/TR/webvtt1/#introduction-other-features +const String _valid_vtt_with_subtitling_features = ''' +WEBVTT + +test +00:00.000 --> 00:02.000 +This is a test. + +Slide 1 +00:00:00.000 --> 00:00:10.700 +Title Slide + +crédit de transcription +00:04.000 --> 00:05.000 +Transcrit par Célestes™ + +'''; + +/// With format [hours]:[minutes]:[seconds].[milliseconds] +const String _valid_vtt_with_hours = ''' +WEBVTT + +test +00:00:01.000 --> 00:00:02.000 +This is a test. + +'''; + +/// With format [hours]:[minutes].[milliseconds] +const String _valid_vtt_without_seconds = ''' +WEBVTT + +60:00.000 --> 60:02.000 +This is a test. + +'''; + +/// With format [minutes]:[seconds].[milliseconds] +const String _valid_vtt_without_hours = ''' +WEBVTT + +00:03.000 --> 00:04.000 +This is a test. '''; From b92f305ccff34e8faf97799eb2dfda2896d27b8c Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:22:47 -0300 Subject: [PATCH 09/69] Changelog updated --- .../video_player/video_player/CHANGELOG.md | 226 +++++++++--------- .../video_player/lib/src/web_vtt.dart | 13 +- .../video_player/video_player/pubspec.yaml | 2 +- .../{web_vtt.test.dart => web_vtt_test.dart} | 0 4 files changed, 125 insertions(+), 116 deletions(-) rename packages/video_player/video_player/test/{web_vtt.test.dart => web_vtt_test.dart} (100%) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index e3f49e736411..e35c860dd933 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,214 +1,216 @@ +## 0.10.11+3 + +- Support to WebVtt added. + ## 0.10.11+2 -* Fix aspectRatio calculation when size.width or size.height are zero. +- Fix aspectRatio calculation when size.width or size.height are zero. ## 0.10.11+1 -* Post-v2 Android embedding cleanups. +- Post-v2 Android embedding cleanups. ## 0.10.11 -* iOS: Fixed crash when detaching from a dying engine. -* Android: Fixed exception when detaching from any engine. +- iOS: Fixed crash when detaching from a dying engine. +- Android: Fixed exception when detaching from any engine. ## 0.10.10 -* Migrated to [pigeon](https://pub.dev/packages/pigeon). +- Migrated to [pigeon](https://pub.dev/packages/pigeon). ## 0.10.9+2 -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). +- Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). ## 0.10.9+1 -* Readme updated to include web support and details on how to use for web +- Readme updated to include web support and details on how to use for web ## 0.10.9 -* Remove Android dependencies fallback. -* Require Flutter SDK 1.12.13+hotfix.5 or greater. -* Fix CocoaPods podspec lint warnings. +- Remove Android dependencies fallback. +- Require Flutter SDK 1.12.13+hotfix.5 or greater. +- Fix CocoaPods podspec lint warnings. ## 0.10.8+2 -* Replace deprecated `getFlutterEngine` call on Android. +- Replace deprecated `getFlutterEngine` call on Android. ## 0.10.8+1 -* Make the pedantic dev_dependency explicit. +- Make the pedantic dev_dependency explicit. ## 0.10.8 -* Added support for cleaning up the plugin if used for add-to-app (Flutter +- Added support for cleaning up the plugin if used for add-to-app (Flutter v1.15.3 is required for that feature). - ## 0.10.7 -* `VideoPlayerController` support for reading closed caption files. -* `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. +- `VideoPlayerController` support for reading closed caption files. +- `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. ## 0.10.6 -* `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read +- `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read [SubRip](https://en.wikipedia.org/wiki/SubRip) files into dart objects. ## 0.10.5+3 -* Add integration instructions for the `web` platform. +- Add integration instructions for the `web` platform. ## 0.10.5+2 -* Make sure the plugin is correctly initialized +- Make sure the plugin is correctly initialized ## 0.10.5+1 -* Fixes issue where `initialize()` `Future` stalls when failing to load source +- Fixes issue where `initialize()` `Future` stalls when failing to load source data and does not throw an error. ## 0.10.5 -* Support `web` by default. -* Require Flutter SDK 1.12.13+hotfix.4 or greater. +- Support `web` by default. +- Require Flutter SDK 1.12.13+hotfix.4 or greater. ## 0.10.4+2 -* Remove the deprecated `author:` field form pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. +- Remove the deprecated `author:` field form pubspec.yaml +- Migrate the plugin to the pubspec platforms manifest. +- Require Flutter SDK 1.10.0 or greater. ## 0.10.4+1 -* Fix pedantic lints. This fixes some potential race conditions in cases where +- Fix pedantic lints. This fixes some potential race conditions in cases where futures within some video_player methods weren't being awaited correctly. ## 0.10.4 -* Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. +- Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. ## 0.10.3+3 -* Add DartDocs and unit tests. +- Add DartDocs and unit tests. ## 0.10.3+2 -* Update the homepage to point to the new plugin location +- Update the homepage to point to the new plugin location ## 0.10.3+1 -* Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. -* Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. -* Updated the example app to include a new page that pop back after video is done playing. +- Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. +- Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. +- Updated the example app to include a new page that pop back after video is done playing. ## 0.10.3 -* Add support for the v2 Android embedding. This shouldn't impact existing +- Add support for the v2 Android embedding. This shouldn't impact existing functionality. ## 0.10.2+6 -* Remove AndroidX warnings. +- Remove AndroidX warnings. ## 0.10.2+5 -* Update unit test for compatibility with Flutter stable branch. +- Update unit test for compatibility with Flutter stable branch. ## 0.10.2+4 -* Define clang module for iOS. +- Define clang module for iOS. ## 0.10.2+3 -* Fix bug where formatHint was not being pass down to network sources. +- Fix bug where formatHint was not being pass down to network sources. ## 0.10.2+2 -* Update and migrate iOS example project. +- Update and migrate iOS example project. ## 0.10.2+1 -* Use DefaultHttpDataSourceFactory only when network schemas and use -DefaultHttpDataSourceFactory by default. +- Use DefaultHttpDataSourceFactory only when network schemas and use + DefaultHttpDataSourceFactory by default. ## 0.10.2 -* **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. +- **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. ## 0.10.1+7 -* Fix tests by ignoring deprecated member use. +- Fix tests by ignoring deprecated member use. ## 0.10.1+6 -* [iOS] Fixed a memory leak with notification observing. +- [iOS] Fixed a memory leak with notification observing. ## 0.10.1+5 -* Fix race condition while disposing the VideoController. +- Fix race condition while disposing the VideoController. ## 0.10.1+4 -* Fixed syntax error in README.md. +- Fixed syntax error in README.md. ## 0.10.1+3 -* Add missing template type parameter to `invokeMethod` calls. -* Bump minimum Flutter version to 1.5.0. -* Replace invokeMethod with invokeMapMethod wherever necessary. +- Add missing template type parameter to `invokeMethod` calls. +- Bump minimum Flutter version to 1.5.0. +- Replace invokeMethod with invokeMapMethod wherever necessary. ## 0.10.1+2 -* Example: Fixed tab display and added scroll view +- Example: Fixed tab display and added scroll view ## 0.10.1+1 -* iOS: Avoid deprecated `seekToTime` API +- iOS: Avoid deprecated `seekToTime` API ## 0.10.1 -* iOS: Consider a player only `initialized` once duration is determined. +- iOS: Consider a player only `initialized` once duration is determined. ## 0.10.0+8 -* iOS: Fix an issue where the player sends initialization message incorrectly. - -* Fix a few other IDE warnings. +- iOS: Fix an issue where the player sends initialization message incorrectly. +- Fix a few other IDE warnings. ## 0.10.0+7 -* Android: Fix issue where buffering status in percentage instead of milliseconds +- Android: Fix issue where buffering status in percentage instead of milliseconds -* Android: Update buffering status everytime we notify for position change +- Android: Update buffering status everytime we notify for position change ## 0.10.0+6 -* Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. +- Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. ## 0.10.0+5 -* Fixed iOS build warnings about implicit retains. +- Fixed iOS build warnings about implicit retains. ## 0.10.0+4 -* Android: Upgrade ExoPlayer to 2.9.6. +- Android: Upgrade ExoPlayer to 2.9.6. ## 0.10.0+3 -* Fix divide by zero bug on iOS. +- Fix divide by zero bug on iOS. ## 0.10.0+2 -* Added supported format documentation in README. +- Added supported format documentation in README. ## 0.10.0+1 -* Log a more detailed warning at build time about the previous AndroidX +- Log a more detailed warning at build time about the previous AndroidX migration. ## 0.10.0 -* **Breaking change**. Migrate from the deprecated original Android Support +- **Breaking change**. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to [also migrate](https://developer.android.com/jetpack/androidx/migrate) if they're @@ -216,153 +218,153 @@ DefaultHttpDataSourceFactory by default. ## 0.9.0 -* Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded - in portrait mode both in iOS and Android. +- Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded + in portrait mode both in iOS and Android. ## 0.8.0 -* Android: Upgrade ExoPlayer to 2.9.1 -* Android: Use current gradle dependencies -* Android 9 compatibility fixes for Demo App +- Android: Upgrade ExoPlayer to 2.9.1 +- Android: Use current gradle dependencies +- Android 9 compatibility fixes for Demo App ## 0.7.2 -* Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. +- Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. ## 0.7.1 -* Fixed null exception on Android when the video has a width or height of 0. +- Fixed null exception on Android when the video has a width or height of 0. ## 0.7.0 -* Add a unit test for controller and texture changes. This is a breaking change since the interface +- Add a unit test for controller and texture changes. This is a breaking change since the interface had to be cleaned up to facilitate faking. ## 0.6.6 -* Fix the condition where the player doesn't update when attached controller is changed. +- Fix the condition where the player doesn't update when attached controller is changed. ## 0.6.5 -* Eliminate race conditions around initialization: now initialization events are queued and guaranteed +- Eliminate race conditions around initialization: now initialization events are queued and guaranteed to be delivered to the Dart side. VideoPlayer widget is rebuilt upon completion of initialization. ## 0.6.4 -* Android: add support for hls, dash and ss video formats. +- Android: add support for hls, dash and ss video formats. ## 0.6.3 -* iOS: Allow audio playback in silent mode. +- iOS: Allow audio playback in silent mode. ## 0.6.2 -* `VideoPlayerController.seekTo()` is now frame accurate on both platforms. +- `VideoPlayerController.seekTo()` is now frame accurate on both platforms. ## 0.6.1 -* iOS: add missing observer removals to prevent crashes on deallocation. +- iOS: add missing observer removals to prevent crashes on deallocation. ## 0.6.0 -* Android: use ExoPlayer instead of MediaPlayer for better video format support. +- Android: use ExoPlayer instead of MediaPlayer for better video format support. ## 0.5.5 -* **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. -* Updated example in README.md. +- **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. +- Updated example in README.md. ## 0.5.4 -* Updated Gradle tooling to match Android Studio 3.1.2. +- Updated Gradle tooling to match Android Studio 3.1.2. ## 0.5.3 -* Added video buffering status. +- Added video buffering status. ## 0.5.2 -* Fixed a bug on iOS that could lead to missing initialization. -* Added support for HLS video on iOS. +- Fixed a bug on iOS that could lead to missing initialization. +- Added support for HLS video on iOS. ## 0.5.1 -* Fixed bug on video loop feature for iOS. +- Fixed bug on video loop feature for iOS. ## 0.5.0 -* Added the constructor `VideoPlayerController.file`. -* **Breaking change**. Changed `VideoPlayerController.isNetwork` to +- Added the constructor `VideoPlayerController.file`. +- **Breaking change**. Changed `VideoPlayerController.isNetwork` to an enum `VideoPlayerController.dataSourceType`. ## 0.4.1 -* Updated Flutter SDK constraint to reflect the changes in v0.4.0. +- Updated Flutter SDK constraint to reflect the changes in v0.4.0. ## 0.4.0 -* **Breaking change**. Removed the `VideoPlayerController` constructor -* Added two new factory constructors `VideoPlayerController.asset` and +- **Breaking change**. Removed the `VideoPlayerController` constructor +- Added two new factory constructors `VideoPlayerController.asset` and `VideoPlayerController.network` to respectively play a video from the Flutter assets and from a network uri. ## 0.3.0 -* **Breaking change**. Set SDK constraints to match the Flutter beta release. +- **Breaking change**. Set SDK constraints to match the Flutter beta release. ## 0.2.1 -* Fixed some signatures to account for strong mode runtime errors. -* Fixed spelling mistake in toString output. +- Fixed some signatures to account for strong mode runtime errors. +- Fixed spelling mistake in toString output. ## 0.2.0 -* **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. -* Updated documentation of when fields are available on `VideoPlayerController`. -* Updated links in README.md. +- **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. +- Updated documentation of when fields are available on `VideoPlayerController`. +- Updated links in README.md. ## 0.1.1 -* Simplified and upgraded Android project template to Android SDK 27. -* Moved Android package to io.flutter.plugins. -* Fixed warnings from the Dart 2.0 analyzer. +- Simplified and upgraded Android project template to Android SDK 27. +- Moved Android package to io.flutter.plugins. +- Fixed warnings from the Dart 2.0 analyzer. ## 0.1.0 -* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin +- **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in order to use this version of the plugin. Instructions can be found [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). ## 0.0.7 -* Added access to the video size. -* Made the VideoProgressIndicator render using a LinearProgressIndicator. +- Added access to the video size. +- Made the VideoProgressIndicator render using a LinearProgressIndicator. ## 0.0.6 -* Fixed a bug related to hot restart on Android. +- Fixed a bug related to hot restart on Android. ## 0.0.5 -* Added VideoPlayerValue.toString(). -* Added FLT prefix to iOS types. +- Added VideoPlayerValue.toString(). +- Added FLT prefix to iOS types. ## 0.0.4 -* The player will now pause on app pause, and resume on app resume. -* Implemented scrubbing on the progress bar. +- The player will now pause on app pause, and resume on app resume. +- Implemented scrubbing on the progress bar. ## 0.0.3 -* Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). -* Added VideoPlayerController.setVolume(). -* Moved the package to flutter/plugins github repo. +- Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). +- Added VideoPlayerController.setVolume(). +- Moved the package to flutter/plugins github repo. ## 0.0.2 -* Fix meta dependency version. +- Fix meta dependency version. ## 0.0.1 -* Initial release +- Initial release diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index ff76c3034df1..cf1db8f84fed 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -35,14 +35,21 @@ List _parseCaptionsFromWebVttString(String file) { int captionNumber = 1; for (List captionLines in _readWebVttFile(file)) { - // if caption has just header or time, but no text, captionLines.length will be < 1 + /// CaptionLines represent a complete caption + /// E.g + /// [ + /// [00:00.000 --> 01:24.000 align:center] + /// ['Introduction'] + /// ] + + /// if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; - // if caption has header equal metadata, ignore + /// if caption has header equal metadata, ignore String metadaType = captionLines[0]?.split(' ')[0]; if (metadata.contains(metadaType)) continue; - // Caption has header + /// Caption has header bool hasHeader = captionLines.length > 2; if (hasHeader && int.tryParse(captionLines[0]) != null) { captionNumber = int.parse(captionLines[0]); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b88edcde5d3c..508a5f5f5b4f 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.10.11+2 +version: 0.10.11+3 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt_test.dart similarity index 100% rename from packages/video_player/video_player/test/web_vtt.test.dart rename to packages/video_player/video_player/test/web_vtt_test.dart From 1573670dc6fa9713cfe4626106cf2843c8e31d19 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:25:23 -0300 Subject: [PATCH 10/69] Sample vtt renamed --- packages/video_player/video_player/example/lib/main.dart | 3 ++- packages/video_player/video_player/example/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index bfe81b9056fb..8d9358a4ec4e 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -211,7 +211,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { Future _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context) .loadString('assets/bumble_bee_captions.srt'); - return SubRipCaptionFile(fileContents); + return SubRipCaptionFile( + fileContents); // For vtt files, use WebVttCaptionFile } @override diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 283a484be711..ea6d0be60be2 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -22,4 +22,4 @@ flutter: - assets/flutter-mark-square-64.png - assets/Butterfly-209.mp4 - assets/bumble_bee_captions.srt - - assets/sample.vtt + - assets/bumble_bee_captions.vtt From 1e53fc5d2c183fcc3485ec11badd1ec4ca6a0173 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:31:38 -0300 Subject: [PATCH 11/69] Sample vtt renamed --- .../video_player/video_player/CHANGELOG.md | 224 +++++++++--------- 1 file changed, 113 insertions(+), 111 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index e35c860dd933..7977562af145 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,216 +1,218 @@ ## 0.10.11+3 -- Support to WebVtt added. +* Support to WebVtt added. ## 0.10.11+2 -- Fix aspectRatio calculation when size.width or size.height are zero. +* Fix aspectRatio calculation when size.width or size.height are zero. ## 0.10.11+1 -- Post-v2 Android embedding cleanups. +* Post-v2 Android embedding cleanups. ## 0.10.11 -- iOS: Fixed crash when detaching from a dying engine. -- Android: Fixed exception when detaching from any engine. +* iOS: Fixed crash when detaching from a dying engine. +* Android: Fixed exception when detaching from any engine. ## 0.10.10 -- Migrated to [pigeon](https://pub.dev/packages/pigeon). +* Migrated to [pigeon](https://pub.dev/packages/pigeon). ## 0.10.9+2 -- Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). +* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). ## 0.10.9+1 -- Readme updated to include web support and details on how to use for web +* Readme updated to include web support and details on how to use for web ## 0.10.9 -- Remove Android dependencies fallback. -- Require Flutter SDK 1.12.13+hotfix.5 or greater. -- Fix CocoaPods podspec lint warnings. +* Remove Android dependencies fallback. +* Require Flutter SDK 1.12.13+hotfix.5 or greater. +* Fix CocoaPods podspec lint warnings. ## 0.10.8+2 -- Replace deprecated `getFlutterEngine` call on Android. +* Replace deprecated `getFlutterEngine` call on Android. ## 0.10.8+1 -- Make the pedantic dev_dependency explicit. +* Make the pedantic dev_dependency explicit. ## 0.10.8 -- Added support for cleaning up the plugin if used for add-to-app (Flutter +* Added support for cleaning up the plugin if used for add-to-app (Flutter v1.15.3 is required for that feature). + ## 0.10.7 -- `VideoPlayerController` support for reading closed caption files. -- `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. +* `VideoPlayerController` support for reading closed caption files. +* `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. ## 0.10.6 -- `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read +* `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read [SubRip](https://en.wikipedia.org/wiki/SubRip) files into dart objects. ## 0.10.5+3 -- Add integration instructions for the `web` platform. +* Add integration instructions for the `web` platform. ## 0.10.5+2 -- Make sure the plugin is correctly initialized +* Make sure the plugin is correctly initialized ## 0.10.5+1 -- Fixes issue where `initialize()` `Future` stalls when failing to load source +* Fixes issue where `initialize()` `Future` stalls when failing to load source data and does not throw an error. ## 0.10.5 -- Support `web` by default. -- Require Flutter SDK 1.12.13+hotfix.4 or greater. +* Support `web` by default. +* Require Flutter SDK 1.12.13+hotfix.4 or greater. ## 0.10.4+2 -- Remove the deprecated `author:` field form pubspec.yaml -- Migrate the plugin to the pubspec platforms manifest. -- Require Flutter SDK 1.10.0 or greater. +* Remove the deprecated `author:` field form pubspec.yaml +* Migrate the plugin to the pubspec platforms manifest. +* Require Flutter SDK 1.10.0 or greater. ## 0.10.4+1 -- Fix pedantic lints. This fixes some potential race conditions in cases where +* Fix pedantic lints. This fixes some potential race conditions in cases where futures within some video_player methods weren't being awaited correctly. ## 0.10.4 -- Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. +* Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. ## 0.10.3+3 -- Add DartDocs and unit tests. +* Add DartDocs and unit tests. ## 0.10.3+2 -- Update the homepage to point to the new plugin location +* Update the homepage to point to the new plugin location ## 0.10.3+1 -- Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. -- Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. -- Updated the example app to include a new page that pop back after video is done playing. +* Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. +* Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. +* Updated the example app to include a new page that pop back after video is done playing. ## 0.10.3 -- Add support for the v2 Android embedding. This shouldn't impact existing +* Add support for the v2 Android embedding. This shouldn't impact existing functionality. ## 0.10.2+6 -- Remove AndroidX warnings. +* Remove AndroidX warnings. ## 0.10.2+5 -- Update unit test for compatibility with Flutter stable branch. +* Update unit test for compatibility with Flutter stable branch. ## 0.10.2+4 -- Define clang module for iOS. +* Define clang module for iOS. ## 0.10.2+3 -- Fix bug where formatHint was not being pass down to network sources. +* Fix bug where formatHint was not being pass down to network sources. ## 0.10.2+2 -- Update and migrate iOS example project. +* Update and migrate iOS example project. ## 0.10.2+1 -- Use DefaultHttpDataSourceFactory only when network schemas and use - DefaultHttpDataSourceFactory by default. +* Use DefaultHttpDataSourceFactory only when network schemas and use +DefaultHttpDataSourceFactory by default. ## 0.10.2 -- **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. +* **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. ## 0.10.1+7 -- Fix tests by ignoring deprecated member use. +* Fix tests by ignoring deprecated member use. ## 0.10.1+6 -- [iOS] Fixed a memory leak with notification observing. +* [iOS] Fixed a memory leak with notification observing. ## 0.10.1+5 -- Fix race condition while disposing the VideoController. +* Fix race condition while disposing the VideoController. ## 0.10.1+4 -- Fixed syntax error in README.md. +* Fixed syntax error in README.md. ## 0.10.1+3 -- Add missing template type parameter to `invokeMethod` calls. -- Bump minimum Flutter version to 1.5.0. -- Replace invokeMethod with invokeMapMethod wherever necessary. +* Add missing template type parameter to `invokeMethod` calls. +* Bump minimum Flutter version to 1.5.0. +* Replace invokeMethod with invokeMapMethod wherever necessary. ## 0.10.1+2 -- Example: Fixed tab display and added scroll view +* Example: Fixed tab display and added scroll view ## 0.10.1+1 -- iOS: Avoid deprecated `seekToTime` API +* iOS: Avoid deprecated `seekToTime` API ## 0.10.1 -- iOS: Consider a player only `initialized` once duration is determined. +* iOS: Consider a player only `initialized` once duration is determined. ## 0.10.0+8 -- iOS: Fix an issue where the player sends initialization message incorrectly. +* iOS: Fix an issue where the player sends initialization message incorrectly. + +* Fix a few other IDE warnings. -- Fix a few other IDE warnings. ## 0.10.0+7 -- Android: Fix issue where buffering status in percentage instead of milliseconds +* Android: Fix issue where buffering status in percentage instead of milliseconds -- Android: Update buffering status everytime we notify for position change +* Android: Update buffering status everytime we notify for position change ## 0.10.0+6 -- Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. +* Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. ## 0.10.0+5 -- Fixed iOS build warnings about implicit retains. +* Fixed iOS build warnings about implicit retains. ## 0.10.0+4 -- Android: Upgrade ExoPlayer to 2.9.6. +* Android: Upgrade ExoPlayer to 2.9.6. ## 0.10.0+3 -- Fix divide by zero bug on iOS. +* Fix divide by zero bug on iOS. ## 0.10.0+2 -- Added supported format documentation in README. +* Added supported format documentation in README. ## 0.10.0+1 -- Log a more detailed warning at build time about the previous AndroidX +* Log a more detailed warning at build time about the previous AndroidX migration. ## 0.10.0 -- **Breaking change**. Migrate from the deprecated original Android Support +* **Breaking change**. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to [also migrate](https://developer.android.com/jetpack/androidx/migrate) if they're @@ -218,153 +220,153 @@ ## 0.9.0 -- Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded - in portrait mode both in iOS and Android. +* Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded + in portrait mode both in iOS and Android. ## 0.8.0 -- Android: Upgrade ExoPlayer to 2.9.1 -- Android: Use current gradle dependencies -- Android 9 compatibility fixes for Demo App +* Android: Upgrade ExoPlayer to 2.9.1 +* Android: Use current gradle dependencies +* Android 9 compatibility fixes for Demo App ## 0.7.2 -- Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. +* Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. ## 0.7.1 -- Fixed null exception on Android when the video has a width or height of 0. +* Fixed null exception on Android when the video has a width or height of 0. ## 0.7.0 -- Add a unit test for controller and texture changes. This is a breaking change since the interface +* Add a unit test for controller and texture changes. This is a breaking change since the interface had to be cleaned up to facilitate faking. ## 0.6.6 -- Fix the condition where the player doesn't update when attached controller is changed. +* Fix the condition where the player doesn't update when attached controller is changed. ## 0.6.5 -- Eliminate race conditions around initialization: now initialization events are queued and guaranteed +* Eliminate race conditions around initialization: now initialization events are queued and guaranteed to be delivered to the Dart side. VideoPlayer widget is rebuilt upon completion of initialization. ## 0.6.4 -- Android: add support for hls, dash and ss video formats. +* Android: add support for hls, dash and ss video formats. ## 0.6.3 -- iOS: Allow audio playback in silent mode. +* iOS: Allow audio playback in silent mode. ## 0.6.2 -- `VideoPlayerController.seekTo()` is now frame accurate on both platforms. +* `VideoPlayerController.seekTo()` is now frame accurate on both platforms. ## 0.6.1 -- iOS: add missing observer removals to prevent crashes on deallocation. +* iOS: add missing observer removals to prevent crashes on deallocation. ## 0.6.0 -- Android: use ExoPlayer instead of MediaPlayer for better video format support. +* Android: use ExoPlayer instead of MediaPlayer for better video format support. ## 0.5.5 -- **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. -- Updated example in README.md. +* **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. +* Updated example in README.md. ## 0.5.4 -- Updated Gradle tooling to match Android Studio 3.1.2. +* Updated Gradle tooling to match Android Studio 3.1.2. ## 0.5.3 -- Added video buffering status. +* Added video buffering status. ## 0.5.2 -- Fixed a bug on iOS that could lead to missing initialization. -- Added support for HLS video on iOS. +* Fixed a bug on iOS that could lead to missing initialization. +* Added support for HLS video on iOS. ## 0.5.1 -- Fixed bug on video loop feature for iOS. +* Fixed bug on video loop feature for iOS. ## 0.5.0 -- Added the constructor `VideoPlayerController.file`. -- **Breaking change**. Changed `VideoPlayerController.isNetwork` to +* Added the constructor `VideoPlayerController.file`. +* **Breaking change**. Changed `VideoPlayerController.isNetwork` to an enum `VideoPlayerController.dataSourceType`. ## 0.4.1 -- Updated Flutter SDK constraint to reflect the changes in v0.4.0. +* Updated Flutter SDK constraint to reflect the changes in v0.4.0. ## 0.4.0 -- **Breaking change**. Removed the `VideoPlayerController` constructor -- Added two new factory constructors `VideoPlayerController.asset` and +* **Breaking change**. Removed the `VideoPlayerController` constructor +* Added two new factory constructors `VideoPlayerController.asset` and `VideoPlayerController.network` to respectively play a video from the Flutter assets and from a network uri. ## 0.3.0 -- **Breaking change**. Set SDK constraints to match the Flutter beta release. +* **Breaking change**. Set SDK constraints to match the Flutter beta release. ## 0.2.1 -- Fixed some signatures to account for strong mode runtime errors. -- Fixed spelling mistake in toString output. +* Fixed some signatures to account for strong mode runtime errors. +* Fixed spelling mistake in toString output. ## 0.2.0 -- **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. -- Updated documentation of when fields are available on `VideoPlayerController`. -- Updated links in README.md. +* **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. +* Updated documentation of when fields are available on `VideoPlayerController`. +* Updated links in README.md. ## 0.1.1 -- Simplified and upgraded Android project template to Android SDK 27. -- Moved Android package to io.flutter.plugins. -- Fixed warnings from the Dart 2.0 analyzer. +* Simplified and upgraded Android project template to Android SDK 27. +* Moved Android package to io.flutter.plugins. +* Fixed warnings from the Dart 2.0 analyzer. ## 0.1.0 -- **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin +* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in order to use this version of the plugin. Instructions can be found [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). ## 0.0.7 -- Added access to the video size. -- Made the VideoProgressIndicator render using a LinearProgressIndicator. +* Added access to the video size. +* Made the VideoProgressIndicator render using a LinearProgressIndicator. ## 0.0.6 -- Fixed a bug related to hot restart on Android. +* Fixed a bug related to hot restart on Android. ## 0.0.5 -- Added VideoPlayerValue.toString(). -- Added FLT prefix to iOS types. +* Added VideoPlayerValue.toString(). +* Added FLT prefix to iOS types. ## 0.0.4 -- The player will now pause on app pause, and resume on app resume. -- Implemented scrubbing on the progress bar. +* The player will now pause on app pause, and resume on app resume. +* Implemented scrubbing on the progress bar. ## 0.0.3 -- Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). -- Added VideoPlayerController.setVolume(). -- Moved the package to flutter/plugins github repo. +* Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). +* Added VideoPlayerController.setVolume(). +* Moved the package to flutter/plugins github repo. ## 0.0.2 -- Fix meta dependency version. +* Fix meta dependency version. ## 0.0.1 -- Initial release +* Initial release From 90342636880de88f415cfe15103117a4a8e6e0d7 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:37:25 -0300 Subject: [PATCH 12/69] Todo Styles commented --- packages/video_player/video_player/lib/src/web_vtt.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index cf1db8f84fed..5f86311b4406 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -133,7 +133,6 @@ Duration _parseWebVttTimestamp(String timestampString) { int hours = 0; int minutes = 0; int seconds = 0; - List styles; if (hoursMinutesSeconds.length > 2) { // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] @@ -157,9 +156,9 @@ Duration _parseWebVttTimestamp(String timestampString) { /// Some captions comes with styles about where/how the caption should be rendered. /// E.g: /// 00:32.500 --> 00:33.500 align:left size:50% - if (milisecondsStyles.length > 1) { - styles = milisecondsStyles.sublist(1); - } + /// if (milisecondsStyles.length > 1) { + /// List styles = milisecondsStyles.sublist(1); + /// } int milliseconds = int.parse(milisecondsStyles[0]); return Duration( From d9647a991f9ad257cd4d9693dd1b67622a57905e Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 21:31:07 -0300 Subject: [PATCH 13/69] Sample VTT file fixed --- .../example/assets/bumble_bee_captions.vtt | 4 ++-- .../example/ios/Flutter/Flutter.podspec | 18 ++++++++++++++++++ .../ios/Runner.xcodeproj/project.pbxproj | 17 ++++------------- .../video_player/example/lib/main.dart | 4 ++-- 4 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt index f077a18f289b..a4f115c72140 100644 --- a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt +++ b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt @@ -1,7 +1,7 @@ WEBVTT -00:00:00,200 --> 00:00:01,750 +00:00:00,200 --> 00:00:01.750 [ Birds chirping ] -00:00:02,300 --> 00:00:05,000 +00:00:02,300 --> 00:00:05.000 [ Buzzing ] diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec new file mode 100644 index 000000000000..5ca30416bac0 --- /dev/null +++ b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.description = <<-DESC +Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. + DESC + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + s.vendored_frameworks = 'Flutter.framework' +end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 9f0a7ef189b9..8e17be05e07b 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,13 +35,11 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -63,8 +55,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -92,9 +82,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -229,7 +217,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -237,9 +225,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 8d9358a4ec4e..ca697cefebd4 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -210,8 +210,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { Future _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context) - .loadString('assets/bumble_bee_captions.srt'); - return SubRipCaptionFile( + .loadString('assets/bumble_bee_captions.vtt'); + return WebVttCaptionFile( fileContents); // For vtt files, use WebVttCaptionFile } From 2ebf99f59e80c143959c48ef3e49c9500786b74b Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 21:31:46 -0300 Subject: [PATCH 14/69] Sample VTT file fixed --- .../video_player/example/assets/bumble_bee_captions.vtt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt index a4f115c72140..1dca2c58695e 100644 --- a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt +++ b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt @@ -1,7 +1,7 @@ WEBVTT -00:00:00,200 --> 00:00:01.750 +00:00:00.200 --> 00:00:01.750 [ Birds chirping ] -00:00:02,300 --> 00:00:05.000 +00:00:02.300 --> 00:00:05.000 [ Buzzing ] From 43abfd640300c92fc02e487d7931f3a58d0927c6 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 21:35:55 -0300 Subject: [PATCH 15/69] Sample VTT file fixed --- .../example/ios/Flutter/Flutter.podspec | 18 ------------------ .../ios/Runner.xcodeproj/project.pbxproj | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec deleted file mode 100644 index 5ca30416bac0..000000000000 --- a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.description = <<-DESC -Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. - DESC - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' - s.vendored_frameworks = 'Flutter.framework' -end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 8e17be05e07b..9f0a7ef189b9 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -24,6 +28,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -35,11 +41,13 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -55,6 +63,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -82,7 +92,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -217,7 +229,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -225,12 +237,9 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; From a44edeaccc667896d80c55825c68dc4b81b13e11 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 16 Jul 2020 10:30:08 -0300 Subject: [PATCH 16/69] Credentials added --- packages/video_player/video_player/lib/src/web_vtt.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 5f86311b4406..57c27c9b2bcc 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -41,7 +41,6 @@ List _parseCaptionsFromWebVttString(String file) { /// [00:00.000 --> 01:24.000 align:center] /// ['Introduction'] /// ] - /// if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; From d92e820e59679edb221165a9f40334780e396791 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 16 Jul 2020 10:39:57 -0300 Subject: [PATCH 17/69] Credentials added --- packages/video_player/video_player/lib/src/web_vtt.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 5f86311b4406..57c27c9b2bcc 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -41,7 +41,6 @@ List _parseCaptionsFromWebVttString(String file) { /// [00:00.000 --> 01:24.000 align:center] /// ['Introduction'] /// ] - /// if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; From 98e21c1eca2605869069cfb3699ac505ecd380dd Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 4 Jun 2020 18:09:20 -0700 Subject: [PATCH 18/69] [image_picker] Add web support to the example app. (#2816) --- .../image_picker/image_picker/CHANGELOG.md | 4 +++ .../image_picker/example/lib/main.dart | 16 +++++++-- .../image_picker/example/pubspec.yaml | 1 + .../image_picker/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../image_picker/example/web/index.html | 33 ++++++++++++++++++ .../image_picker/example/web/manifest.json | 23 ++++++++++++ .../image_picker/image_picker/pubspec.yaml | 2 +- 9 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 packages/image_picker/image_picker/example/web/favicon.png create mode 100644 packages/image_picker/image_picker/example/web/icons/Icon-192.png create mode 100644 packages/image_picker/image_picker/example/web/icons/Icon-512.png create mode 100644 packages/image_picker/image_picker/example/web/index.html create mode 100644 packages/image_picker/image_picker/example/web/manifest.json diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index a72e3bcbc221..e3856c266464 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.7+1 + +* Add web support to the example app. + ## 0.6.7 * Utilize the new platform_interface package. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index 231dcb25931b..dff2906edf87 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -52,7 +52,11 @@ class _MyHomePageState extends State { Future _playVideo(PickedFile file) async { if (file != null && mounted) { await _disposeVideoController(); - _controller = VideoPlayerController.file(File(file.path)); + if (kIsWeb) { + _controller = VideoPlayerController.network(file.path); + } else { + _controller = VideoPlayerController.file(File(file.path)); + } await _controller.setVolume(1.0); await _controller.initialize(); await _controller.setLooping(true); @@ -139,7 +143,13 @@ class _MyHomePageState extends State { return retrieveError; } if (_imageFile != null) { - return Image.file(File(_imageFile.path)); + if (kIsWeb) { + // Why network? + // See https://pub.dev/packages/image_picker#getting-ready-for-the-web-platform + return Image.network(_imageFile.path); + } else { + return Image.file(File(_imageFile.path)); + } } else if (_pickImageError != null) { return Text( 'Pick image error: $_pickImageError', @@ -180,7 +190,7 @@ class _MyHomePageState extends State { title: Text(widget.title), ), body: Center( - child: defaultTargetPlatform == TargetPlatform.android + child: !kIsWeb && defaultTargetPlatform == TargetPlatform.android ? FutureBuilder( future: retrieveLostData(), builder: (BuildContext context, AsyncSnapshot snapshot) { diff --git a/packages/image_picker/image_picker/example/pubspec.yaml b/packages/image_picker/image_picker/example/pubspec.yaml index f3171aa7ccf8..93df8dfdc010 100755 --- a/packages/image_picker/image_picker/example/pubspec.yaml +++ b/packages/image_picker/image_picker/example/pubspec.yaml @@ -9,6 +9,7 @@ dependencies: flutter_plugin_android_lifecycle: ^1.0.2 image_picker: path: ../ + image_picker_for_web: ^0.1.0 dev_dependencies: flutter_driver: diff --git a/packages/image_picker/image_picker/example/web/favicon.png b/packages/image_picker/image_picker/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker/example/web/icons/Icon-192.png b/packages/image_picker/image_picker/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker/example/web/icons/Icon-512.png b/packages/image_picker/image_picker/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/image_picker/image_picker/example/web/index.html b/packages/image_picker/image_picker/example/web/index.html new file mode 100644 index 000000000000..787bbc72f6b1 --- /dev/null +++ b/packages/image_picker/image_picker/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + url_launcher web example + + + + + + + + diff --git a/packages/image_picker/image_picker/example/web/manifest.json b/packages/image_picker/image_picker/example/web/manifest.json new file mode 100644 index 000000000000..7d9c25627ebd --- /dev/null +++ b/packages/image_picker/image_picker/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "image_picker example", + "short_name": "image_picker", + "start_url": ".", + "display": "minimal-ui", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "An example of the image_picker on the web.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 51069b6e6690..9086028952ce 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker -version: 0.6.7 +version: 0.6.7+1 flutter: plugin: From e146366b3391a4f21c399d08966ad4c70fa7411f Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Sun, 7 Jun 2020 01:17:49 +0200 Subject: [PATCH 19/69] Fix bug in example (#2801) By ensuring that flutter widget bindings are initialized. The current version fails on startup. --- packages/camera/CHANGELOG.md | 4 ++++ packages/camera/README.md | 1 + packages/camera/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/camera/CHANGELOG.md b/packages/camera/CHANGELOG.md index d59cb71758ca..43e5f463604c 100644 --- a/packages/camera/CHANGELOG.md +++ b/packages/camera/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.8+3 + +* Fix bug in usage example in README.md + ## 0.5.8+2 * Post-v2 embedding cleanups. diff --git a/packages/camera/README.md b/packages/camera/README.md index 4c23236cd0aa..fbd73929e6de 100644 --- a/packages/camera/README.md +++ b/packages/camera/README.md @@ -53,6 +53,7 @@ import 'package:camera/camera.dart'; List cameras; Future main() async { + WidgetsFlutterBinding.ensureInitialized(); cameras = await availableCameras(); runApp(CameraApp()); } diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml index 6cb514765e2d..a1ce42370191 100644 --- a/packages/camera/pubspec.yaml +++ b/packages/camera/pubspec.yaml @@ -2,7 +2,7 @@ name: camera description: A Flutter plugin for getting information about and controlling the camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video, and streaming image buffers to dart. -version: 0.5.8+2 +version: 0.5.8+3 homepage: https://github.com/flutter/plugins/tree/master/packages/camera From c05a6f11c9468770b5d591fcc7550eaada28cb82 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Fri, 12 Jun 2020 20:50:56 -0700 Subject: [PATCH 20/69] Update Linux desktop Dockerfile for GTK switch (#2826) Once the switch to GTK lands, libgtk-3-dev will be needed for building. --- .ci/Dockerfile-LinuxDesktop | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/Dockerfile-LinuxDesktop b/.ci/Dockerfile-LinuxDesktop index 75308c4aa400..25924d4ea8d9 100644 --- a/.ci/Dockerfile-LinuxDesktop +++ b/.ci/Dockerfile-LinuxDesktop @@ -19,3 +19,5 @@ RUN sudo apt-get update && sudo apt-get install -y google-cloud-sdk && \ RUN sudo apt-get install -y xvfb libegl1-mesa # Install Linux desktop build tool requirements. RUN sudo apt-get install -y clang cmake ninja-build file pkg-config +# Install necessary libraries. +RUN sudo apt-get install -y libgtk-3-dev From 50044cc8e8e501eb9432b84819ff68acb929859e Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Mon, 15 Jun 2020 22:48:31 +0700 Subject: [PATCH 21/69] [image_picker] fixes for iOS which doesn't present camera/albums with more complex navigation (#2755) --- AUTHORS | 3 +- .../image_picker/image_picker/CHANGELOG.md | 4 ++ .../ios/Classes/FLTImagePickerPlugin.h | 2 +- .../ios/Classes/FLTImagePickerPlugin.m | 40 ++++++++++------ .../ios/Tests/ImagePickerPluginTests.m | 46 +++++++++++++------ .../image_picker/image_picker/pubspec.yaml | 2 +- 6 files changed, 65 insertions(+), 32 deletions(-) diff --git a/AUTHORS b/AUTHORS index b27c156188f8..17ede94e79ba 100644 --- a/AUTHORS +++ b/AUTHORS @@ -56,4 +56,5 @@ Giancarlo Rocha Ryo Miyake Théo Champion Kazuki Yamaguchi -Eitan Schwartz \ No newline at end of file +Eitan Schwartz +Chris Rutkowski diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index e3856c266464..6b85f26daf33 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.7+2 + +* iOS: Fixes unpresentable album/image picker if window's root view controller is already presenting other view controller. + ## 0.6.7+1 * Add web support to the example app. diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h index 38e5b56600f3..b6d8687a32e3 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.h @@ -7,7 +7,7 @@ @interface FLTImagePickerPlugin : NSObject // For testing only. -- (instancetype)initWithViewController:(UIViewController *)viewController; - (UIImagePickerController *)getImagePickerController; +- (UIViewController *)viewControllerWithWindow:(UIWindow *)window; @end diff --git a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m index 449157565e7f..00fdec245aaf 100644 --- a/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m +++ b/packages/image_picker/image_picker/ios/Classes/FLTImagePickerPlugin.m @@ -25,7 +25,6 @@ @interface FLTImagePickerPlugin () *)registrar { FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/image_picker" binaryMessenger:[registrar messenger]]; - UIViewController *viewController = - [UIApplication sharedApplication].delegate.window.rootViewController; - FLTImagePickerPlugin *instance = - [[FLTImagePickerPlugin alloc] initWithViewController:viewController]; + FLTImagePickerPlugin *instance = [FLTImagePickerPlugin new]; [registrar addMethodCallDelegate:instance channel:channel]; } -- (instancetype)initWithViewController:(UIViewController *)viewController { - self = [super init]; - if (self) { - _viewController = viewController; - } - return self; -} - - (UIImagePickerController *)getImagePickerController { return _imagePickerController; } +- (UIViewController *)viewControllerWithWindow:(UIWindow *)window { + UIWindow *windowToUse = window; + if (windowToUse == nil) { + for (UIWindow *window in [UIApplication sharedApplication].windows) { + if (window.isKeyWindow) { + windowToUse = window; + break; + } + } + } + + UIViewController *topController = windowToUse.rootViewController; + while (topController.presentedViewController) { + topController = topController.presentedViewController; + } + return topController; +} + - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { if (self.result) { self.result([FlutterError errorWithCode:@"multiple_request" @@ -136,7 +142,9 @@ - (void)showCamera { [UIImagePickerController isCameraDeviceAvailable:_device]) { _imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera; _imagePickerController.cameraDevice = _device; - [_viewController presentViewController:_imagePickerController animated:YES completion:nil]; + [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController + animated:YES + completion:nil]; } else { [[[UIAlertView alloc] initWithTitle:@"Error" message:@"Camera not available." @@ -241,7 +249,9 @@ - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status { - (void)showPhotoLibrary { // No need to check if SourceType is available. It always is. _imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary; - [_viewController presentViewController:_imagePickerController animated:YES completion:nil]; + [[self viewControllerWithWindow:nil] presentViewController:_imagePickerController + animated:YES + completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker diff --git a/packages/image_picker/image_picker/ios/Tests/ImagePickerPluginTests.m b/packages/image_picker/image_picker/ios/Tests/ImagePickerPluginTests.m index e5c681e7d956..c8d5a2bb5368 100644 --- a/packages/image_picker/image_picker/ios/Tests/ImagePickerPluginTests.m +++ b/packages/image_picker/image_picker/ios/Tests/ImagePickerPluginTests.m @@ -7,6 +7,19 @@ @import image_picker; @import XCTest; +@interface MockViewController : UIViewController +@property(nonatomic, retain) UIViewController *mockPresented; +@end + +@implementation MockViewController +@synthesize mockPresented; + +- (UIViewController *)presentedViewController { + return mockPresented; +} + +@end + @interface FLTImagePickerPlugin (Test) @property(copy, nonatomic) FlutterResult result; - (void)handleSavedPath:(NSString *)path; @@ -23,8 +36,7 @@ - (void)testPluginPickImageDeviceBack { if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; @@ -39,8 +51,7 @@ - (void)testPluginPickImageDeviceFront { if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; @@ -55,8 +66,7 @@ - (void)testPluginPickVideoDeviceBack { if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; @@ -71,8 +81,7 @@ - (void)testPluginPickImageDeviceCancelClickMultipleTimes { if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; @@ -90,8 +99,7 @@ - (void)testPluginPickVideoDeviceFront { if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { return; } - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(1)}]; @@ -104,8 +112,7 @@ - (void)testPluginPickVideoDeviceFront { #pragma mark - Test video duration - (void)testPickingVideoWithDuration { - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickVideo" arguments:@{@"source" : @(0), @"cameraDevice" : @(0), @"maxDuration" : @95}]; @@ -116,8 +123,7 @@ - (void)testPickingVideoWithDuration { } - (void)testPluginPickImageSelectMultipleTimes { - FLTImagePickerPlugin *plugin = - [[FLTImagePickerPlugin alloc] initWithViewController:[UIViewController new]]; + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; FlutterMethodCall *call = [FlutterMethodCall methodCallWithMethodName:@"pickImage" arguments:@{@"source" : @(0), @"cameraDevice" : @(0)}]; @@ -131,4 +137,16 @@ - (void)testPluginPickImageSelectMultipleTimes { [plugin handleSavedPath:@"test"]; } +- (void)testViewController { + UIWindow *window = [UIWindow new]; + MockViewController *vc1 = [MockViewController new]; + window.rootViewController = vc1; + + UIViewController *vc2 = [UIViewController new]; + vc1.mockPresented = vc2; + + FLTImagePickerPlugin *plugin = [FLTImagePickerPlugin new]; + XCTAssertEqual([plugin viewControllerWithWindow:window], vc2); +} + @end diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 9086028952ce..4f52b19c2659 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker -version: 0.6.7+1 +version: 0.6.7+2 flutter: plugin: From 2aacc0ff34cf0a0f89497a3f935c9a1982fd7e25 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 16 Jun 2020 10:23:36 -0600 Subject: [PATCH 22/69] [path_provider] Updated documentation reflecting changes needed for testing (#2815) The recent federation of the Linux path provider might break tests, this updates the README.md to address those changes and direct the users how to mock the plugin in their tests. --- .../path_provider/path_provider/CHANGELOG.md | 3 +++ packages/path_provider/path_provider/README.md | 17 ++++++++++++++++- .../path_provider/path_provider/pubspec.yaml | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/path_provider/path_provider/CHANGELOG.md b/packages/path_provider/path_provider/CHANGELOG.md index a2df04613965..226d9ae01d0c 100644 --- a/packages/path_provider/path_provider/CHANGELOG.md +++ b/packages/path_provider/path_provider/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.6.11 +* Updated documentation to reflect the need for changes in testing for federated plugins + ## 1.6.10 * Linux implementation endorsement diff --git a/packages/path_provider/path_provider/README.md b/packages/path_provider/path_provider/README.md index 944137f4631c..982a9f49cae5 100644 --- a/packages/path_provider/path_provider/README.md +++ b/packages/path_provider/path_provider/README.md @@ -2,7 +2,8 @@ [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dartlang.org/packages/path_provider) -A Flutter plugin for finding commonly used locations on the filesystem. Supports iOS and Android. +A Flutter plugin for finding commonly used locations on the filesystem. Supports iOS, Android, Linux and MacOS. +Not all methods are supported on all platforms. ## Usage @@ -19,3 +20,17 @@ String appDocPath = appDocDir.path; ``` Please see the example app of this plugin for a full example. + +### Usage in tests + +`path_provider` now uses a `PlatformInterface`, meaning that not all platforms share the a single `PlatformChannel`-based implementation. +With that change, tests should be updated to mock `PathProviderPlatform` rather than `PlatformChannel`. + +See this `path_provider` [test](https://github.com/flutter/plugins/blob/master/packages/path_provider/path_provider/test/path_provider_test.dart) for an example. + +You will also have to temporarily add the following line to the setup of your test. +```dart +disablePathProviderPlatformOverride = true; +``` + +See this [issue](https://github.com/flutter/flutter/issues/52267), for more details on why this is needed. \ No newline at end of file diff --git a/packages/path_provider/path_provider/pubspec.yaml b/packages/path_provider/path_provider/pubspec.yaml index 98059e148140..d83911ddc2a8 100644 --- a/packages/path_provider/path_provider/pubspec.yaml +++ b/packages/path_provider/path_provider/pubspec.yaml @@ -2,7 +2,7 @@ name: path_provider description: Flutter plugin for getting commonly used locations on the Android & iOS file systems, such as the temp and app data directories. homepage: https://github.com/flutter/plugins/tree/master/packages/path_provider/path_provider -version: 1.6.10 +version: 1.6.11 flutter: plugin: From 928bfb4d558efd77c3c52dfb5c485eabda41860e Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 17 Jun 2020 13:25:56 -0700 Subject: [PATCH 23/69] [url_launcher] docs: note about encoding URIs (#2172) Add a note about encoding URLs passed to the url_launcher in the packages README. --- .../url_launcher/url_launcher/CHANGELOG.md | 6 +++++- packages/url_launcher/url_launcher/README.md | 21 +++++++++++++++++++ .../url_launcher/url_launcher/pubspec.yaml | 2 +- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 2855eade4d21..6b1327a2da52 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.4.11 + +* Add documentation in README suggesting how to properly encode urls with special characters. + ## 5.4.10 * Post-v2 Android embedding cleanups. @@ -68,7 +72,7 @@ ## 5.2.3 -Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. +* Android: Use android.arch.lifecycle instead of androidx.lifecycle:lifecycle in `build.gradle` to support apps that has not been migrated to AndroidX. ## 5.2.2 diff --git a/packages/url_launcher/url_launcher/README.md b/packages/url_launcher/url_launcher/README.md index 2dcc6359c1af..53c7fe96cde7 100644 --- a/packages/url_launcher/url_launcher/README.md +++ b/packages/url_launcher/url_launcher/README.md @@ -53,6 +53,27 @@ Common schemes supported by both iOS and Android: More details can be found here for [iOS](https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/Introduction/Introduction.html) and [Android](https://developer.android.com/guide/components/intents-common.html) +### Encoding URLs + +URLs must be properly encoded, especially when including spaces or other special characters. This can be done using the [`Uri` class](https://api.dart.dev/stable/2.7.1/dart-core/Uri-class.html): +```dart +import 'dart:core'; +import 'package:url_launcher/url_launcher.dart'; + +final Uri _emailLaunchUri = Uri( + scheme: 'mailto', + path: 'smith@example.com', + queryParameters: { + 'subject': 'Example Subject & Symbols are allowed!' + } +); + +// ... + +// mailto:smith@example.com?subject=Example+Subject+%26+Symbols+are+allowed%21 +launch(_emailLaunchUri.toString()); +``` + ## Handling missing URL receivers A particular mobile device may not be able to receive all supported URL schemes. diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 156d608583a5..08ac3f9f8b80 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher description: Flutter plugin for launching a URL on Android and iOS. Supports web, phone, SMS, and email schemes. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher -version: 5.4.10 +version: 5.4.11 flutter: plugin: From ee9e13acedcdd842a3552527b901df57bda52d78 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Mon, 22 Jun 2020 10:50:51 -0700 Subject: [PATCH 24/69] [In_App_Purchase]queryPastPurchases() shouldn't block transaction updates. (#2834) * Allow all transactions except for purchasing ones to be passed back to the Flutter side. * Restore transactions shouldn't block transaction updates --- packages/in_app_purchase/CHANGELOG.md | 5 ++++ .../ios/Classes/FIAPaymentQueueHandler.m | 6 ++--- .../ios/Tests/InAppPurchasePluginTest.m | 1 + .../ios/Tests/PaymentQueueTest.m | 5 ++++ packages/in_app_purchase/ios/Tests/Stubs.m | 12 +++++++-- .../in_app_purchase/app_store_connection.dart | 6 +++-- .../in_app_purchase_connection.dart | 2 +- .../sk_payment_queue_wrapper.dart | 5 ++-- packages/in_app_purchase/pubspec.yaml | 2 +- .../app_store_connection_test.dart | 26 +++++++++++++++++-- .../sk_test_stub_objects.dart | 1 + 11 files changed, 56 insertions(+), 15 deletions(-) diff --git a/packages/in_app_purchase/CHANGELOG.md b/packages/in_app_purchase/CHANGELOG.md index b7b025cbef6f..c159b094fb47 100644 --- a/packages/in_app_purchase/CHANGELOG.md +++ b/packages/in_app_purchase/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.3.4+1 + +* iOS: Fix the bug that `SKPaymentQueueWrapper.transactions` doesn't return all transactions. +* iOS: Fix the app crashes if `InAppPurchaseConnection.instance` is called in the `main()`. + ## 0.3.4 * Expose SKError code to client apps. diff --git a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m index 8bdb7f25f111..57370e16fcbb 100644 --- a/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m +++ b/packages/in_app_purchase/ios/Classes/FIAPaymentQueueHandler.m @@ -75,7 +75,7 @@ - (void)restoreTransactions:(nullable NSString *)applicationName { - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { - if (transaction.transactionIdentifier) { + if (transaction.transactionState != SKPaymentTransactionStatePurchasing) { // Use product identifier instead of transaction identifier for few reasons: // 1. Only transactions with purchased state and failed state will have a transaction id, it // will become impossible for clients to finish deferred transactions when needed. @@ -92,9 +92,7 @@ - (void)paymentQueue:(SKPaymentQueue *)queue - (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { - if (transaction.transactionIdentifier) { - [self.transactionsSetter removeObjectForKey:transaction.payment.productIdentifier]; - } + [self.transactionsSetter removeObjectForKey:transaction.payment.productIdentifier]; } self.transactionsRemoved(transactions); } diff --git a/packages/in_app_purchase/ios/Tests/InAppPurchasePluginTest.m b/packages/in_app_purchase/ios/Tests/InAppPurchasePluginTest.m index e6a18e0acf58..20543a203a97 100644 --- a/packages/in_app_purchase/ios/Tests/InAppPurchasePluginTest.m +++ b/packages/in_app_purchase/ios/Tests/InAppPurchasePluginTest.m @@ -121,6 +121,7 @@ - (void)testAddPaymentWithSameProductIDWillFail { @"simulatesAskToBuyInSandBox" : @YES, }]; SKPaymentQueueStub* queue = [SKPaymentQueueStub new]; + queue.testState = SKPaymentTransactionStatePurchased; self.plugin.paymentQueueHandler = [[FIAPaymentQueueHandler alloc] initWithQueue:queue transactionsUpdated:^(NSArray* _Nonnull transactions) { } diff --git a/packages/in_app_purchase/ios/Tests/PaymentQueueTest.m b/packages/in_app_purchase/ios/Tests/PaymentQueueTest.m index 2085ba328140..8f5b66496f69 100644 --- a/packages/in_app_purchase/ios/Tests/PaymentQueueTest.m +++ b/packages/in_app_purchase/ios/Tests/PaymentQueueTest.m @@ -66,6 +66,7 @@ - (void)testTransactionPurchased { [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchased); + XCTAssertEqual(tran.transactionIdentifier, @"fakeID"); } - (void)testDuplicateTransactionsWillTriggerAnError { @@ -113,6 +114,7 @@ - (void)testTransactionFailed { [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateFailed); + XCTAssertEqual(tran.transactionIdentifier, nil); } - (void)testTransactionRestored { @@ -140,6 +142,7 @@ - (void)testTransactionRestored { [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateRestored); + XCTAssertEqual(tran.transactionIdentifier, @"fakeID"); } - (void)testTransactionPurchasing { @@ -167,6 +170,7 @@ - (void)testTransactionPurchasing { [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStatePurchasing); + XCTAssertEqual(tran.transactionIdentifier, nil); } - (void)testTransactionDeferred { @@ -194,6 +198,7 @@ - (void)testTransactionDeferred { [handler addPayment:payment]; [self waitForExpectations:@[ expectation ] timeout:5]; XCTAssertEqual(tran.transactionState, SKPaymentTransactionStateDeferred); + XCTAssertEqual(tran.transactionIdentifier, nil); } - (void)testFinishTransaction { diff --git a/packages/in_app_purchase/ios/Tests/Stubs.m b/packages/in_app_purchase/ios/Tests/Stubs.m index 2c3460f17f4b..58b77c14127d 100644 --- a/packages/in_app_purchase/ios/Tests/Stubs.m +++ b/packages/in_app_purchase/ios/Tests/Stubs.m @@ -215,7 +215,11 @@ - (instancetype)initWithMap:(NSDictionary *)map { - (instancetype)initWithState:(SKPaymentTransactionState)state { self = [super init]; if (self) { - [self setValue:@"fakeID" forKey:@"transactionIdentifier"]; + // Only purchased and restored transactions have transactionIdentifier: + // https://developer.apple.com/documentation/storekit/skpaymenttransaction/1411288-transactionidentifier?language=objc + if (state == SKPaymentTransactionStatePurchased || state == SKPaymentTransactionStateRestored) { + [self setValue:@"fakeID" forKey:@"transactionIdentifier"]; + } [self setValue:@(state) forKey:@"transactionState"]; } return self; @@ -224,7 +228,11 @@ - (instancetype)initWithState:(SKPaymentTransactionState)state { - (instancetype)initWithState:(SKPaymentTransactionState)state payment:(SKPayment *)payment { self = [super init]; if (self) { - [self setValue:@"fakeID" forKey:@"transactionIdentifier"]; + // Only purchased and restored transactions have transactionIdentifier: + // https://developer.apple.com/documentation/storekit/skpaymenttransaction/1411288-transactionidentifier?language=objc + if (state == SKPaymentTransactionStatePurchased || state == SKPaymentTransactionStateRestored) { + [self setValue:@"fakeID" forKey:@"transactionIdentifier"]; + } [self setValue:@(state) forKey:@"transactionState"]; _payment = payment; } diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart index 871879dca08e..da6fc7417585 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/app_store_connection.dart @@ -208,12 +208,14 @@ class _TransactionObserver implements SKTransactionObserverWrapper { return wrapper.transactionState == SKPaymentTransactionStateWrapper.restored; }).map((SKPaymentTransactionWrapper wrapper) => wrapper)); - return; } String receiptData = await getReceiptData(); purchaseUpdatedController - .add(transactions.map((SKPaymentTransactionWrapper transaction) { + .add(transactions.where((SKPaymentTransactionWrapper wrapper) { + return wrapper.transactionState != + SKPaymentTransactionStateWrapper.restored; + }).map((SKPaymentTransactionWrapper transaction) { PurchaseDetails purchaseDetails = PurchaseDetails.fromSKTransaction(transaction, receiptData); return purchaseDetails; diff --git a/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart b/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart index 2079f69dce6c..ba3932f73878 100644 --- a/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart +++ b/packages/in_app_purchase/lib/src/in_app_purchase/in_app_purchase_connection.dart @@ -71,7 +71,7 @@ abstract class InAppPurchaseConnection { /// Enable the [InAppPurchaseConnection] to handle pending purchases. /// - /// Android Only: This method is required to be called when initialize the application. + /// This method is required to be called when initialize the application. /// It is to acknowledge your application has been updated to support pending purchases. /// See [Support pending transactions](https://developer.android.com/google/play/billing/billing_library_overview#pending) /// for more details. diff --git a/packages/in_app_purchase/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart b/packages/in_app_purchase/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart index 49c438e40231..33d9281d3ce0 100644 --- a/packages/in_app_purchase/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart +++ b/packages/in_app_purchase/lib/src/store_kit_wrappers/sk_payment_queue_wrapper.dart @@ -37,9 +37,7 @@ class SKPaymentQueueWrapper { static final SKPaymentQueueWrapper _singleton = SKPaymentQueueWrapper._(); - SKPaymentQueueWrapper._() { - callbackChannel.setMethodCallHandler(_handleObserverCallbacks); - } + SKPaymentQueueWrapper._(); /// Calls [`-[SKPaymentQueue transactions]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506026-transactions?language=objc) Future> transactions() async { @@ -59,6 +57,7 @@ class SKPaymentQueueWrapper { /// addTransactionObserver:]`](https://developer.apple.com/documentation/storekit/skpaymentqueue/1506042-addtransactionobserver?language=objc). void setTransactionObserver(SKTransactionObserverWrapper observer) { _observer = observer; + callbackChannel.setMethodCallHandler(_handleObserverCallbacks); } /// Posts a payment to the queue. diff --git a/packages/in_app_purchase/pubspec.yaml b/packages/in_app_purchase/pubspec.yaml index 12c7b45f7ddf..58f8e1174618 100644 --- a/packages/in_app_purchase/pubspec.yaml +++ b/packages/in_app_purchase/pubspec.yaml @@ -1,7 +1,7 @@ name: in_app_purchase description: A Flutter plugin for in-app purchases. Exposes APIs for making in-app purchases through the App Store and Google Play. homepage: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase -version: 0.3.4 +version: 0.3.4+1 dependencies: async: ^2.0.8 diff --git a/packages/in_app_purchase/test/in_app_purchase_connection/app_store_connection_test.dart b/packages/in_app_purchase/test/in_app_purchase_connection/app_store_connection_test.dart index cb2e0e7cad56..881e1fcc75b7 100644 --- a/packages/in_app_purchase/test/in_app_purchase_connection/app_store_connection_test.dart +++ b/packages/in_app_purchase/test/in_app_purchase_connection/app_store_connection_test.dart @@ -90,6 +90,28 @@ void main() { expect(response.error, isNull); }); + test('queryPastPurchases should not block transaction updates', () async { + fakeIOSPlatform.transactions + .add(fakeIOSPlatform.createPurchasedTransactionWithProductID('foo')); + Completer completer = Completer(); + Stream> stream = + AppStoreConnection.instance.purchaseUpdatedStream; + + StreamSubscription subscription; + subscription = stream.listen((purchaseDetailsList) { + if (purchaseDetailsList.first.status == PurchaseStatus.purchased) { + completer.complete(purchaseDetailsList); + subscription.cancel(); + } + }); + QueryPurchaseDetailsResponse response = + await AppStoreConnection.instance.queryPastPurchases(); + List result = await completer.future; + expect(result.length, 1); + expect(result.first.productID, 'foo'); + expect(response.error, isNull); + }); + test('should get empty result if there is no restored transactions', () async { fakeIOSPlatform.testRestoredTransactionsNull = true; @@ -328,10 +350,10 @@ class FakeIOSPlatform { SKPaymentTransactionWrapper createPendingTransactionWithProductID(String id) { return SKPaymentTransactionWrapper( + transactionIdentifier: null, payment: SKPaymentWrapper(productIdentifier: id), transactionState: SKPaymentTransactionStateWrapper.purchasing, transactionTimeStamp: 123123.121, - transactionIdentifier: id, error: null, originalTransaction: null); } @@ -349,10 +371,10 @@ class FakeIOSPlatform { SKPaymentTransactionWrapper createFailedTransactionWithProductID(String id) { return SKPaymentTransactionWrapper( + transactionIdentifier: null, payment: SKPaymentWrapper(productIdentifier: id), transactionState: SKPaymentTransactionStateWrapper.failed, transactionTimeStamp: 123123.121, - transactionIdentifier: id, error: SKError( code: 0, domain: 'ios_domain', diff --git a/packages/in_app_purchase/test/store_kit_wrappers/sk_test_stub_objects.dart b/packages/in_app_purchase/test/store_kit_wrappers/sk_test_stub_objects.dart index 1dc70748f1db..c976e80a90a5 100644 --- a/packages/in_app_purchase/test/store_kit_wrappers/sk_test_stub_objects.dart +++ b/packages/in_app_purchase/test/store_kit_wrappers/sk_test_stub_objects.dart @@ -22,6 +22,7 @@ final SKPaymentTransactionWrapper dummyOriginalTransaction = transactionIdentifier: '123123', error: dummyError, ); + final SKPaymentTransactionWrapper dummyTransaction = SKPaymentTransactionWrapper( transactionState: SKPaymentTransactionStateWrapper.purchased, From bf3e958fa8281fd52952f775ef2bd11259a02a36 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 23 Jun 2020 14:03:15 -0400 Subject: [PATCH 25/69] [e2e] Fix e2e pixel ratio (#2842) * Fix e2e device pixel ratio * Update changelog and version * Formatting --- packages/e2e/CHANGELOG.md | 4 ++++ packages/e2e/lib/e2e.dart | 4 ++-- packages/e2e/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/e2e/CHANGELOG.md b/packages/e2e/CHANGELOG.md index 725b648dc5a5..0d349965371b 100644 --- a/packages/e2e/CHANGELOG.md +++ b/packages/e2e/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.0+1 + +* Fixed the device pixel ratio problem. + ## 0.5.0 * **Breaking change** by default, tests will use the device window size. diff --git a/packages/e2e/lib/e2e.dart b/packages/e2e/lib/e2e.dart index c2ddb2d6b801..ec1615e904b4 100644 --- a/packages/e2e/lib/e2e.dart +++ b/packages/e2e/lib/e2e.dart @@ -52,8 +52,8 @@ class E2EWidgetsFlutterBinding extends LiveTestWidgetsFlutterBinding { bool get registerTestTextInput => false; @override - ViewConfiguration createViewConfiguration() => - TestViewConfiguration(size: window.physicalSize); + ViewConfiguration createViewConfiguration() => TestViewConfiguration( + size: window.physicalSize / window.devicePixelRatio); final Completer _allTestsPassed = Completer(); diff --git a/packages/e2e/pubspec.yaml b/packages/e2e/pubspec.yaml index 82de42bd5c66..0d4bc99f64f3 100644 --- a/packages/e2e/pubspec.yaml +++ b/packages/e2e/pubspec.yaml @@ -1,6 +1,6 @@ name: e2e description: Runs tests that use the flutter_test API as integration tests. -version: 0.5.0 +version: 0.5.0+1 homepage: https://github.com/flutter/plugins/tree/master/packages/e2e environment: From 91bba0f5c44826e21eb9994ccdcd4490d7ff9661 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 24 Jun 2020 10:52:58 -0400 Subject: [PATCH 26/69] Update README for plugin list (#2843) --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index aeb03ffa23ac..026dd9d7739b 100644 --- a/README.md +++ b/README.md @@ -44,13 +44,18 @@ These are the available plugins in this repository. | [camera](./packages/camera/) | [![pub package](https://img.shields.io/pub/v/camera.svg)](https://pub.dev/packages/camera) | | [connectivity](./packages/connectivity/) | [![pub package](https://img.shields.io/pub/v/connectivity.svg)](https://pub.dev/packages/connectivity) | | [device_info](./packages/device_info/) | [![pub package](https://img.shields.io/pub/v/device_info.svg)](https://pub.dev/packages/device_info) | +| [e2e](./packages/e2e/) | [![pub package](https://img.shields.io/pub/v/e2e.svg)](https://pub.dev/packages/e2e) | +| [espresso](./packages/espresso/) | [![pub package](https://img.shields.io/pub/v/espresso.svg)](https://pub.dev/packages/espresso) | +| [flutter_plugin_android_lifecycle](./packages/flutter_plugin_android_lifecycle/) | [![pub package](https://img.shields.io/pub/v/flutter_plugin_android_lifecycle.svg)](https://pub.dev/packages/flutter_plugin_android_lifecycle) | | [google_maps_flutter](./packages/google_maps_flutter) | [![pub package](https://img.shields.io/pub/v/google_maps_flutter.svg)](https://pub.dev/packages/google_maps_flutter) | | [google_sign_in](./packages/google_sign_in/) | [![pub package](https://img.shields.io/pub/v/google_sign_in.svg)](https://pub.dev/packages/google_sign_in) | | [image_picker](./packages/image_picker/) | [![pub package](https://img.shields.io/pub/v/image_picker.svg)](https://pub.dev/packages/image_picker) | | [in_app_purchase](./packages/in_app_purchase/) | [![pub package](https://img.shields.io/pub/v/in_app_purchase.svg)](https://pub.dev/packages/in_app_purchase) | +| [ios_platform_images](./packages/ios_platform_images/) | [![pub package](https://img.shields.io/pub/v/ios_platform_images.svg)](https://pub.dev/packages/ios_platform_images) | | [local_auth](./packages/local_auth/) | [![pub package](https://img.shields.io/pub/v/local_auth.svg)](https://pub.dev/packages/local_auth) | | [package_info](./packages/package_info/) | [![pub package](https://img.shields.io/pub/v/package_info.svg)](https://pub.dev/packages/package_info) | | [path_provider](./packages/path_provider/) | [![pub package](https://img.shields.io/pub/v/path_provider.svg)](https://pub.dev/packages/path_provider) | +| [plugin_platform_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | | [quick_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | | [sensors](./packages/sensors/) | [![pub package](https://img.shields.io/pub/v/sensors.svg)](https://pub.dev/packages/sensors) | | [share](./packages/share/) | [![pub package](https://img.shields.io/pub/v/share.svg)](https://pub.dev/packages/share) | From 459b7f8ccab075c8333489b98deb82608c21e929 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Mon, 29 Jun 2020 20:23:26 -0700 Subject: [PATCH 27/69] [connectivity_for_web] Introduce connectivity_for_web package. (#2820) Move the package formerly known as `experimental_connectivity_web` to flutter/plugins master, as `connectivity_for_web`. --- .../connectivity_for_web/.gitignore | 8 ++ .../connectivity_for_web/.metadata | 10 ++ .../connectivity_for_web/CHANGELOG.md | 11 ++ .../connectivity/connectivity_for_web/LICENSE | 26 ++++ .../connectivity_for_web/README.md | 62 ++++++++++ .../ios/connectivity_for_web.podspec | 23 ++++ .../lib/connectivity_for_web.dart | 63 ++++++++++ .../src/dart_html_connectivity_plugin.dart | 34 +++++ .../generated/network_information_types.dart | 78 ++++++++++++ ...k_information_api_connectivity_plugin.dart | 48 +++++++ .../lib/src/utils/connectivity_result.dart | 56 +++++++++ .../connectivity_for_web/pubspec.yaml | 31 +++++ .../connectivity_for_web/test/.gitignore | 8 ++ .../connectivity_for_web/test/lib/main.dart | 74 +++++++++++ .../test/lib/src/connectivity_mocks.dart | 60 +++++++++ .../connectivity_for_web/test/pubspec.yaml | 24 ++++ .../connectivity_for_web/test/web/index.html | 10 ++ .../connectivity_for_web/ts/.gitignore | 2 + .../connectivity_for_web/ts/README.md | 25 ++++ .../connectivity_for_web/ts/package-lock.json | 117 ++++++++++++++++++ .../connectivity_for_web/ts/package.json | 17 +++ .../ts/scripts/run_facade_gen.sh | 18 +++ 22 files changed, 805 insertions(+) create mode 100644 packages/connectivity/connectivity_for_web/.gitignore create mode 100644 packages/connectivity/connectivity_for_web/.metadata create mode 100644 packages/connectivity/connectivity_for_web/CHANGELOG.md create mode 100644 packages/connectivity/connectivity_for_web/LICENSE create mode 100644 packages/connectivity/connectivity_for_web/README.md create mode 100644 packages/connectivity/connectivity_for_web/ios/connectivity_for_web.podspec create mode 100644 packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart create mode 100644 packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart create mode 100644 packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart create mode 100644 packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart create mode 100644 packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart create mode 100644 packages/connectivity/connectivity_for_web/pubspec.yaml create mode 100644 packages/connectivity/connectivity_for_web/test/.gitignore create mode 100644 packages/connectivity/connectivity_for_web/test/lib/main.dart create mode 100644 packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart create mode 100644 packages/connectivity/connectivity_for_web/test/pubspec.yaml create mode 100644 packages/connectivity/connectivity_for_web/test/web/index.html create mode 100644 packages/connectivity/connectivity_for_web/ts/.gitignore create mode 100644 packages/connectivity/connectivity_for_web/ts/README.md create mode 100644 packages/connectivity/connectivity_for_web/ts/package-lock.json create mode 100644 packages/connectivity/connectivity_for_web/ts/package.json create mode 100755 packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh diff --git a/packages/connectivity/connectivity_for_web/.gitignore b/packages/connectivity/connectivity_for_web/.gitignore new file mode 100644 index 000000000000..d7dee828a6b9 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ +lib/generated_plugin_registrant.dart diff --git a/packages/connectivity/connectivity_for_web/.metadata b/packages/connectivity/connectivity_for_web/.metadata new file mode 100644 index 000000000000..23eb55ba6da2 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/.metadata @@ -0,0 +1,10 @@ +# 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: 52ee8a6c6565cd421dfa32042941eb40691f4746 + channel: master + +project_type: plugin diff --git a/packages/connectivity/connectivity_for_web/CHANGELOG.md b/packages/connectivity/connectivity_for_web/CHANGELOG.md new file mode 100644 index 000000000000..89e186abe1cb --- /dev/null +++ b/packages/connectivity/connectivity_for_web/CHANGELOG.md @@ -0,0 +1,11 @@ +## 0.3.0 + +* Rename from "experimental_connectivity_web" to "connectivity_for_web", and move to flutter/plugins master. + +## 0.2.0 + +* Add fallback on dart:html for browsers where NetworkInformationAPI is not supported. + +## 0.1.0 + +* Initial release. diff --git a/packages/connectivity/connectivity_for_web/LICENSE b/packages/connectivity/connectivity_for_web/LICENSE new file mode 100644 index 000000000000..4da9688730d1 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/LICENSE @@ -0,0 +1,26 @@ +Copyright 2016, the Flutter project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/connectivity/connectivity_for_web/README.md b/packages/connectivity/connectivity_for_web/README.md new file mode 100644 index 000000000000..66efc49fc840 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/README.md @@ -0,0 +1,62 @@ +# connectivity_for_web + +A web implementation of [connectivity](https://pub.dev/connectivity/connectivity). Currently this package uses an experimental API, with a fallback to dart:html, so not all features may be available to all browsers. + +## Usage + +### Import the package + +This package is a non-endorsed implementation of `connectivity` for the web platform, so you need to modify your `pubspec.yaml` to use it: + +```yaml +... +dependencies: + ... + connectivity: ^0.4.9 + connectivity_for_web: ^0.3.0 + ... +... +``` + +## Example + +Find the example wiring in the [Google sign-in example application](https://github.com/ditman/plugins/blob/connectivity-web/packages/connectivity/connectivity/example/lib/main.dart). + +## Limitations on the web platform + +In order to retrieve information about the quality/speed of a browser's connection, the web implementation of the `connectivity` plugin uses the browser's [**NetworkInformation** Web API](https://developer.mozilla.org/en-US/docs/Web/API/NetworkInformation), which as of this writing (June 2020) is still "experimental", and not available in all browsers: + +![Data on support for the netinfo feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/netinfo.png) + +On desktop browsers, this API only returns a very broad set of connectivity statuses (One of `'slow-2g', '2g', '3g', or '4g'`), and may *not* provide a Stream of changes. Firefox still hasn't enabled this feature by default. + +**Fallback to `navigator.onLine`** + +For those browsers where the NetworkInformation Web API is not available, the plugin falls back to the [**NavigatorOnLine** Web API](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine), which is more broadly supported: + +![Data on support for the online-status feature across the major browsers from caniuse.com](https://caniuse.bitsofco.de/image/online-status.png) + + +The NavigatorOnLine API is [provided by `dart:html`](https://api.dart.dev/stable/2.7.2/dart-html/Navigator/onLine.html), and only supports a boolean connectivity status (either online or offline), with no network speed information. In those cases the plugin will return either `wifi` (when the browser is online) or `none` (when it's not). + +Other than the approximate "downlink" speed, where available, and due to security and privacy concerns, **no Web browser will provide** any specific information about the actual network your users' device is connected to, like **the SSID on a Wi-Fi, or the MAC address of their device.** + +## Contributions and Testing + +Tests are crucial to contributions to this package. All new contributions should be reasonably tested. + +In order to run tests in this package, do: + +``` +cd test +flutter run -d chrome +``` + +All contributions to this package are welcome. Read the [Contributing to Flutter Plugins](https://github.com/flutter/plugins/blob/master/CONTRIBUTING.md) guide to get started. + +## Issues and feedback + +Please file an [issue](https://github.com/ditman/plugins/issues/new) +to send feedback or report a bug. + +**Thank you!** diff --git a/packages/connectivity/connectivity_for_web/ios/connectivity_for_web.podspec b/packages/connectivity/connectivity_for_web/ios/connectivity_for_web.podspec new file mode 100644 index 000000000000..75b891c56533 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/ios/connectivity_for_web.podspec @@ -0,0 +1,23 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint connectivity_web.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'connectivity_for_web' + s.version = '0.1.0' + s.summary = 'No-op implementation of connectivity web plugin to avoid build issues on iOS' + s.description = <<-DESC +temp fake connectivity_web plugin + DESC + s.homepage = 'https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_for_web' + s.license = { :file => '../LICENSE' } + s.author = { 'Flutter Team' => 'flutter-dev@googlegroups.com' } + s.source = { :path => '.' } + s.source_files = 'Classes/**/*' + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.swift_version = '5.0' +end diff --git a/packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart b/packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart new file mode 100644 index 000000000000..fd061a878867 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/lib/connectivity_for_web.dart @@ -0,0 +1,63 @@ +import 'dart:async'; + +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_web_plugins/flutter_web_plugins.dart'; + +import 'src/network_information_api_connectivity_plugin.dart'; +import 'src/dart_html_connectivity_plugin.dart'; + +/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. +class ConnectivityPlugin extends ConnectivityPlatform { + /// Factory method that initializes the connectivity plugin platform with an instance + /// of the plugin for the web. + static void registerWith(Registrar registrar) { + if (NetworkInformationApiConnectivityPlugin.isSupported()) { + ConnectivityPlatform.instance = NetworkInformationApiConnectivityPlugin(); + } else { + ConnectivityPlatform.instance = DartHtmlConnectivityPlugin(); + } + } + + // The following are completely unsupported methods on the web platform. + + // Creates an "unsupported_operation" PlatformException for a given `method` name. + Object _unsupported(String method) { + return PlatformException( + code: 'UNSUPPORTED_OPERATION', + message: '$method() is not supported on the web platform.', + ); + } + + /// Obtains the wifi name (SSID) of the connected network + @override + Future getWifiName() { + throw _unsupported('getWifiName'); + } + + /// Obtains the wifi BSSID of the connected network. + @override + Future getWifiBSSID() { + throw _unsupported('getWifiBSSID'); + } + + /// Obtains the IP address of the connected wifi network + @override + Future getWifiIP() { + throw _unsupported('getWifiIP'); + } + + /// Request to authorize the location service (Only on iOS). + @override + Future requestLocationServiceAuthorization({ + bool requestAlwaysLocationUsage = false, + }) { + throw _unsupported('requestLocationServiceAuthorization'); + } + + /// Get the current location service authorization (Only on iOS). + @override + Future getLocationServiceAuthorization() { + throw _unsupported('getLocationServiceAuthorization'); + } +} diff --git a/packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart b/packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart new file mode 100644 index 000000000000..5caa5679d6ad --- /dev/null +++ b/packages/connectivity/connectivity_for_web/lib/src/dart_html_connectivity_plugin.dart @@ -0,0 +1,34 @@ +import 'dart:async'; +import 'dart:html' as html show window; + +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; +import 'package:connectivity_for_web/connectivity_for_web.dart'; + +/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. +class DartHtmlConnectivityPlugin extends ConnectivityPlugin { + /// Checks the connection status of the device. + @override + Future checkConnectivity() async { + return html.window.navigator.onLine + ? ConnectivityResult.wifi + : ConnectivityResult.none; + } + + StreamController _connectivityResult; + + /// Returns a Stream of ConnectivityResults changes. + @override + Stream get onConnectivityChanged { + if (_connectivityResult == null) { + _connectivityResult = StreamController(); + // Fallback to dart:html window.onOnline / window.onOffline + html.window.onOnline.listen((event) { + _connectivityResult.add(ConnectivityResult.wifi); + }); + html.window.onOffline.listen((event) { + _connectivityResult.add(ConnectivityResult.none); + }); + } + return _connectivityResult.stream; + } +} diff --git a/packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart b/packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart new file mode 100644 index 000000000000..c4045b3ec1fc --- /dev/null +++ b/packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart @@ -0,0 +1,78 @@ +@JS() +library network_information_types; + +import "package:js/js.dart"; +import "dart:html" show EventListener, EventTarget; + +/// W3C Spec Draft http://wicg.github.io/netinfo/ +/// Edition: Draft Community Group Report 20 February 2019 + +/// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface +@anonymous +@JS() +abstract class Navigator implements NavigatorNetworkInformation {} + +@anonymous +@JS() +abstract class WorkerNavigator implements NavigatorNetworkInformation { + external factory WorkerNavigator({NetworkInformation connection}); +} + +/// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface +@anonymous +@JS() +abstract class NavigatorNetworkInformation { + external NetworkInformation get connection; + external factory NavigatorNetworkInformation({NetworkInformation connection}); +} + +/// http://wicg.github.io/netinfo/#connection-types +/*type ConnectionType = + | 'bluetooth' + | 'cellular' + | 'ethernet' + | 'mixed' + | 'none' + | 'other' + | 'unknown' + | 'wifi' + | 'wimax'; +*/ + +/// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum +/*type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';*/ + +/// http://wicg.github.io/netinfo/#dom-megabit +/*type Megabit = number;*/ +/// http://wicg.github.io/netinfo/#dom-millisecond +/*type Millisecond = number;*/ + +/// http://wicg.github.io/netinfo/#networkinformation-interface +@anonymous +@JS() +abstract class NetworkInformation implements EventTarget { + /// http://wicg.github.io/netinfo/#type-attribute + external String /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/ get type; + + /// http://wicg.github.io/netinfo/#effectivetype-attribute + external String /*'2g'|'3g'|'4g'|'slow-2g'*/ get effectiveType; + + /// http://wicg.github.io/netinfo/#downlinkmax-attribute + external num get downlinkMax; + + /// http://wicg.github.io/netinfo/#downlink-attribute + external num get downlink; + + /// http://wicg.github.io/netinfo/#rtt-attribute + external num get rtt; + + /// http://wicg.github.io/netinfo/#savedata-attribute + external bool get saveData; + + /// http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection + external EventListener get onchange; + external set onchange(EventListener v); +} + +@JS() +external Navigator get navigator; diff --git a/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart b/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart new file mode 100644 index 000000000000..d88487b9c406 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart @@ -0,0 +1,48 @@ +import 'dart:async'; + +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; +import 'package:connectivity_for_web/connectivity_for_web.dart'; +import 'package:flutter/foundation.dart'; +import 'package:js/js.dart'; + +import 'generated/network_information_types.dart' as dom; +import 'utils/connectivity_result.dart'; + +/// The web implementation of the ConnectivityPlatform of the Connectivity plugin. +class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin { + final dom.NetworkInformation _networkInformation; + + /// A check to determine if this version of the plugin can be used. + static bool isSupported() => dom.navigator?.connection != null; + + /// The constructor of the plugin. + NetworkInformationApiConnectivityPlugin() + : this.withConnection(dom.navigator?.connection); + + /// Creates the plugin, with an override of the NetworkInformation object. + @visibleForTesting + NetworkInformationApiConnectivityPlugin.withConnection( + dom.NetworkInformation connection) + : _networkInformation = connection; + + /// Checks the connection status of the device. + @override + Future checkConnectivity() async { + return networkInformationToConnectivityResult(_networkInformation); + } + + StreamController _connectivityResult; + + /// Returns a Stream of ConnectivityResults changes. + @override + Stream get onConnectivityChanged { + if (_connectivityResult == null) { + _connectivityResult = StreamController(); + _networkInformation.onchange = allowInterop((_) { + _connectivityResult + .add(networkInformationToConnectivityResult(_networkInformation)); + }); + } + return _connectivityResult.stream; + } +} diff --git a/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart b/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart new file mode 100644 index 000000000000..28943ef5c7e1 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart @@ -0,0 +1,56 @@ +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; + +/// Converts an incoming NetworkInformation object into the correct ConnectivityResult. +// +// We can't be more specific on the signature of this method because the API is odd, +// data can come from a static value in the DOM, or as the 'target' of a DOM Event. +// +// If we type info as `NetworkInformation`, Dart will complain with: +// "Uncaught Error: Expected a value of type 'NetworkInformation', +// but got one of type 'NetworkInformation'" +ConnectivityResult networkInformationToConnectivityResult( + dynamic /* NetworkInformation */ info) { + if (info == null) { + return ConnectivityResult.none; + } + if (info.downlink == 0 && info.rtt == 0) { + return ConnectivityResult.none; + } + if (info.type != null) { + return _typeToConnectivityResult(info.type); + } + if (info.effectiveType != null) { + return _effectiveTypeToConnectivityResult(info.effectiveType); + } + return ConnectivityResult.none; +} + +ConnectivityResult _effectiveTypeToConnectivityResult(String effectiveType) { + // Possible values: + /*'2g'|'3g'|'4g'|'slow-2g'*/ + switch (effectiveType) { + case 'slow-2g': + case '2g': + case '3g': + return ConnectivityResult.mobile; + default: + return ConnectivityResult.wifi; + } +} + +ConnectivityResult _typeToConnectivityResult(String type) { + // Possible values: + /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/ + switch (type) { + case 'none': + return ConnectivityResult.none; + case 'bluetooth': + case 'cellular': + case 'mixed': + case 'other': + case 'unknown': + return ConnectivityResult.mobile; + default: + return ConnectivityResult.wifi; + } +} diff --git a/packages/connectivity/connectivity_for_web/pubspec.yaml b/packages/connectivity/connectivity_for_web/pubspec.yaml new file mode 100644 index 000000000000..e4a1673e40c2 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/pubspec.yaml @@ -0,0 +1,31 @@ +name: connectivity_for_web +description: An implementation for the web platform of the Flutter `connectivity` plugin. This uses the NetworkInformation Web API, with a fallback to Navigator.onLine. +version: 0.3.0 +homepage: https://github.com/ditman/plugins/tree/connectivity-web/packages/connectivity/experimental_connectivity_web + +flutter: + plugin: + platforms: + web: + pluginClass: ConnectivityPlugin + fileName: connectivity_for_web.dart + +dependencies: + connectivity_platform_interface: ^1.0.3 + js: ^0.6.1+1 + flutter_web_plugins: + sdk: flutter + flutter: + sdk: flutter + +dev_dependencies: + test: any + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + e2e: ^0.2.4+3 + +environment: + sdk: ">=2.6.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4" diff --git a/packages/connectivity/connectivity_for_web/test/.gitignore b/packages/connectivity/connectivity_for_web/test/.gitignore new file mode 100644 index 000000000000..d7dee828a6b9 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/test/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ +lib/generated_plugin_registrant.dart diff --git a/packages/connectivity/connectivity_for_web/test/lib/main.dart b/packages/connectivity/connectivity_for_web/test/lib/main.dart new file mode 100644 index 000000000000..21621a947ee6 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/test/lib/main.dart @@ -0,0 +1,74 @@ +import 'package:e2e/e2e.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; +import 'package:connectivity_for_web/src/network_information_api_connectivity_plugin.dart'; + +import 'src/connectivity_mocks.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + group('checkConnectivity', () { + void testCheckConnectivity({ + String type, + String effectiveType, + num downlink = 10, + num rtt = 50, + ConnectivityResult expected, + }) { + MockNetworkInformation connection = MockNetworkInformation( + type: type, + effectiveType: effectiveType, + downlink: downlink, + rtt: rtt); + NetworkInformationApiConnectivityPlugin plugin = + NetworkInformationApiConnectivityPlugin.withConnection(connection); + expect(plugin.checkConnectivity(), completion(equals(expected))); + } + + test('0 downlink and rtt -> none', () { + testCheckConnectivity( + effectiveType: '4g', + downlink: 0, + rtt: 0, + expected: ConnectivityResult.none); + }); + test('slow-2g -> mobile', () { + testCheckConnectivity( + effectiveType: 'slow-2g', expected: ConnectivityResult.mobile); + }); + test('2g -> mobile', () { + testCheckConnectivity( + effectiveType: '2g', expected: ConnectivityResult.mobile); + }); + test('3g -> mobile', () { + testCheckConnectivity( + effectiveType: '3g', expected: ConnectivityResult.mobile); + }); + test('4g -> wifi', () { + testCheckConnectivity( + effectiveType: '4g', expected: ConnectivityResult.wifi); + }); + }); + + group('get onConnectivityChanged', () { + test('puts change events in a Stream', () async { + MockNetworkInformation connection = + MockNetworkInformation(effectiveType: '4g', downlink: 10, rtt: 50); + NetworkInformationApiConnectivityPlugin plugin = + NetworkInformationApiConnectivityPlugin.withConnection(connection); + + Stream results = plugin.onConnectivityChanged; + + // Fake a disconnect-reconnect + connection.mockChangeValue(downlink: 0, rtt: 0); + connection.mockChangeValue(downlink: 10, rtt: 50); + + // The stream of results is infinite, so we need to .take(2) for this test to complete. + expect( + results.take(2).toList(), + completion( + equals([ConnectivityResult.none, ConnectivityResult.wifi]))); + }); + }); +} diff --git a/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart b/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart new file mode 100644 index 000000000000..9ce2e811d461 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart @@ -0,0 +1,60 @@ +import 'dart:html'; + +import 'package:connectivity_for_web/src/generated/network_information_types.dart' + as dom; + +/// A Mock implementation of the NetworkInformation API that allows +/// for external modification of its values. +class MockNetworkInformation extends dom.NetworkInformation { + @override + String type; + + @override + String effectiveType; + + @override + num downlink; + + @override + num rtt; + + @override + EventListener onchange; + + /// Constructor of mocked instances... + MockNetworkInformation({ + this.type, + this.effectiveType, + this.downlink, + this.rtt, + }); + + /// Changes the desired values, and triggers the change event listener. + void mockChangeValue({ + String type, + String effectiveType, + num downlink, + num rtt, + }) { + this.type = type ?? this.type; + this.effectiveType = effectiveType ?? this.effectiveType; + this.downlink = downlink ?? this.downlink; + this.rtt = rtt ?? this.rtt; + + onchange(Event('change')); + } + + @override + void addEventListener(String type, listener, [bool useCapture]) {} + + @override + bool dispatchEvent(Event event) { + return true; + } + + @override + Events get on => null; + + @override + void removeEventListener(String type, listener, [bool useCapture]) {} +} diff --git a/packages/connectivity/connectivity_for_web/test/pubspec.yaml b/packages/connectivity/connectivity_for_web/test/pubspec.yaml new file mode 100644 index 000000000000..4d7d10a775e2 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/test/pubspec.yaml @@ -0,0 +1,24 @@ +name: connectivity_web_example +description: Example web app for the connectivity plugin +version: 0.1.0 +homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_web + +dependencies: + connectivity_for_web: + path: ../ + js: ^0.6.1+1 + flutter_web_plugins: + sdk: flutter + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_driver: + sdk: flutter + e2e: ^0.2.4+3 + +environment: + sdk: ">=2.6.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4" diff --git a/packages/connectivity/connectivity_for_web/test/web/index.html b/packages/connectivity/connectivity_for_web/test/web/index.html new file mode 100644 index 000000000000..6eff9a740d43 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/test/web/index.html @@ -0,0 +1,10 @@ + + + + + example + + + + + diff --git a/packages/connectivity/connectivity_for_web/ts/.gitignore b/packages/connectivity/connectivity_for_web/ts/.gitignore new file mode 100644 index 000000000000..de4d1f007dd1 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/ts/.gitignore @@ -0,0 +1,2 @@ +dist +node_modules diff --git a/packages/connectivity/connectivity_for_web/ts/README.md b/packages/connectivity/connectivity_for_web/ts/README.md new file mode 100644 index 000000000000..3372ad2f3790 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/ts/README.md @@ -0,0 +1,25 @@ +# JS Facade generator + +This npm script takes the `network-information-types` npm package, and runs it through Dart's `dart_js_facade_gen` to auto-generate (most) of the JS facades used by this plugin. + +The process is not completely automated yet, but it should be pretty close. + +To generate the facades, and after [installing `npm`](https://www.npmjs.com/get-npm), do: + +``` +npm install +npm run build +``` + +The above will fetch the required dependencies, and generate a `dist/network_information_types.dart` file that you can use with the plugin. + +``` +cp dist/*.dart ../lib/src/generated +``` + +This script should come handy once the Network Information Web API changes, or becomes stable, so the JS-interop part of this plugin can be regenerated more easily. + +Read more: + +* [Dart JS Interop](https://dart.dev/web/js-interop) +* [dart_js_facade_gen](https://www.npmjs.com/package/dart_js_facade_gen) \ No newline at end of file diff --git a/packages/connectivity/connectivity_for_web/ts/package-lock.json b/packages/connectivity/connectivity_for_web/ts/package-lock.json new file mode 100644 index 000000000000..45293a400492 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/ts/package-lock.json @@ -0,0 +1,117 @@ +{ + "name": "network-information-types-to-dart-generator", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/chai": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.9.tgz", + "integrity": "sha512-NeXgZj+MFL4izGqA4sapdYzkzQG+MtGra9vhQ58dnmDY++VgJaRUws+aLVV5zRJCYJl/8s9IjMmhiUw1WsKSmw==" + }, + "@types/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", + "requires": { + "@types/node": "*" + } + }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=" + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" + }, + "@types/node": { + "version": "12.12.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.28.tgz", + "integrity": "sha512-g73GJYJDXgf0jqg+P9S8h2acWbDXNkoCX8DLtJVu7Fkn788pzQ/oJsrdJz/2JejRf/SjfZaAhsw+3nd1D5EWGg==" + }, + "@types/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LrnsgZIfJaysFkv9rRJp4/uAyqw87oVed3s1hhF83nwbo9c7MG9g5DqR0seHP+lkX4ldmMrVolPjQSe2ZfD0yA==", + "requires": { + "source-map": "*" + } + }, + "@types/source-map-support": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.1.tgz", + "integrity": "sha512-VDqnZe9D2zR19qbeRvwYyHSp7AtUtCkTaRVFQ8wzwH9TXw9kKKq/vBhfEnFEXVupO2M0lBMA9mr/XyQ6gEkUOA==", + "requires": { + "@types/node": "*" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "dart-style": { + "version": "1.3.2-dev", + "resolved": "https://registry.npmjs.org/dart-style/-/dart-style-1.3.2-dev.tgz", + "integrity": "sha512-NFI4UQYvG32t/cEkQAdkXT2ZT72tjF61tMWoALmnGwj03d2Co94zwGfbnFfdQUQvrhUNx8Wz2jKSVxGrmFaVJQ==" + }, + "dart_js_facade_gen": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/dart_js_facade_gen/-/dart_js_facade_gen-0.0.7.tgz", + "integrity": "sha512-AZiWsccbzhgJWmBjbFTPuvBhwGXk7AN8nOP91/I8PqUfSvVALiWshDc66TvywNkdNogAE5X8zlxjodw1C3iHpA==", + "requires": { + "@types/chai": "^4.2.3", + "@types/fs-extra": "^8.0.0", + "@types/minimist": "^1.2.0", + "@types/mocha": "^5.2.7", + "@types/node": "^12.7.8", + "@types/source-map": "^0.5.7", + "@types/source-map-support": "^0.5.0", + "dart-style": "^1.3.2-dev", + "minimist": "^1.2.0", + "source-map": "^0.7.3", + "source-map-support": "^0.5.13", + "typescript": "^3.6.3" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "network-information-types": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/network-information-types/-/network-information-types-0.1.0.tgz", + "integrity": "sha512-cRUCYZoRHTMjYcgk5MbwqM0h0Za34panRxAJKY8n+mQ+NLMuRIw7aKzmaZqkC/cte7bnRcdfTwFA27GgN62EtQ==" + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "typescript": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", + "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==" + } + } +} diff --git a/packages/connectivity/connectivity_for_web/ts/package.json b/packages/connectivity/connectivity_for_web/ts/package.json new file mode 100644 index 000000000000..665c89d6afbb --- /dev/null +++ b/packages/connectivity/connectivity_for_web/ts/package.json @@ -0,0 +1,17 @@ +{ + "name": "network-information-types-to-dart-generator", + "version": "1.0.0", + "description": "Use dart_js_facade_gen to generate the facade for the network-information-types package.", + "main": "index.js", + "private": true, + "scripts": { + "build": "./scripts/run_facade_gen.sh", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "MIT", + "dependencies": { + "network-information-types": "0.1.0", + "dart_js_facade_gen": "^0.0.7" + } +} diff --git a/packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh b/packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh new file mode 100755 index 000000000000..c74b8ba171b2 --- /dev/null +++ b/packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +INDEX_PATH=node_modules/network-information-types/dist-types/index.d.ts +WORK_PATH=network_information_types.d.ts +DIST_PATH=dist + +# Create dist if it doesn't exist already +mkdir -p $DIST_PATH + +# Copy the input file(s) into our work path +cp $INDEX_PATH $WORK_PATH + +# Run dart_js_facade_gen +dart_js_facade_gen $WORK_PATH --trust-js-types --generate-html --destination . + +# Move output to the right place, and clean after yourself +mv *.dart $DIST_PATH +rm $WORK_PATH From b43b1a8ce3b958bf4e1feb54475f75b706743c1f Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 30 Jun 2020 19:42:00 +0100 Subject: [PATCH 28/69] [url_launcher_web] Adds "tel" and "sms" URL support (#2847) --- .../url_launcher_web/CHANGELOG.md | 4 ++ .../lib/url_launcher_web.dart | 19 ++++-- .../url_launcher_web/pubspec.yaml | 2 +- .../test/url_launcher_web_test.dart | 65 ++++++++++++++++++- 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/packages/url_launcher/url_launcher_web/CHANGELOG.md b/packages/url_launcher/url_launcher_web/CHANGELOG.md index df1b2c97b744..e9f7bde63fda 100644 --- a/packages/url_launcher/url_launcher_web/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_web/CHANGELOG.md @@ -1,3 +1,7 @@ +# 0.1.2 + +- Adds "tel" and "sms" support + # 0.1.1+6 - Open "mailto" urls with target set as "\_top" on Safari browsers. diff --git a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart index e55ceb2269bc..1bac4d524122 100644 --- a/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart +++ b/packages/url_launcher/url_launcher_web/lib/url_launcher_web.dart @@ -7,7 +7,11 @@ import 'package:url_launcher_platform_interface/url_launcher_platform_interface. import 'package:platform_detect/platform_detect.dart' show browser; -const _mailtoScheme = 'mailto'; +const _safariTargetTopSchemes = { + 'mailto', + 'tel', + 'sms', +}; /// The web implementation of [UrlLauncherPlatform]. /// @@ -16,7 +20,10 @@ class UrlLauncherPlugin extends UrlLauncherPlatform { html.Window _window; // The set of schemes that can be handled by the plugin - static final _supportedSchemes = {'http', 'https', _mailtoScheme}; + static final _supportedSchemes = { + 'http', + 'https', + }.union(_safariTargetTopSchemes); /// A constructor that allows tests to override the window object used by the plugin. UrlLauncherPlugin({@visibleForTesting html.Window window}) @@ -29,16 +36,18 @@ class UrlLauncherPlugin extends UrlLauncherPlatform { String _getUrlScheme(String url) => Uri.tryParse(url)?.scheme; - bool _isMailtoScheme(String url) => _getUrlScheme(url) == _mailtoScheme; + bool _isSafariTargetTopScheme(String url) => + _safariTargetTopSchemes.contains(_getUrlScheme(url)); /// Opens the given [url] in a new window. /// /// Returns the newly created window. @visibleForTesting html.WindowBase openNewWindow(String url) { - // We need to open mailto urls on the _top window context on safari browsers. + // We need to open mailto, tel and sms urls on the _top window context on safari browsers. // See https://github.com/flutter/flutter/issues/51461 for reference. - final target = browser.isSafari && _isMailtoScheme(url) ? '_top' : ''; + final target = + browser.isSafari && _isSafariTargetTopScheme(url) ? '_top' : ''; return _window.open(url, target); } diff --git a/packages/url_launcher/url_launcher_web/pubspec.yaml b/packages/url_launcher/url_launcher_web/pubspec.yaml index 207f2dc15424..9a5beac00b94 100644 --- a/packages/url_launcher/url_launcher_web/pubspec.yaml +++ b/packages/url_launcher/url_launcher_web/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/u # 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.1.1+6 +version: 0.1.2 flutter: plugin: diff --git a/packages/url_launcher/url_launcher_web/test/url_launcher_web_test.dart b/packages/url_launcher/url_launcher_web/test/url_launcher_web_test.dart index 4cf92062e10f..b7e107d892cf 100644 --- a/packages/url_launcher/url_launcher_web/test/url_launcher_web_test.dart +++ b/packages/url_launcher/url_launcher_web/test/url_launcher_web_test.dart @@ -36,8 +36,13 @@ void main() { plugin.canLaunch('mailto:name@mydomain.com'), completion(isTrue)); }); - test('"tel" URLs -> false', () { - expect(plugin.canLaunch('tel:5551234567'), completion(isFalse)); + test('"tel" URLs -> true', () { + expect(plugin.canLaunch('tel:5551234567'), completion(isTrue)); + }); + + test('"sms" URLs -> true', () { + expect(plugin.canLaunch('sms:+19725551212?body=hello%20there'), + completion(isTrue)); }); }); @@ -48,6 +53,9 @@ void main() { .thenReturn(MockWindow()); when(mockWindow.open('mailto:name@mydomain.com', '')) .thenReturn(MockWindow()); + when(mockWindow.open('tel:5551234567', '')).thenReturn(MockWindow()); + when(mockWindow.open('sms:+19725551212?body=hello%20there', '')) + .thenReturn(MockWindow()); }); test('launching a URL returns true', () { @@ -77,6 +85,34 @@ void main() { ), completion(isTrue)); }); + + test('launching a "tel" returns true', () { + expect( + plugin.launch( + 'tel:5551234567', + useSafariVC: null, + useWebView: null, + universalLinksOnly: null, + enableDomStorage: null, + enableJavaScript: null, + headers: null, + ), + completion(isTrue)); + }); + + test('launching a "sms" returns true', () { + expect( + plugin.launch( + 'sms:+19725551212?body=hello%20there', + useSafariVC: null, + useWebView: null, + universalLinksOnly: null, + enableDomStorage: null, + enableJavaScript: null, + headers: null, + ), + completion(isTrue)); + }); }); group('openNewWindow', () { @@ -98,6 +134,18 @@ void main() { verify(mockWindow.open('mailto:name@mydomain.com', '')); }); + test('tel urls should be launched on a new window', () { + plugin.openNewWindow('tel:5551234567'); + + verify(mockWindow.open('tel:5551234567', '')); + }); + + test('sms urls should be launched on a new window', () { + plugin.openNewWindow('sms:+19725551212?body=hello%20there'); + + verify(mockWindow.open('sms:+19725551212?body=hello%20there', '')); + }); + group('Safari', () { setUp(() { platform.configurePlatformForTesting(browser: platform.safari); @@ -120,6 +168,19 @@ void main() { verify(mockWindow.open('mailto:name@mydomain.com', '_top')); }); + + test('tel urls should be launched on the same window', () { + plugin.openNewWindow('tel:5551234567'); + + verify(mockWindow.open('tel:5551234567', '_top')); + }); + + test('sms urls should be launched on the same window', () { + plugin.openNewWindow('sms:+19725551212?body=hello%20there'); + + verify( + mockWindow.open('sms:+19725551212?body=hello%20there', '_top')); + }); }); }); }); From db547b604bad7b40e032f9be42fc6813be68d756 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 1 Jul 2020 10:07:47 +1200 Subject: [PATCH 29/69] [shared_preferences_linux] Add support for Linux (#2836) Adds shared_preferences support for Linux. Part of flutter/flutter#41720 --- .../shared_preferences_linux/.gitignore | 7 ++ .../shared_preferences_linux/.metadata | 10 ++ .../shared_preferences_linux/CHANGELOG.md | 2 + .../shared_preferences_linux/LICENSE | 27 ++++++ .../shared_preferences_linux/README.md | 22 +++++ .../example/.gitignore | 43 +++++++++ .../example/.metadata | 10 ++ .../example/README.md | 8 ++ .../example/lib/main.dart | 87 +++++++++++++++++ .../example/pubspec.yaml | 15 +++ .../lib/shared_preferences_linux.dart | 96 +++++++++++++++++++ .../shared_preferences_linux/pubspec.yaml | 29 ++++++ .../test/shared_preferences_linux_test.dart | 74 ++++++++++++++ script/build_all_plugins_app.sh | 1 + 14 files changed, 431 insertions(+) create mode 100644 packages/shared_preferences/shared_preferences_linux/.gitignore create mode 100644 packages/shared_preferences/shared_preferences_linux/.metadata create mode 100644 packages/shared_preferences/shared_preferences_linux/CHANGELOG.md create mode 100644 packages/shared_preferences/shared_preferences_linux/LICENSE create mode 100644 packages/shared_preferences/shared_preferences_linux/README.md create mode 100644 packages/shared_preferences/shared_preferences_linux/example/.gitignore create mode 100644 packages/shared_preferences/shared_preferences_linux/example/.metadata create mode 100644 packages/shared_preferences/shared_preferences_linux/example/README.md create mode 100644 packages/shared_preferences/shared_preferences_linux/example/lib/main.dart create mode 100644 packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml create mode 100644 packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart create mode 100644 packages/shared_preferences/shared_preferences_linux/pubspec.yaml create mode 100644 packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart diff --git a/packages/shared_preferences/shared_preferences_linux/.gitignore b/packages/shared_preferences/shared_preferences_linux/.gitignore new file mode 100644 index 000000000000..e9dc58d3d6e2 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/.gitignore @@ -0,0 +1,7 @@ +.DS_Store +.dart_tool/ + +.packages +.pub/ + +build/ diff --git a/packages/shared_preferences/shared_preferences_linux/.metadata b/packages/shared_preferences/shared_preferences_linux/.metadata new file mode 100644 index 000000000000..9615744e96d1 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/.metadata @@ -0,0 +1,10 @@ +# 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: e491544588e8d34fdf31d5f840b4649850ef167a + channel: master + +project_type: plugin diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md new file mode 100644 index 000000000000..11694802aacf --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -0,0 +1,2 @@ +## 0.0.1 +* Initial release to support shared_preferences on Linux. diff --git a/packages/shared_preferences/shared_preferences_linux/LICENSE b/packages/shared_preferences/shared_preferences_linux/LICENSE new file mode 100644 index 000000000000..0c91662b3f2f --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/shared_preferences/shared_preferences_linux/README.md b/packages/shared_preferences/shared_preferences_linux/README.md new file mode 100644 index 000000000000..1894f50ae99e --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/README.md @@ -0,0 +1,22 @@ +# shared_preferences_linux + +The Linux implementation of [`shared_preferences`][1]. + +## Usage + +### Import the package + +This package is an unendorsed Linux implementation of `shared_preferences`. + +In order to use this now, you'll need to depend on `shared_preferences_linux`. +When this package is endorsed it will be automatically used by the `shared_preferences` package and you can switch to that API. + +```yaml +... +dependencies: + ... + shared_preferences_linux: ^0.0.1 + ... +``` + +[1]: ../ diff --git a/packages/shared_preferences/shared_preferences_linux/example/.gitignore b/packages/shared_preferences/shared_preferences_linux/example/.gitignore new file mode 100644 index 000000000000..1ba9c339effb --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/.gitignore @@ -0,0 +1,43 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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 +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/shared_preferences/shared_preferences_linux/example/.metadata b/packages/shared_preferences/shared_preferences_linux/example/.metadata new file mode 100644 index 000000000000..c0bc9a90268a --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/.metadata @@ -0,0 +1,10 @@ +# 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: e491544588e8d34fdf31d5f840b4649850ef167a + channel: master + +project_type: app diff --git a/packages/shared_preferences/shared_preferences_linux/example/README.md b/packages/shared_preferences/shared_preferences_linux/example/README.md new file mode 100644 index 000000000000..9d3bf1faf406 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/README.md @@ -0,0 +1,8 @@ +# shared_preferences_example + +Demonstrates how to use the shared_preferences plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). diff --git a/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart new file mode 100644 index 000000000000..ceacf2f95f28 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/lib/main.dart @@ -0,0 +1,87 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:shared_preferences_linux/shared_preferences_linux.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'SharedPreferences Demo', + home: SharedPreferencesDemo(), + ); + } +} + +class SharedPreferencesDemo extends StatefulWidget { + SharedPreferencesDemo({Key key}) : super(key: key); + + @override + SharedPreferencesDemoState createState() => SharedPreferencesDemoState(); +} + +class SharedPreferencesDemoState extends State { + final prefs = SharedPreferencesLinux.instance; + Future _counter; + + Future _incrementCounter() async { + final values = await prefs.getAll(); + final int counter = (values['counter'] as int ?? 0) + 1; + + setState(() { + _counter = prefs.setValue(null, "counter", counter).then((bool success) { + return counter; + }); + }); + } + + @override + void initState() { + super.initState(); + _counter = prefs.getAll().then((Map values) { + return (values['counter'] ?? 0); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text("SharedPreferences Demo"), + ), + body: Center( + child: FutureBuilder( + future: _counter, + builder: (BuildContext context, AsyncSnapshot snapshot) { + switch (snapshot.connectionState) { + case ConnectionState.waiting: + return const CircularProgressIndicator(); + default: + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return Text( + 'Button tapped ${snapshot.data} time${snapshot.data == 1 ? '' : 's'}.\n\n' + 'This should persist across restarts.', + ); + } + } + })), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), + ); + } +} diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml new file mode 100644 index 000000000000..1c0624035c54 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -0,0 +1,15 @@ +name: shared_preferences_linux_example +description: Demonstrates how to use the shared_preferences_linux plugin. + +dependencies: + flutter: + sdk: flutter + shared_preferences_linux: ^0.1.0 + +dependency_overrides: + shared_preferences_linux: + path: ../ + +flutter: + uses-material-design: true + diff --git a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart new file mode 100644 index 000000000000..dc93100c3277 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -0,0 +1,96 @@ +// Copyright 2020 The Chromium 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 'dart:async'; +import 'dart:convert' show json; +import 'package:file/file.dart'; +import 'package:file/local.dart'; +import 'package:meta/meta.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; + +/// The Linux implementation of [SharedPreferencesStorePlatform]. +/// +/// This class implements the `package:shared_preferences` functionality for Linux. +class SharedPreferencesLinux extends SharedPreferencesStorePlatform { + /// The default instance of [SharedPreferencesLinux] to use. + static SharedPreferencesLinux instance = SharedPreferencesLinux(); + + /// Local copy of preferences + Map _cachedPreferences; + + /// File system used to store to disk. Exposed for testing only. + @visibleForTesting + FileSystem fs = LocalFileSystem(); + + /// Gets the file where the preferences are stored. + Future _getLocalDataFile() async { + var directory = await getApplicationSupportDirectory(); + var filePath = path.join(directory.path, 'shared_preferences.json'); + return fs.file(filePath); + } + + /// Gets the preferences from the stored file. Once read, the preferences are + /// maintained in memory. + Future> _readPreferences() async { + if (_cachedPreferences != null) { + return _cachedPreferences; + } + + _cachedPreferences = {}; + var localDataFile = await _getLocalDataFile(); + if (localDataFile.existsSync()) { + String stringMap = localDataFile.readAsStringSync(); + if (stringMap.isNotEmpty) { + _cachedPreferences = json.decode(stringMap) as Map; + } + } + + return _cachedPreferences; + } + + /// Writes the cached preferences to disk. Returns [true] if the operation + /// succeeded. + Future _writePreferences(Map preferences) async { + try { + var localDataFile = await _getLocalDataFile(); + if (!localDataFile.existsSync()) { + localDataFile.createSync(recursive: true); + } + var stringMap = json.encode(preferences); + localDataFile.writeAsStringSync(stringMap); + } catch (e) { + print("Error saving preferences to disk: $e"); + return false; + } + return true; + } + + @override + Future clear() async { + var preferences = await _readPreferences(); + preferences.clear(); + return _writePreferences(preferences); + } + + @override + Future> getAll() async { + return _readPreferences(); + } + + @override + Future remove(String key) async { + var preferences = await _readPreferences(); + preferences.remove(key); + return _writePreferences(preferences); + } + + @override + Future setValue(String valueType, String key, Object value) async { + var preferences = await _readPreferences(); + preferences[key] = value; + return _writePreferences(preferences); + } +} diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml new file mode 100644 index 000000000000..2539b937e006 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -0,0 +1,29 @@ +name: shared_preferences_linux +description: Linux implementation of the shared_preferences plugin +version: 0.0.1 +homepage: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux + +flutter: + plugin: + platforms: + linux: + dartPluginClass: SharedPreferencesLinux + pluginClass: none + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.8 <2.0.0" + +dependencies: + file: ^5.1.0 + flutter: + sdk: flutter + meta: ^1.0.4 + path: ^1.6.4 + path_provider: ^1.6.11 + shared_preferences_platform_interface: ^1.0.0 + +dev_dependencies: + flutter_test: + sdk: flutter + pedantic: ^1.8.0 diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart new file mode 100644 index 000000000000..8a794b1fa7c2 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart @@ -0,0 +1,74 @@ +// Copyright 2020 The Chromium 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:file/memory.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:path/path.dart' as path; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences_linux/shared_preferences_linux.dart'; + +MemoryFileSystem fs = MemoryFileSystem.test(); + +void main() { + setUp(() {}); + + tearDown(() {}); + + Future _getFilePath() async { + var directory = await getApplicationSupportDirectory(); + return path.join(directory.path, 'shared_preferences.json'); + } + + _writeTestFile(String value) async { + fs.file(await _getFilePath()) + ..createSync(recursive: true) + ..writeAsStringSync(value); + } + + Future _readTestFile() async { + return fs.file(await _getFilePath()).readAsStringSync(); + } + + SharedPreferencesLinux _getPreferences() { + var prefs = SharedPreferencesLinux(); + prefs.fs = fs; + return prefs; + } + + test('getAll', () async { + await _writeTestFile('{"key1": "one", "key2": 2}'); + var prefs = _getPreferences(); + + var values = await prefs.getAll(); + expect(values, hasLength(2)); + expect(values['key1'], 'one'); + expect(values['key2'], 2); + }); + + test('remove', () async { + await _writeTestFile('{"key1":"one","key2":2}'); + var prefs = _getPreferences(); + + await prefs.remove('key2'); + + expect(await _readTestFile(), '{"key1":"one"}'); + }); + + test('setValue', () async { + await _writeTestFile('{}'); + var prefs = _getPreferences(); + + await prefs.setValue('', 'key1', 'one'); + await prefs.setValue('', 'key2', 2); + + expect(await _readTestFile(), '{"key1":"one","key2":2}'); + }); + + test('clear', () async { + await _writeTestFile('{"key1":"one","key2":2}'); + var prefs = _getPreferences(); + + await prefs.clear(); + expect(await _readTestFile(), '{}'); + }); +} diff --git a/script/build_all_plugins_app.sh b/script/build_all_plugins_app.sh index 2c0a90693851..262c4ed7b1d3 100755 --- a/script/build_all_plugins_app.sh +++ b/script/build_all_plugins_app.sh @@ -26,6 +26,7 @@ readonly EXCLUDED_PLUGINS_LIST=( "path_provider_platform_interface" "path_provider_web" "plugin_platform_interface" + "shared_preferences_linux" "shared_preferences_macos" "shared_preferences_platform_interface" "shared_preferences_web" From 786439995653171ff305d6ceb9125c6b4d068b5b Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 1 Jul 2020 08:02:49 +0800 Subject: [PATCH 30/69] [e2e] Use SettableFuture instead of CompletableFuture (#2854) Expose a `Future` for `testResults` instead. This works better for internal use cases. --- packages/e2e/CHANGELOG.md | 4 ++++ packages/e2e/android/build.gradle | 3 ++- .../src/main/java/dev/flutter/plugins/e2e/E2EPlugin.java | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/e2e/CHANGELOG.md b/packages/e2e/CHANGELOG.md index 0d349965371b..ad2f8d3e24f0 100644 --- a/packages/e2e/CHANGELOG.md +++ b/packages/e2e/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.0 + +* **Breaking change** `E2EPlugin` exports a `Future` for `testResults`. + ## 0.5.0+1 * Fixed the device pixel ratio problem. diff --git a/packages/e2e/android/build.gradle b/packages/e2e/android/build.gradle index ec686f4b4e3d..d0bb6c5a5967 100644 --- a/packages/e2e/android/build.gradle +++ b/packages/e2e/android/build.gradle @@ -33,11 +33,12 @@ android { } dependencies { api 'junit:junit:4.12' - implementation 'net.sourceforge.streamsupport:android-retrofuture:1.7.2' // https://developer.android.com/jetpack/androidx/releases/test/#1.2.0 api 'androidx.test:runner:1.2.0' api 'androidx.test:rules:1.2.0' api 'androidx.test.espresso:espresso-core:3.2.0' + + implementation 'com.google.guava:guava:28.1-android' } } diff --git a/packages/e2e/android/src/main/java/dev/flutter/plugins/e2e/E2EPlugin.java b/packages/e2e/android/src/main/java/dev/flutter/plugins/e2e/E2EPlugin.java index f6c5b306b5d9..31100d442731 100644 --- a/packages/e2e/android/src/main/java/dev/flutter/plugins/e2e/E2EPlugin.java +++ b/packages/e2e/android/src/main/java/dev/flutter/plugins/e2e/E2EPlugin.java @@ -5,6 +5,7 @@ package dev.flutter.plugins.e2e; import android.content.Context; +import com.google.common.util.concurrent.SettableFuture; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; @@ -13,13 +14,15 @@ import io.flutter.plugin.common.MethodChannel.Result; import io.flutter.plugin.common.PluginRegistry.Registrar; import java.util.Map; -import java9.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; /** E2EPlugin */ public class E2EPlugin implements MethodCallHandler, FlutterPlugin { private MethodChannel methodChannel; - public static CompletableFuture> testResults = new CompletableFuture<>(); + private static final SettableFuture> testResultsSettable = + SettableFuture.create(); + public static final Future> testResults = testResultsSettable; private static final String CHANNEL = "plugins.flutter.io/e2e"; @@ -49,7 +52,7 @@ public void onDetachedFromEngine(FlutterPluginBinding binding) { public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("allTestsFinished")) { Map results = call.argument("results"); - testResults.complete(results); + testResultsSettable.set(results); result.success(null); } else { result.notImplemented(); From 7afa2c2c473d61739cf3848f9670109f68321269 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 30 Jun 2020 17:32:17 -0700 Subject: [PATCH 31/69] [connectivity] Endorse connectivity_for_web. (#2853) --- .../connectivity/connectivity/CHANGELOG.md | 4 +++ .../connectivity/example/lib/main.dart | 6 ++-- .../connectivity/example/web/favicon.png | Bin 0 -> 917 bytes .../example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes .../example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../connectivity/example/web/index.html | 33 ++++++++++++++++++ .../connectivity/example/web/manifest.json | 23 ++++++++++++ .../connectivity/connectivity/pubspec.yaml | 7 ++-- 8 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 packages/connectivity/connectivity/example/web/favicon.png create mode 100644 packages/connectivity/connectivity/example/web/icons/Icon-192.png create mode 100644 packages/connectivity/connectivity/example/web/icons/Icon-512.png create mode 100644 packages/connectivity/connectivity/example/web/index.html create mode 100644 packages/connectivity/connectivity/example/web/manifest.json diff --git a/packages/connectivity/connectivity/CHANGELOG.md b/packages/connectivity/connectivity/CHANGELOG.md index 9325f1a7868f..3bb777104c0e 100644 --- a/packages/connectivity/connectivity/CHANGELOG.md +++ b/packages/connectivity/connectivity/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.4.9 + +* Add support for `web` (by endorsing `connectivity_for_web` 0.3.0) + ## 0.4.8+6 * Update lower bound of dart dependency to 2.1.0. diff --git a/packages/connectivity/connectivity/example/lib/main.dart b/packages/connectivity/connectivity/example/lib/main.dart index 4ad30972679a..f8486165fa70 100644 --- a/packages/connectivity/connectivity/example/lib/main.dart +++ b/packages/connectivity/connectivity/example/lib/main.dart @@ -91,7 +91,7 @@ class _MyHomePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text('Plugin example app'), + title: const Text('Connectivity example app'), ), body: Center(child: Text('Connection Status: $_connectionStatus')), ); @@ -103,7 +103,7 @@ class _MyHomePageState extends State { String wifiName, wifiBSSID, wifiIP; try { - if (Platform.isIOS) { + if (!kIsWeb && Platform.isIOS) { LocationAuthorizationStatus status = await _connectivity.getLocationServiceAuthorization(); if (status == LocationAuthorizationStatus.notDetermined) { @@ -125,7 +125,7 @@ class _MyHomePageState extends State { } try { - if (Platform.isIOS) { + if (!kIsWeb && Platform.isIOS) { LocationAuthorizationStatus status = await _connectivity.getLocationServiceAuthorization(); if (status == LocationAuthorizationStatus.notDetermined) { diff --git a/packages/connectivity/connectivity/example/web/favicon.png b/packages/connectivity/connectivity/example/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/packages/connectivity/connectivity/example/web/icons/Icon-192.png b/packages/connectivity/connectivity/example/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/packages/connectivity/connectivity/example/web/icons/Icon-512.png b/packages/connectivity/connectivity/example/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/packages/connectivity/connectivity/example/web/index.html b/packages/connectivity/connectivity/example/web/index.html new file mode 100644 index 000000000000..9b7a438f823a --- /dev/null +++ b/packages/connectivity/connectivity/example/web/index.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + example + + + + + + + + diff --git a/packages/connectivity/connectivity/example/web/manifest.json b/packages/connectivity/connectivity/example/web/manifest.json new file mode 100644 index 000000000000..8c012917dab7 --- /dev/null +++ b/packages/connectivity/connectivity/example/web/manifest.json @@ -0,0 +1,23 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/packages/connectivity/connectivity/pubspec.yaml b/packages/connectivity/connectivity/pubspec.yaml index 9aaa2620f82c..c7ef8fe6e723 100644 --- a/packages/connectivity/connectivity/pubspec.yaml +++ b/packages/connectivity/connectivity/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/c # 0.4.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.4.8+6 +version: 0.4.9 flutter: plugin: @@ -17,13 +17,16 @@ flutter: pluginClass: FLTConnectivityPlugin macos: default_package: connectivity_macos + web: + default_package: connectivity_for_web dependencies: flutter: sdk: flutter - meta: "^1.0.5" + meta: ^1.0.5 connectivity_platform_interface: ^1.0.2 connectivity_macos: ^0.1.0 + connectivity_for_web: ^0.3.0 dev_dependencies: flutter_test: From a8a599408d216132907d7cc550bbfec3ba6ff8da Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 1 Jul 2020 22:20:34 +0800 Subject: [PATCH 32/69] [e2e] Bump version to 0.6.0 (#2855) Missed this earlier in #2854 --- packages/e2e/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/e2e/pubspec.yaml b/packages/e2e/pubspec.yaml index 0d4bc99f64f3..25eeb3ce1374 100644 --- a/packages/e2e/pubspec.yaml +++ b/packages/e2e/pubspec.yaml @@ -1,6 +1,6 @@ name: e2e description: Runs tests that use the flutter_test API as integration tests. -version: 0.5.0+1 +version: 0.6.0 homepage: https://github.com/flutter/plugins/tree/master/packages/e2e environment: From 0fdc514e8f7ffc6793a1249fa308c9f6f2d6087d Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 1 Jul 2020 15:53:53 -0700 Subject: [PATCH 33/69] [multiple] Improve video playback in image_picker example (#2819) This change adds small bugfixes to the following packages: * video_player_web: * Prevent parsing Blob URLs so Safari can play from a PickedFile. * Allow users to 'mute' videos by setting their volume to 0.0 (this enables auto-play in most modern browsers) * video_player: * Fix an issue where aspect ratio calculations failed when some of the video sizes were zero (happened in web for incorrect videos) * image_picker (example app): * Start videos in web muted so they can auto-play * Dispose video controllers when they're really not needed. This fixes a fatal crash in Safari (not so fatal in other browsers). --- .../image_picker/image_picker/CHANGELOG.md | 6 +++ .../image_picker/example/lib/main.dart | 16 +++++-- .../image_picker/image_picker/pubspec.yaml | 2 +- .../video_player/video_player/CHANGELOG.md | 4 ++ .../video_player/lib/video_player.dart | 2 +- .../video_player/video_player/pubspec.yaml | 2 +- .../video_player/test/video_player_test.dart | 42 +++++++++++++++++++ .../video_player_web/CHANGELOG.md | 5 +++ .../lib/video_player_web.dart | 21 +++++++--- .../video_player_web/pubspec.yaml | 2 +- 10 files changed, 89 insertions(+), 13 deletions(-) diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 6b85f26daf33..0cba0c8d92cb 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.6.7+3 + +* Fixes to the example app: + * Make videos in web start muted. This allows auto-play across browsers. + * Prevent the app from disposing of video controllers too early. + ## 0.6.7+2 * iOS: Fixes unpresentable album/image picker if window's root view controller is already presenting other view controller. diff --git a/packages/image_picker/image_picker/example/lib/main.dart b/packages/image_picker/image_picker/example/lib/main.dart index dff2906edf87..ece8c45d9c8e 100755 --- a/packages/image_picker/image_picker/example/lib/main.dart +++ b/packages/image_picker/image_picker/example/lib/main.dart @@ -42,6 +42,7 @@ class _MyHomePageState extends State { dynamic _pickImageError; bool isVideo = false; VideoPlayerController _controller; + VideoPlayerController _toBeDisposed; String _retrieveDataError; final ImagePicker _picker = ImagePicker(); @@ -54,10 +55,16 @@ class _MyHomePageState extends State { await _disposeVideoController(); if (kIsWeb) { _controller = VideoPlayerController.network(file.path); + // In web, most browsers won't honor a programmatic call to .play + // if the video has a sound track (and is not muted). + // Mute the video so it auto-plays in web! + // This is not needed if the call to .play is the result of user + // interaction (clicking on a "play" button, for example). + await _controller.setVolume(0.0); } else { _controller = VideoPlayerController.file(File(file.path)); + await _controller.setVolume(1.0); } - await _controller.setVolume(1.0); await _controller.initialize(); await _controller.setLooping(true); await _controller.play(); @@ -114,10 +121,11 @@ class _MyHomePageState extends State { } Future _disposeVideoController() async { - if (_controller != null) { - await _controller.dispose(); - _controller = null; + if (_toBeDisposed != null) { + await _toBeDisposed.dispose(); } + _toBeDisposed = _controller; + _controller = null; } Widget _previewVideo() { diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index 4f52b19c2659..eedecd46e1ee 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker -version: 0.6.7+2 +version: 0.6.7+3 flutter: plugin: diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 9928fe31c5a3..e3f49e736411 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.10.11+2 + +* Fix aspectRatio calculation when size.width or size.height are zero. + ## 0.10.11+1 * Post-v2 Android embedding cleanups. diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index f2f9289c1fda..a2290b2e5916 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -97,7 +97,7 @@ class VideoPlayerValue { /// Returns [size.width] / [size.height] when size is non-null, or `1.0.` when /// size is null or the aspect ratio would be less than or equal to 0.0. double get aspectRatio { - if (size == null) { + if (size == null || size.width == 0 || size.height == 0) { return 1.0; } final double aspectRatio = size.width / size.height; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index f967cd07ef7f..03f71bf4f412 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.10.11+1 +version: 0.10.11+2 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: diff --git a/packages/video_player/video_player/test/video_player_test.dart b/packages/video_player/video_player/test/video_player_test.dart index ac8459d0c9e9..ae236def4e57 100644 --- a/packages/video_player/video_player/test/video_player_test.dart +++ b/packages/video_player/video_player/test/video_player_test.dart @@ -517,6 +517,48 @@ void main() { expect(exactCopy.toString(), original.toString()); }); + + group('aspectRatio', () { + test('640x480 -> 4:3', () { + final value = VideoPlayerValue( + size: Size(640, 480), + duration: Duration(seconds: 1), + ); + expect(value.aspectRatio, 4 / 3); + }); + + test('null size -> 1.0', () { + final value = VideoPlayerValue( + size: null, + duration: Duration(seconds: 1), + ); + expect(value.aspectRatio, 1.0); + }); + + test('height = 0 -> 1.0', () { + final value = VideoPlayerValue( + size: Size(640, 0), + duration: Duration(seconds: 1), + ); + expect(value.aspectRatio, 1.0); + }); + + test('width = 0 -> 1.0', () { + final value = VideoPlayerValue( + size: Size(0, 480), + duration: Duration(seconds: 1), + ); + expect(value.aspectRatio, 1.0); + }); + + test('negative aspect ratio -> 1.0', () { + final value = VideoPlayerValue( + size: Size(640, -480), + duration: Duration(seconds: 1), + ); + expect(value.aspectRatio, 1.0); + }); + }); }); test('VideoProgressColors', () { diff --git a/packages/video_player/video_player_web/CHANGELOG.md b/packages/video_player/video_player_web/CHANGELOG.md index cd977c335d62..9c500e951122 100644 --- a/packages/video_player/video_player_web/CHANGELOG.md +++ b/packages/video_player/video_player_web/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.1.3+2 + +* Allow users to set the 'muted' attribute on video elements by setting their volume to 0. +* Do not parse URIs on 'network' videos to not break blobs (Safari). + ## 0.1.3+1 * Remove Android folder from `video_player_web`. diff --git a/packages/video_player/video_player_web/lib/video_player_web.dart b/packages/video_player/video_player_web/lib/video_player_web.dart index 039c3ce65a7e..3a849f45e0c1 100644 --- a/packages/video_player/video_player_web/lib/video_player_web.dart +++ b/packages/video_player/video_player_web/lib/video_player_web.dart @@ -66,10 +66,12 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { final int textureId = _textureCounter; _textureCounter++; - Uri uri; + String uri; switch (dataSource.sourceType) { case DataSourceType.network: - uri = Uri.parse(dataSource.uri); + // Do NOT modify the incoming uri, it can be a Blob, and Safari doesn't + // like blobs that have changed. + uri = dataSource.uri; break; case DataSourceType.asset: String assetUrl = dataSource.asset; @@ -79,7 +81,7 @@ class VideoPlayerPlugin extends VideoPlayerPlatform { // 'webOnlyAssetManager' is only in the web version of dart:ui // ignore: undefined_prefixed_name assetUrl = ui.webOnlyAssetManager.getAssetUrl(assetUrl); - uri = Uri.parse(assetUrl); + uri = assetUrl; break; case DataSourceType.file: return Future.error(UnimplementedError( @@ -145,18 +147,21 @@ class _VideoPlayer { final StreamController eventController = StreamController(); - final Uri uri; + final String uri; final int textureId; VideoElement videoElement; bool isInitialized = false; void initialize() { videoElement = VideoElement() - ..src = uri.toString() + ..src = uri ..autoplay = false ..controls = false ..style.border = 'none'; + // Allows Safari iOS to play the video inline + videoElement.setAttribute('playsinline', 'true'); + // TODO(hterkelsen): Use initialization parameters once they are available // ignore: undefined_prefixed_name ui.platformViewRegistry.registerViewFactory( @@ -218,6 +223,12 @@ class _VideoPlayer { } void setVolume(double value) { + // TODO: Do we need to expose a "muted" API? https://github.com/flutter/flutter/issues/60721 + if (value > 0.0) { + videoElement.muted = false; + } else { + videoElement.muted = true; + } videoElement.volume = value; } diff --git a/packages/video_player/video_player_web/pubspec.yaml b/packages/video_player/video_player_web/pubspec.yaml index 523d5a79a75b..891430d7483b 100644 --- a/packages/video_player/video_player_web/pubspec.yaml +++ b/packages/video_player/video_player_web/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/v # 0.1.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.1.3+1 +version: 0.1.3+2 flutter: plugin: From 1ca27a79fd686b92822ce48d5a16a0d12b9eb8b5 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Mon, 6 Jul 2020 14:42:10 -0500 Subject: [PATCH 34/69] [image_picker] updated VALID_ARCHS to support iPhone simulator (#2761) --- AUTHORS | 1 + packages/image_picker/image_picker/CHANGELOG.md | 4 ++++ packages/image_picker/image_picker/ios/image_picker.podspec | 2 +- packages/image_picker/image_picker/pubspec.yaml | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index 17ede94e79ba..51345c9a3481 100644 --- a/AUTHORS +++ b/AUTHORS @@ -58,3 +58,4 @@ Théo Champion Kazuki Yamaguchi Eitan Schwartz Chris Rutkowski +Juan Alvarez diff --git a/packages/image_picker/image_picker/CHANGELOG.md b/packages/image_picker/image_picker/CHANGELOG.md index 0cba0c8d92cb..0cec3a32b5f1 100644 --- a/packages/image_picker/image_picker/CHANGELOG.md +++ b/packages/image_picker/image_picker/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.7+4 + +* Support iOS simulator x86_64 architecture. + ## 0.6.7+3 * Fixes to the example app: diff --git a/packages/image_picker/image_picker/ios/image_picker.podspec b/packages/image_picker/image_picker/ios/image_picker.podspec index 47020f71711c..5c13cef272dd 100644 --- a/packages/image_picker/image_picker/ios/image_picker.podspec +++ b/packages/image_picker/image_picker/ios/image_picker.podspec @@ -18,7 +18,7 @@ Downloaded by pub (not CocoaPods). s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' s.platform = :ios, '8.0' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS' => 'armv7 arm64 x86_64' } + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'Tests/**/*' diff --git a/packages/image_picker/image_picker/pubspec.yaml b/packages/image_picker/image_picker/pubspec.yaml index eedecd46e1ee..06da5297d80c 100755 --- a/packages/image_picker/image_picker/pubspec.yaml +++ b/packages/image_picker/image_picker/pubspec.yaml @@ -2,7 +2,7 @@ name: image_picker description: Flutter plugin for selecting images from the Android and iOS image library, and taking new pictures with the camera. homepage: https://github.com/flutter/plugins/tree/master/packages/image_picker/image_picker -version: 0.6.7+3 +version: 0.6.7+4 flutter: plugin: From 39ba0ae0f83acbc1623b2d2e98f4b37577058d5d Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 8 Jul 2020 02:36:39 +1200 Subject: [PATCH 35/69] [url_launcher_linux] Add Linux url_launcher plugin (#2857) Adds url_launcher_linux, the federated implementation of url_launcher. Not yet endorsed by url_launcher Part of https://github.com/flutter/flutter/issues/41721 --- .ci/Dockerfile-LinuxDesktop | 8 + .../url_launcher_linux/.gitignore | 3 + .../url_launcher/url_launcher_linux/.metadata | 10 + .../url_launcher_linux/CHANGELOG.md | 2 + .../url_launcher/url_launcher_linux/LICENSE | 27 +++ .../url_launcher/url_launcher_linux/README.md | 22 ++ .../url_launcher_linux/example/.gitignore | 44 ++++ .../url_launcher_linux/example/.metadata | 10 + .../url_launcher_linux/example/README.md | 8 + .../url_launcher_linux/example/lib/main.dart | 194 ++++++++++++++++++ .../example/linux/.gitignore | 1 + .../example/linux/CMakeLists.txt | 95 +++++++++ .../example/linux/flutter/CMakeLists.txt | 86 ++++++++ .../flutter/generated_plugin_registrant.cc | 13 ++ .../flutter/generated_plugin_registrant.h | 13 ++ .../linux/flutter/generated_plugins.cmake | 16 ++ .../url_launcher_linux/example/linux/main.cc | 6 + .../example/linux/my_application.cc | 44 ++++ .../example/linux/my_application.h | 18 ++ .../url_launcher_linux/example/pubspec.yaml | 18 ++ .../example/test_driver/url_launcher_e2e.dart | 23 +++ .../test_driver/url_launcher_e2e_test.dart | 15 ++ .../url_launcher_linux/ios/.gitignore | 37 ++++ .../ios/url_launcher_linux.podspec | 22 ++ .../lib/url_launcher_linux.dart | 3 + .../url_launcher_linux/linux/CMakeLists.txt | 17 ++ .../url_launcher_linux/url_launcher_plugin.h | 31 +++ .../linux/url_launcher_plugin.cc | 148 +++++++++++++ .../url_launcher_linux/pubspec.yaml | 19 ++ 29 files changed, 953 insertions(+) create mode 100644 packages/url_launcher/url_launcher_linux/.gitignore create mode 100644 packages/url_launcher/url_launcher_linux/.metadata create mode 100644 packages/url_launcher/url_launcher_linux/CHANGELOG.md create mode 100644 packages/url_launcher/url_launcher_linux/LICENSE create mode 100644 packages/url_launcher/url_launcher_linux/README.md create mode 100644 packages/url_launcher/url_launcher_linux/example/.gitignore create mode 100644 packages/url_launcher/url_launcher_linux/example/.metadata create mode 100644 packages/url_launcher/url_launcher_linux/example/README.md create mode 100644 packages/url_launcher/url_launcher_linux/example/lib/main.dart create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/.gitignore create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/CMakeLists.txt create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/flutter/CMakeLists.txt create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/main.cc create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/my_application.cc create mode 100644 packages/url_launcher/url_launcher_linux/example/linux/my_application.h create mode 100644 packages/url_launcher/url_launcher_linux/example/pubspec.yaml create mode 100644 packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e.dart create mode 100644 packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e_test.dart create mode 100644 packages/url_launcher/url_launcher_linux/ios/.gitignore create mode 100644 packages/url_launcher/url_launcher_linux/ios/url_launcher_linux.podspec create mode 100644 packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart create mode 100644 packages/url_launcher/url_launcher_linux/linux/CMakeLists.txt create mode 100644 packages/url_launcher/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h create mode 100644 packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc create mode 100644 packages/url_launcher/url_launcher_linux/pubspec.yaml diff --git a/.ci/Dockerfile-LinuxDesktop b/.ci/Dockerfile-LinuxDesktop index 25924d4ea8d9..63e4516e26fc 100644 --- a/.ci/Dockerfile-LinuxDesktop +++ b/.ci/Dockerfile-LinuxDesktop @@ -21,3 +21,11 @@ RUN sudo apt-get install -y xvfb libegl1-mesa RUN sudo apt-get install -y clang cmake ninja-build file pkg-config # Install necessary libraries. RUN sudo apt-get install -y libgtk-3-dev + +# Add repo for Google Chrome and install it, for url_launcher tests. +RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - +RUN echo 'deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main' | sudo tee /etc/apt/sources.list.d/google-chrome.list +RUN sudo apt-get update && sudo apt-get install -y --no-install-recommends google-chrome-stable +# Make it the default so http: has a handler. +RUN sudo apt-get install -y xdg-utils +RUN xdg-settings set default-web-browser google-chrome.desktop diff --git a/packages/url_launcher/url_launcher_linux/.gitignore b/packages/url_launcher/url_launcher_linux/.gitignore new file mode 100644 index 000000000000..53e92cc4181f --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/.gitignore @@ -0,0 +1,3 @@ +.packages +.flutter-plugins +pubspec.lock diff --git a/packages/url_launcher/url_launcher_linux/.metadata b/packages/url_launcher/url_launcher_linux/.metadata new file mode 100644 index 000000000000..457a92ae1645 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/.metadata @@ -0,0 +1,10 @@ +# 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: 4b12050112afd581ddf53df848275fa681f908f3 + channel: master + +project_type: plugin diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md new file mode 100644 index 000000000000..18ba82adbe4d --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -0,0 +1,2 @@ +# 0.0.1 +* The initial implementation of url_launcher for Linux diff --git a/packages/url_launcher/url_launcher_linux/LICENSE b/packages/url_launcher/url_launcher_linux/LICENSE new file mode 100644 index 000000000000..0c91662b3f2f --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/url_launcher/url_launcher_linux/README.md b/packages/url_launcher/url_launcher_linux/README.md new file mode 100644 index 000000000000..9b16bdfad813 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/README.md @@ -0,0 +1,22 @@ +# url_launcher_linux + +The Linux implementation of [`url_launcher`][1]. + +## Usage + +### Import the package + +This package is an unendorsed Linux implementation of `url_launcher`. + +In order to use this now, you'll need to depend on `url_launcher_linux`. +When this package is endorsed it will be automatically used by the `url_launcher` package and you can switch to that API. + +```yaml +... +dependencies: + ... + url_launcher_linux: ^0.0.1 + ... +``` + +[1]: ../ diff --git a/packages/url_launcher/url_launcher_linux/example/.gitignore b/packages/url_launcher/url_launcher_linux/example/.gitignore new file mode 100644 index 000000000000..f3c205341e7d --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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 +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/url_launcher/url_launcher_linux/example/.metadata b/packages/url_launcher/url_launcher_linux/example/.metadata new file mode 100644 index 000000000000..99b1a7456d66 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/.metadata @@ -0,0 +1,10 @@ +# 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: 4b12050112afd581ddf53df848275fa681f908f3 + channel: master + +project_type: app diff --git a/packages/url_launcher/url_launcher_linux/example/README.md b/packages/url_launcher/url_launcher_linux/example/README.md new file mode 100644 index 000000000000..28dd90d71700 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/README.md @@ -0,0 +1,8 @@ +# url_launcher_example + +Demonstrates how to use the url_launcher plugin. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). diff --git a/packages/url_launcher/url_launcher_linux/example/lib/main.dart b/packages/url_launcher/url_launcher_linux/example/lib/main.dart new file mode 100644 index 000000000000..b5cce7482d07 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/lib/main.dart @@ -0,0 +1,194 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'URL Launcher', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: MyHomePage(title: 'URL Launcher'), + ); + } +} + +class MyHomePage extends StatefulWidget { + MyHomePage({Key key, this.title}) : super(key: key); + final String title; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + Future _launched; + String _phone = ''; + + Future _launchInBrowser(String url) async { + if (await canLaunch(url)) { + await launch( + url, + forceSafariVC: false, + forceWebView: false, + headers: {'my_header_key': 'my_header_value'}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewOrVC(String url) async { + if (await canLaunch(url)) { + await launch( + url, + forceSafariVC: true, + forceWebView: true, + headers: {'my_header_key': 'my_header_value'}, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewWithJavaScript(String url) async { + if (await canLaunch(url)) { + await launch( + url, + forceSafariVC: true, + forceWebView: true, + enableJavaScript: true, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchInWebViewWithDomStorage(String url) async { + if (await canLaunch(url)) { + await launch( + url, + forceSafariVC: true, + forceWebView: true, + enableDomStorage: true, + ); + } else { + throw 'Could not launch $url'; + } + } + + Future _launchUniversalLinkIos(String url) async { + if (await canLaunch(url)) { + final bool nativeAppLaunchSucceeded = await launch( + url, + forceSafariVC: false, + universalLinksOnly: true, + ); + if (!nativeAppLaunchSucceeded) { + await launch( + url, + forceSafariVC: true, + ); + } + } + } + + Widget _launchStatus(BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + return const Text(''); + } + } + + Future _makePhoneCall(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + throw 'Could not launch $url'; + } + } + + @override + Widget build(BuildContext context) { + const String toLaunch = 'https://www.cylog.org/headers/'; + return Scaffold( + appBar: AppBar( + title: Text(widget.title), + ), + body: ListView( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: TextField( + onChanged: (String text) => _phone = text, + decoration: const InputDecoration( + hintText: 'Input the phone number to launch')), + ), + RaisedButton( + onPressed: () => setState(() { + _launched = _makePhoneCall('tel:$_phone'); + }), + child: const Text('Make phone call'), + ), + const Padding( + padding: EdgeInsets.all(16.0), + child: Text(toLaunch), + ), + RaisedButton( + onPressed: () => setState(() { + _launched = _launchInBrowser(toLaunch); + }), + child: const Text('Launch in browser'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + RaisedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewOrVC(toLaunch); + }), + child: const Text('Launch in app'), + ), + RaisedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithJavaScript(toLaunch); + }), + child: const Text('Launch in app(JavaScript ON)'), + ), + RaisedButton( + onPressed: () => setState(() { + _launched = _launchInWebViewWithDomStorage(toLaunch); + }), + child: const Text('Launch in app(DOM storage ON)'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + RaisedButton( + onPressed: () => setState(() { + _launched = _launchUniversalLinkIos(toLaunch); + }), + child: const Text( + 'Launch a universal link in a native app, fallback to Safari.(Youtube)'), + ), + const Padding(padding: EdgeInsets.all(16.0)), + FutureBuilder(future: _launched, builder: _launchStatus), + ], + ), + ], + ), + ); + } +} diff --git a/packages/url_launcher/url_launcher_linux/example/linux/.gitignore b/packages/url_launcher/url_launcher_linux/example/linux/.gitignore new file mode 100644 index 000000000000..d3896c98444f --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/packages/url_launcher/url_launcher_linux/example/linux/CMakeLists.txt b/packages/url_launcher/url_launcher_linux/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..0236a8806654 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/CMakeLists.txt b/packages/url_launcher/url_launcher_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..94f43ff7fa6a --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000000..026851fa2f96 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000000..9bf7478940c1 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..1fc8ed344297 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/url_launcher/url_launcher_linux/example/linux/main.cc b/packages/url_launcher/url_launcher_linux/example/linux/main.cc new file mode 100644 index 000000000000..e7c5c5437037 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/url_launcher/url_launcher_linux/example/linux/my_application.cc b/packages/url_launcher/url_launcher_linux/example/linux/my_application.cc new file mode 100644 index 000000000000..c2357f17ea9c --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/my_application.cc @@ -0,0 +1,44 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), nullptr)); +} diff --git a/packages/url_launcher/url_launcher_linux/example/linux/my_application.h b/packages/url_launcher/url_launcher_linux/example/linux/my_application.h new file mode 100644 index 000000000000..72271d5e4170 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/url_launcher/url_launcher_linux/example/pubspec.yaml b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml new file mode 100644 index 000000000000..a4d08f30d6b4 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/pubspec.yaml @@ -0,0 +1,18 @@ +name: url_launcher_example +description: Demonstrates how to use the url_launcher plugin. + +dependencies: + flutter: + sdk: flutter + url_launcher: any + url_launcher_linux: + path: ../ + +dev_dependencies: + e2e: "^0.2.0" + flutter_driver: + sdk: flutter + pedantic: ^1.8.0 + +flutter: + uses-material-design: true diff --git a/packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e.dart b/packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e.dart new file mode 100644 index 000000000000..516835cec33b --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e.dart @@ -0,0 +1,23 @@ +// Copyright 2019, the Chromium project authors. Please see the AUTHORS file +// for details. 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_test/flutter_test.dart'; +import 'package:e2e/e2e.dart'; +import 'package:url_launcher/url_launcher.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + test('canLaunch', () async { + expect(await canLaunch('randomstring'), false); + + // Generally all devices should have some default browser. + expect(await canLaunch('http://flutter.dev'), true); + + // Desktop will not necessarily support sms:. + + // tel: and mailto: links may not be openable on every device. iOS + // simulators notably can't open these link types. + }); +} diff --git a/packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e_test.dart b/packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e_test.dart new file mode 100644 index 000000000000..1bcd0d37f450 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/example/test_driver/url_launcher_e2e_test.dart @@ -0,0 +1,15 @@ +// Copyright 2019, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter_driver/flutter_driver.dart'; + +Future main() async { + final FlutterDriver driver = await FlutterDriver.connect(); + final String result = + await driver.requestData(null, timeout: const Duration(minutes: 1)); + await driver.close(); + exit(result == 'pass' ? 0 : 1); +} diff --git a/packages/url_launcher/url_launcher_linux/ios/.gitignore b/packages/url_launcher/url_launcher_linux/ios/.gitignore new file mode 100644 index 000000000000..aa479fd3ce8a --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/ios/.gitignore @@ -0,0 +1,37 @@ +.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/flutter_export_environment.sh \ No newline at end of file diff --git a/packages/url_launcher/url_launcher_linux/ios/url_launcher_linux.podspec b/packages/url_launcher/url_launcher_linux/ios/url_launcher_linux.podspec new file mode 100644 index 000000000000..1359fd403d8d --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/ios/url_launcher_linux.podspec @@ -0,0 +1,22 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint url_launcher_linux.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'url_launcher_linux' + s.version = '0.0.1' + s.summary = 'url_launcher_linux iOS stub' + s.description = <<-DESC + No-op implementation of the Linux url_launcher plugin to avoid build issues on iOS + DESC + s.homepage = 'https://github.com/flutter/plugins' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux' } + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.swift_version = '5.0' +end diff --git a/packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart b/packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart new file mode 100644 index 000000000000..18f7af1836ce --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/lib/url_launcher_linux.dart @@ -0,0 +1,3 @@ +// The url_launcher_platform_interface defaults to MethodChannelUrlLauncher +// as its instance, which is all the Linux implementation needs. This file +// is here to silence warnings when publishing to pub. diff --git a/packages/url_launcher/url_launcher_linux/linux/CMakeLists.txt b/packages/url_launcher/url_launcher_linux/linux/CMakeLists.txt new file mode 100644 index 000000000000..1403d0cbc9e4 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/linux/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) +set(PROJECT_NAME "url_launcher_linux") +project(${PROJECT_NAME} LANGUAGES CXX) + +set(PLUGIN_NAME "${PROJECT_NAME}_plugin") + +add_library(${PLUGIN_NAME} SHARED + "url_launcher_plugin.cc" +) +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) +target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::GTK) diff --git a/packages/url_launcher/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h b/packages/url_launcher/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h new file mode 100644 index 000000000000..efcfe62e706a --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/linux/include/url_launcher_linux/url_launcher_plugin.h @@ -0,0 +1,31 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_ +#define PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_ + +// A plugin to launch URLs. + +#include + +G_BEGIN_DECLS + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +G_DECLARE_FINAL_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, FL, + URL_LAUNCHER_PLUGIN, GObject) + +FLUTTER_PLUGIN_EXPORT FlUrlLauncherPlugin* fl_url_launcher_plugin_new( + FlPluginRegistrar* registrar); + +FLUTTER_PLUGIN_EXPORT void url_launcher_plugin_register_with_registrar( + FlPluginRegistrar* registrar); + +G_END_DECLS + +#endif // PACKAGES_URL_LAUNCHER_URL_LAUNCHER_LINUX_LINUX_INCLUDE_URL_LAUNCHER_URL_LAUNCHER_PLUGIN_H_ diff --git a/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc b/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc new file mode 100644 index 000000000000..592bb965e83f --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/linux/url_launcher_plugin.cc @@ -0,0 +1,148 @@ +// Copyright 2020 The Chromium 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 "include/url_launcher_linux/url_launcher_plugin.h" + +#include +#include + +// See url_launcher_channel.dart for documentation. +const char kChannelName[] = "plugins.flutter.io/url_launcher"; +const char kBadArgumentsError[] = "Bad Arguments"; +const char kLaunchError[] = "Launch Error"; +const char kCanLaunchMethod[] = "canLaunch"; +const char kLaunchMethod[] = "launch"; +const char kUrlKey[] = "url"; + +struct _FlUrlLauncherPlugin { + GObject parent_instance; + + FlPluginRegistrar* registrar; + + // Connection to Flutter engine. + FlMethodChannel* channel; +}; + +G_DEFINE_TYPE(FlUrlLauncherPlugin, fl_url_launcher_plugin, g_object_get_type()) + +// Gets the URL from the arguments or generates an error. +static gchar* get_url(FlValue* args, GError** error) { + if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { + g_set_error(error, 0, 0, "Argument map missing or malformed"); + return nullptr; + } + FlValue* url_value = fl_value_lookup_string(args, kUrlKey); + if (url_value == nullptr) { + g_set_error(error, 0, 0, "Missing URL"); + return nullptr; + } + + return g_strdup(fl_value_get_string(url_value)); +} + +// Called to check if a URL can be launched. +static FlMethodResponse* can_launch(FlUrlLauncherPlugin* self, FlValue* args) { + g_autoptr(GError) error = nullptr; + g_autofree gchar* url = get_url(args, &error); + if (url == nullptr) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, error->message, nullptr)); + } + + gboolean is_launchable = FALSE; + g_autofree gchar* scheme = g_uri_parse_scheme(url); + if (scheme != nullptr) { + g_autoptr(GAppInfo) app_info = + g_app_info_get_default_for_uri_scheme(scheme); + is_launchable = app_info != nullptr; + } + + g_autoptr(FlValue) result = fl_value_new_bool(is_launchable); + return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); +} + +// Called when a URL should launch. +static FlMethodResponse* launch(FlUrlLauncherPlugin* self, FlValue* args) { + g_autoptr(GError) error = nullptr; + g_autofree gchar* url = get_url(args, &error); + if (url == nullptr) { + return FL_METHOD_RESPONSE(fl_method_error_response_new( + kBadArgumentsError, error->message, nullptr)); + } + + FlView* view = fl_plugin_registrar_get_view(self->registrar); + gboolean launched; + if (view != nullptr) { + GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))); + launched = gtk_show_uri_on_window(window, url, GDK_CURRENT_TIME, &error); + } else { + launched = g_app_info_launch_default_for_uri(url, nullptr, &error); + } + if (!launched) { + g_autofree gchar* message = + g_strdup_printf("Failed to launch URL: %s", error->message); + return FL_METHOD_RESPONSE( + fl_method_error_response_new(kLaunchError, message, nullptr)); + } + + g_autoptr(FlValue) result = fl_value_new_bool(TRUE); + return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); +} + +// Called when a method call is received from Flutter. +static void method_call_cb(FlMethodChannel* channel, FlMethodCall* method_call, + gpointer user_data) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(user_data); + + const gchar* method = fl_method_call_get_name(method_call); + FlValue* args = fl_method_call_get_args(method_call); + + g_autoptr(FlMethodResponse) response = nullptr; + if (strcmp(method, kCanLaunchMethod) == 0) + response = can_launch(self, args); + else if (strcmp(method, kLaunchMethod) == 0) + response = launch(self, args); + else + response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); + + g_autoptr(GError) error = nullptr; + if (!fl_method_call_respond(method_call, response, &error)) + g_warning("Failed to send method call response: %s", error->message); +} + +static void fl_url_launcher_plugin_dispose(GObject* object) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN(object); + + g_clear_object(&self->registrar); + g_clear_object(&self->channel); + + G_OBJECT_CLASS(fl_url_launcher_plugin_parent_class)->dispose(object); +} + +static void fl_url_launcher_plugin_class_init(FlUrlLauncherPluginClass* klass) { + G_OBJECT_CLASS(klass)->dispose = fl_url_launcher_plugin_dispose; +} + +FlUrlLauncherPlugin* fl_url_launcher_plugin_new(FlPluginRegistrar* registrar) { + FlUrlLauncherPlugin* self = FL_URL_LAUNCHER_PLUGIN( + g_object_new(fl_url_launcher_plugin_get_type(), nullptr)); + + self->registrar = FL_PLUGIN_REGISTRAR(g_object_ref(registrar)); + + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + self->channel = + fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), + kChannelName, FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(self->channel, method_call_cb, + g_object_ref(self), g_object_unref); + + return self; +} + +static void fl_url_launcher_plugin_init(FlUrlLauncherPlugin* self) {} + +void url_launcher_plugin_register_with_registrar(FlPluginRegistrar* registrar) { + FlUrlLauncherPlugin* plugin = fl_url_launcher_plugin_new(registrar); + g_object_unref(plugin); +} diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml new file mode 100644 index 000000000000..2662176ca9e6 --- /dev/null +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -0,0 +1,19 @@ +name: url_launcher_linux +description: Linux implementation of the url_launcher plugin. +version: 0.0.1 +homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux + +flutter: + plugin: + platforms: + linux: + pluginClass: UrlLauncherPlugin + +environment: + sdk: ">=2.1.0 <3.0.0" + flutter: ">=1.12.8 <2.0.0" + +dependencies: + flutter: + sdk: flutter + From 7391c73ae664dd99fde0352235acfa3cde7d9534 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 7 Jul 2020 11:32:38 -0700 Subject: [PATCH 36/69] [url_launcher] Endorse url_launcher_linux (#2863) Adds an endorsement for url_launcher_linux, so that Linux is supported automatically. Part of https://github.com/flutter/flutter/issues/41721 --- .../url_launcher/url_launcher/CHANGELOG.md | 6 +- .../url_launcher/example/linux/CMakeLists.txt | 95 +++++++++++++++++++ .../example/linux/flutter/CMakeLists.txt | 86 +++++++++++++++++ .../flutter/generated_plugin_registrant.cc | 13 +++ .../flutter/generated_plugin_registrant.h | 13 +++ .../linux/flutter/generated_plugins.cmake | 16 ++++ .../url_launcher/example/linux/main.cc | 6 ++ .../example/linux/my_application.cc | 44 +++++++++ .../example/linux/my_application.h | 18 ++++ .../example/test_driver/url_launcher_e2e.dart | 9 +- .../url_launcher/url_launcher/pubspec.yaml | 5 +- .../url_launcher_linux/CHANGELOG.md | 3 + .../url_launcher/url_launcher_linux/README.md | 18 +++- .../url_launcher_linux/pubspec.yaml | 2 +- script/build_all_plugins_app.sh | 1 + 15 files changed, 327 insertions(+), 8 deletions(-) create mode 100644 packages/url_launcher/url_launcher/example/linux/CMakeLists.txt create mode 100644 packages/url_launcher/url_launcher/example/linux/flutter/CMakeLists.txt create mode 100644 packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/url_launcher/url_launcher/example/linux/main.cc create mode 100644 packages/url_launcher/url_launcher/example/linux/my_application.cc create mode 100644 packages/url_launcher/url_launcher/example/linux/my_application.h diff --git a/packages/url_launcher/url_launcher/CHANGELOG.md b/packages/url_launcher/url_launcher/CHANGELOG.md index 6b1327a2da52..a5364726bee3 100644 --- a/packages/url_launcher/url_launcher/CHANGELOG.md +++ b/packages/url_launcher/url_launcher/CHANGELOG.md @@ -1,3 +1,7 @@ +## 5.5.0 + +* Support Linux by default. + ## 5.4.11 * Add documentation in README suggesting how to properly encode urls with special characters. @@ -46,7 +50,7 @@ ## 5.4.0 -* Support macos by default. +* Support macOS by default. ## 5.3.0 diff --git a/packages/url_launcher/url_launcher/example/linux/CMakeLists.txt b/packages/url_launcher/url_launcher/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..0236a8806654 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/CMakeLists.txt b/packages/url_launcher/url_launcher/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..94f43ff7fa6a --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000000..026851fa2f96 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); +} diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000000..9bf7478940c1 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..1fc8ed344297 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,16 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + url_launcher_linux +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/url_launcher/url_launcher/example/linux/main.cc b/packages/url_launcher/url_launcher/example/linux/main.cc new file mode 100644 index 000000000000..e7c5c5437037 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/url_launcher/url_launcher/example/linux/my_application.cc b/packages/url_launcher/url_launcher/example/linux/my_application.cc new file mode 100644 index 000000000000..c2357f17ea9c --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/my_application.cc @@ -0,0 +1,44 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), nullptr)); +} diff --git a/packages/url_launcher/url_launcher/example/linux/my_application.h b/packages/url_launcher/url_launcher/example/linux/my_application.h new file mode 100644 index 000000000000..72271d5e4170 --- /dev/null +++ b/packages/url_launcher/url_launcher/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/url_launcher/url_launcher/example/test_driver/url_launcher_e2e.dart b/packages/url_launcher/url_launcher/example/test_driver/url_launcher_e2e.dart index e1d75f93b326..6cf1168c81bd 100644 --- a/packages/url_launcher/url_launcher/example/test_driver/url_launcher_e2e.dart +++ b/packages/url_launcher/url_launcher/example/test_driver/url_launcher_e2e.dart @@ -2,6 +2,9 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:io' show Platform; + +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:flutter_test/flutter_test.dart'; import 'package:e2e/e2e.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -15,8 +18,10 @@ void main() { // Generally all devices should have some default browser. expect(await canLaunch('http://flutter.dev'), true); - // Generally all devices should have some default SMS app. - expect(await canLaunch('sms:5555555555'), true); + // SMS handling is available by default on most platforms. + if (kIsWeb || !Platform.isLinux) { + expect(await canLaunch('sms:5555555555'), true); + } // tel: and mailto: links may not be openable on every device. iOS // simulators notably can't open these link types. diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 08ac3f9f8b80..299de938165f 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher description: Flutter plugin for launching a URL on Android and iOS. Supports web, phone, SMS, and email schemes. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher -version: 5.4.11 +version: 5.5.0 flutter: plugin: @@ -14,6 +14,8 @@ flutter: pluginClass: FLTURLLauncherPlugin web: default_package: url_launcher_web + linux: + default_package: url_laucher_linux macos: default_package: url_laucher_macos @@ -27,6 +29,7 @@ dependencies: # TODO(amirh): Revisit this (either update this part in the design or the pub tool). # https://github.com/flutter/flutter/issues/46264 url_launcher_web: ^0.1.0+1 + url_launcher_linux: ^0.0.1 url_launcher_macos: ^0.0.1 dev_dependencies: diff --git a/packages/url_launcher/url_launcher_linux/CHANGELOG.md b/packages/url_launcher/url_launcher_linux/CHANGELOG.md index 18ba82adbe4d..e03b1d387c55 100644 --- a/packages/url_launcher/url_launcher_linux/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_linux/CHANGELOG.md @@ -1,2 +1,5 @@ +# 0.0.1+1 +* README update for endorsement by url_launcher. + # 0.0.1 * The initial implementation of url_launcher for Linux diff --git a/packages/url_launcher/url_launcher_linux/README.md b/packages/url_launcher/url_launcher_linux/README.md index 9b16bdfad813..0474c58da40e 100644 --- a/packages/url_launcher/url_launcher_linux/README.md +++ b/packages/url_launcher/url_launcher_linux/README.md @@ -6,10 +6,22 @@ The Linux implementation of [`url_launcher`][1]. ### Import the package -This package is an unendorsed Linux implementation of `url_launcher`. +This package has been endorsed, meaning that you only need to add `url_launcher` +as a dependency in your `pubspec.yaml`. It will be automatically included in your app +when you depend on `package:url_launcher`. -In order to use this now, you'll need to depend on `url_launcher_linux`. -When this package is endorsed it will be automatically used by the `url_launcher` package and you can switch to that API. +This is what the above means to your `pubspec.yaml`: + +```yaml +... +dependencies: + ... + url_launcher: ^5.5.0 + ... +``` + +If you wish to use the Linux package only, you can add `url_launcher_linux` as a +dependency: ```yaml ... diff --git a/packages/url_launcher/url_launcher_linux/pubspec.yaml b/packages/url_launcher/url_launcher_linux/pubspec.yaml index 2662176ca9e6..74c2968aba69 100644 --- a/packages/url_launcher/url_launcher_linux/pubspec.yaml +++ b/packages/url_launcher/url_launcher_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: url_launcher_linux description: Linux implementation of the url_launcher plugin. -version: 0.0.1 +version: 0.0.1+1 homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_linux flutter: diff --git a/script/build_all_plugins_app.sh b/script/build_all_plugins_app.sh index 262c4ed7b1d3..961cecce7903 100755 --- a/script/build_all_plugins_app.sh +++ b/script/build_all_plugins_app.sh @@ -31,6 +31,7 @@ readonly EXCLUDED_PLUGINS_LIST=( "shared_preferences_platform_interface" "shared_preferences_web" "shared_preferences_windows" + "url_launcher_linux" "url_launcher_macos" "url_launcher_platform_interface" "url_launcher_web" From 00efa43e5faefed597a9c0cc364c71864210b2d5 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 7 Jul 2020 18:33:49 -0700 Subject: [PATCH 37/69] [shared_preferences_linux] Add iOS stub (#2865) Add missing iOS stub to shared_preferences_linux --- .../shared_preferences_linux/CHANGELOG.md | 3 +++ .../ios/shared_preferences_linux.podspec | 22 +++++++++++++++++++ .../shared_preferences_linux/pubspec.yaml | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 packages/shared_preferences/shared_preferences_linux/ios/shared_preferences_linux.podspec diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index 11694802aacf..de171a9fcf80 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,2 +1,5 @@ +## 0.0.2 +* Add iOS stub. + ## 0.0.1 * Initial release to support shared_preferences on Linux. diff --git a/packages/shared_preferences/shared_preferences_linux/ios/shared_preferences_linux.podspec b/packages/shared_preferences/shared_preferences_linux/ios/shared_preferences_linux.podspec new file mode 100644 index 000000000000..8f4d3cdddcd5 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/ios/shared_preferences_linux.podspec @@ -0,0 +1,22 @@ +# +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. +# Run `pod lib lint shared_preferences_launcher_linux.podspec' to validate before publishing. +# +Pod::Spec.new do |s| + s.name = 'shared_preferences_linux' + s.version = '0.0.1' + s.summary = 'shared_preferences_linux iOS stub' + s.description = <<-DESC + No-op implementation of the Linux shared_preferences plugin to avoid build issues on iOS + DESC + s.homepage = 'https://github.com/flutter/plugins' + s.license = { :type => 'BSD', :file => '../LICENSE' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :http => 'https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux' } + s.dependency 'Flutter' + s.platform = :ios, '8.0' + + # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } + s.swift_version = '5.0' +end diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 2539b937e006..95c3aca71237 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin -version: 0.0.1 +version: 0.0.2 homepage: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux flutter: From 8e1dcad52085e13ce04cf94fbe66f5aa11760491 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Tue, 7 Jul 2020 21:00:58 -0700 Subject: [PATCH 38/69] [shared_preferences] Shared preferences linux endorsement (#2864) Endorses shared_preferences_linux. Since shared_preferences_linux is pure Dart, this uses manual registration of the Linux implementation. It is similar to the solution used for path_provider, but improves on it by more narrowly tailoring the registration to cases where another registration hasn't already been done, and only checking the first time, thus avoiding the need for a variable to override it. Updates shared_preferences_linux to depend on path_provider_linux rather than path_provider, to avoid adding unnecessary native plugin dependencies to other platforms. Also adds a driver test for shared_preference_linux, which I missed in reviewing the initial shared_preferences_linux landing. --- .../shared_preferences/CHANGELOG.md | 4 + .../shared_preferences/README.md | 8 +- .../lib/shared_preferences.dart | 25 ++++- .../shared_preferences/pubspec.yaml | 5 +- .../shared_preferences_linux/CHANGELOG.md | 3 + .../example/linux/CMakeLists.txt | 95 +++++++++++++++++++ .../example/linux/flutter/CMakeLists.txt | 86 +++++++++++++++++ .../flutter/generated_plugin_registrant.cc | 9 ++ .../flutter/generated_plugin_registrant.h | 13 +++ .../linux/flutter/generated_plugins.cmake | 15 +++ .../example/linux/main.cc | 6 ++ .../example/linux/my_application.cc | 44 +++++++++ .../example/linux/my_application.h | 18 ++++ .../example/pubspec.yaml | 11 +++ .../test_driver/shared_preferences_e2e.dart | 89 +++++++++++++++++ .../shared_preferences_e2e_test.dart | 15 +++ .../lib/shared_preferences_linux.dart | 9 +- .../shared_preferences_linux/pubspec.yaml | 4 +- .../test/shared_preferences_linux_test.dart | 13 ++- 19 files changed, 454 insertions(+), 18 deletions(-) create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/CMakeLists.txt create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/flutter/CMakeLists.txt create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/main.cc create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/my_application.cc create mode 100644 packages/shared_preferences/shared_preferences_linux/example/linux/my_application.h create mode 100644 packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e.dart create mode 100644 packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e_test.dart diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index 8c7ee24dfa6b..d17bbb96289b 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.5.8 + +* Support Linux by default. + ## 0.5.7+3 * Post-v2 Android embedding cleanup. diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index b5bf4050a8fd..9ccac0ac49ae 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -2,10 +2,10 @@ [![pub package](https://img.shields.io/pub/v/shared_preferences.svg)](https://pub.dartlang.org/packages/shared_preferences) -Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing -a persistent store for simple data. Data is persisted to disk asynchronously. -Neither platform can guarantee that writes will be persisted to disk after -returning and this plugin must not be used for storing critical data. +Wraps platform-specific persistent storage for simple data +(NSUserDefaults on iOS and macOS, SharedPreferences on Android, etc.). Data may be persisted to disk asynchronously, +and there is no guarantee that writes will be persisted to disk after +returning, so this plugin must not be used for storing critical data. **Please set your constraint to `shared_preferences: '>=0.5.y+x <2.0.0'`** diff --git a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart index 62160dee20fd..b8d3452a0a0e 100644 --- a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart +++ b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart @@ -3,10 +3,14 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io' show Platform; +import 'package:flutter/foundation.dart' show kIsWeb; import 'package:meta/meta.dart'; +import 'package:shared_preferences_linux/shared_preferences_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; /// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing /// a persistent store for simple data. @@ -17,9 +21,26 @@ class SharedPreferences { static const String _prefix = 'flutter.'; static Completer _completer; + static bool _manualDartRegistrationNeeded = true; + + static SharedPreferencesStorePlatform get _store { + // This is to manually endorse the Linux implementation until automatic + // registration of dart plugins is implemented. For details see + // https://github.com/flutter/flutter/issues/52267. + if (_manualDartRegistrationNeeded) { + // Only do the initial registration if it hasn't already been overridden + // with a non-default instance. + if (!kIsWeb && + Platform.isLinux && + SharedPreferencesStorePlatform.instance + is MethodChannelSharedPreferencesStore) { + SharedPreferencesStorePlatform.instance = SharedPreferencesLinux(); + } + _manualDartRegistrationNeeded = false; + } - static SharedPreferencesStorePlatform get _store => - SharedPreferencesStorePlatform.instance; + return SharedPreferencesStorePlatform.instance; + } /// Loads and parses the [SharedPreferences] for this app from disk. /// diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 2b278248745a..ca8c443cd753 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/shared_prefere # 0.5.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.5.7+3 +version: 0.5.8 flutter: plugin: @@ -15,6 +15,8 @@ flutter: pluginClass: SharedPreferencesPlugin ios: pluginClass: FLTSharedPreferencesPlugin + linux: + default_package: shared_preferences_linux macos: default_package: shared_preferences_macos web: @@ -30,6 +32,7 @@ dependencies: # validation, so we set a ^ constraint. # TODO(franciscojma): Revisit this (either update this part in the design or the pub tool). # https://github.com/flutter/flutter/issues/46264 + shared_preferences_linux: ^0.0.2 shared_preferences_macos: ^0.0.1 shared_preferences_web: ^0.1.2 diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index de171a9fcf80..353a921ff281 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,3 +1,6 @@ +## 0.0.2+1 +* Replace path_provider dependency with path_provider_linux. + ## 0.0.2 * Add iOS stub. diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/CMakeLists.txt b/packages/shared_preferences/shared_preferences_linux/example/linux/CMakeLists.txt new file mode 100644 index 000000000000..0236a8806654 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +# Application build +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) +apply_standard_settings(${BINARY_NAME}) +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/CMakeLists.txt b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000000..94f43ff7fa6a --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/CMakeLists.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + linux-x64 ${CMAKE_BUILD_TYPE} +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000000..d38195aa0412 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,9 @@ +// +// Generated file. Do not edit. +// + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000000..9bf7478940c1 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,13 @@ +// +// Generated file. Do not edit. +// + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake new file mode 100644 index 000000000000..51436ae8c982 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/flutter/generated_plugins.cmake @@ -0,0 +1,15 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/main.cc b/packages/shared_preferences/shared_preferences_linux/example/linux/main.cc new file mode 100644 index 000000000000..e7c5c5437037 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/my_application.cc b/packages/shared_preferences/shared_preferences_linux/example/linux/my_application.cc new file mode 100644 index 000000000000..c2357f17ea9c --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/my_application.cc @@ -0,0 +1,44 @@ +#include "my_application.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "example"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), nullptr)); +} diff --git a/packages/shared_preferences/shared_preferences_linux/example/linux/my_application.h b/packages/shared_preferences/shared_preferences_linux/example/linux/my_application.h new file mode 100644 index 000000000000..72271d5e4170 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index 1c0624035c54..8e726d642274 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -4,11 +4,22 @@ description: Demonstrates how to use the shared_preferences_linux plugin. dependencies: flutter: sdk: flutter + shared_preferences: any shared_preferences_linux: ^0.1.0 dependency_overrides: shared_preferences_linux: path: ../ + # Remove this override once the endorsement is published. + shared_preferences: + path: ../../shared_preferences/ + +dev_dependencies: + flutter_driver: + sdk: flutter + test: any + e2e: ^0.2.0 + pedantic: ^1.8.0 flutter: uses-material-design: true diff --git a/packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e.dart b/packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e.dart new file mode 100644 index 000000000000..b693df2131ed --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e.dart @@ -0,0 +1,89 @@ +import 'dart:async'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:e2e/e2e.dart'; + +void main() { + E2EWidgetsFlutterBinding.ensureInitialized(); + + group('$SharedPreferences', () { + const Map kTestValues = { + 'flutter.String': 'hello world', + 'flutter.bool': true, + 'flutter.int': 42, + 'flutter.double': 3.14159, + 'flutter.List': ['foo', 'bar'], + }; + + const Map kTestValues2 = { + 'flutter.String': 'goodbye world', + 'flutter.bool': false, + 'flutter.int': 1337, + 'flutter.double': 2.71828, + 'flutter.List': ['baz', 'quox'], + }; + + SharedPreferences preferences; + + setUp(() async { + preferences = await SharedPreferences.getInstance(); + }); + + tearDown(() { + preferences.clear(); + }); + + test('reading', () async { + expect(preferences.get('String'), isNull); + expect(preferences.get('bool'), isNull); + expect(preferences.get('int'), isNull); + expect(preferences.get('double'), isNull); + expect(preferences.get('List'), isNull); + expect(preferences.getString('String'), isNull); + expect(preferences.getBool('bool'), isNull); + expect(preferences.getInt('int'), isNull); + expect(preferences.getDouble('double'), isNull); + expect(preferences.getStringList('List'), isNull); + }); + + test('writing', () async { + await Future.wait(>[ + preferences.setString('String', kTestValues2['flutter.String']), + preferences.setBool('bool', kTestValues2['flutter.bool']), + preferences.setInt('int', kTestValues2['flutter.int']), + preferences.setDouble('double', kTestValues2['flutter.double']), + preferences.setStringList('List', kTestValues2['flutter.List']) + ]); + expect(preferences.getString('String'), kTestValues2['flutter.String']); + expect(preferences.getBool('bool'), kTestValues2['flutter.bool']); + expect(preferences.getInt('int'), kTestValues2['flutter.int']); + expect(preferences.getDouble('double'), kTestValues2['flutter.double']); + expect(preferences.getStringList('List'), kTestValues2['flutter.List']); + }); + + test('removing', () async { + const String key = 'testKey'; + await preferences.setString(key, kTestValues['flutter.String']); + await preferences.setBool(key, kTestValues['flutter.bool']); + await preferences.setInt(key, kTestValues['flutter.int']); + await preferences.setDouble(key, kTestValues['flutter.double']); + await preferences.setStringList(key, kTestValues['flutter.List']); + await preferences.remove(key); + expect(preferences.get('testKey'), isNull); + }); + + test('clearing', () async { + await preferences.setString('String', kTestValues['flutter.String']); + await preferences.setBool('bool', kTestValues['flutter.bool']); + await preferences.setInt('int', kTestValues['flutter.int']); + await preferences.setDouble('double', kTestValues['flutter.double']); + await preferences.setStringList('List', kTestValues['flutter.List']); + await preferences.clear(); + expect(preferences.getString('String'), null); + expect(preferences.getBool('bool'), null); + expect(preferences.getInt('int'), null); + expect(preferences.getDouble('double'), null); + expect(preferences.getStringList('List'), null); + }); + }); +} diff --git a/packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e_test.dart b/packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e_test.dart new file mode 100644 index 000000000000..f3aa9e218d82 --- /dev/null +++ b/packages/shared_preferences/shared_preferences_linux/example/test_driver/shared_preferences_e2e_test.dart @@ -0,0 +1,15 @@ +// Copyright 2019, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:async'; +import 'dart:io'; +import 'package:flutter_driver/flutter_driver.dart'; + +Future main() async { + final FlutterDriver driver = await FlutterDriver.connect(); + final String result = + await driver.requestData(null, timeout: const Duration(minutes: 1)); + await driver.close(); + exit(result == 'pass' ? 0 : 1); +} diff --git a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart index dc93100c3277..c975ad1a7544 100644 --- a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart +++ b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -4,11 +4,12 @@ import 'dart:async'; import 'dart:convert' show json; + import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as path; -import 'package:path_provider/path_provider.dart'; +import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; /// The Linux implementation of [SharedPreferencesStorePlatform]. @@ -27,9 +28,9 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { /// Gets the file where the preferences are stored. Future _getLocalDataFile() async { - var directory = await getApplicationSupportDirectory(); - var filePath = path.join(directory.path, 'shared_preferences.json'); - return fs.file(filePath); + final pathProvider = PathProviderLinux(); + final directory = await pathProvider.getApplicationSupportPath(); + return fs.file(path.join(directory, 'shared_preferences.json')); } /// Gets the preferences from the stored file. Once read, the preferences are diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 95c3aca71237..3d40d39241ac 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -1,6 +1,6 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin -version: 0.0.2 +version: 0.0.2+1 homepage: https://github.com/flutter/plugins/tree/master/packages/shared_preferences/shared_preferences_linux flutter: @@ -20,7 +20,7 @@ dependencies: sdk: flutter meta: ^1.0.4 path: ^1.6.4 - path_provider: ^1.6.11 + path_provider_linux: ^0.0.1 shared_preferences_platform_interface: ^1.0.0 dev_dependencies: diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart index 8a794b1fa7c2..8c659f212aa5 100644 --- a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart @@ -4,19 +4,22 @@ import 'package:file/memory.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as path; -import 'package:path_provider/path_provider.dart'; +import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; -MemoryFileSystem fs = MemoryFileSystem.test(); +MemoryFileSystem fs; void main() { - setUp(() {}); + setUp(() { + fs = MemoryFileSystem.test(); + }); tearDown(() {}); Future _getFilePath() async { - var directory = await getApplicationSupportDirectory(); - return path.join(directory.path, 'shared_preferences.json'); + final pathProvider = PathProviderLinux(); + final directory = await pathProvider.getApplicationSupportPath(); + return path.join(directory, 'shared_preferences.json'); } _writeTestFile(String value) async { From 9fdcb05b1b0254243c37a10a4d6e09b16602560e Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Mon, 13 Jul 2020 15:45:57 -0700 Subject: [PATCH 39/69] [connectivity_for_web] Fix JS Interop in release mode. (#2869) * Switch to dart:html window.navigator.connection instead of package:js JS-interop. * Overwrite connection.onchange instead of listening to connection.onChange Stream (prevents multiple subscriptions after hot-reload). * Cleaned up old code related to generating the JS facade. --- .../connectivity_for_web/CHANGELOG.md | 4 + .../generated/network_information_types.dart | 78 ------------ ...k_information_api_connectivity_plugin.dart | 39 ++++-- .../lib/src/utils/connectivity_result.dart | 17 +-- .../connectivity_for_web/pubspec.yaml | 4 +- .../connectivity_for_web/test/lib/main.dart | 21 ++-- .../test/lib/src/connectivity_mocks.dart | 53 ++------ .../connectivity_for_web/test/pubspec.yaml | 3 +- .../connectivity_for_web/ts/.gitignore | 2 - .../connectivity_for_web/ts/README.md | 25 ---- .../connectivity_for_web/ts/package-lock.json | 117 ------------------ .../connectivity_for_web/ts/package.json | 17 --- .../ts/scripts/run_facade_gen.sh | 18 --- 13 files changed, 61 insertions(+), 337 deletions(-) delete mode 100644 packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart delete mode 100644 packages/connectivity/connectivity_for_web/ts/.gitignore delete mode 100644 packages/connectivity/connectivity_for_web/ts/README.md delete mode 100644 packages/connectivity/connectivity_for_web/ts/package-lock.json delete mode 100644 packages/connectivity/connectivity_for_web/ts/package.json delete mode 100755 packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh diff --git a/packages/connectivity/connectivity_for_web/CHANGELOG.md b/packages/connectivity/connectivity_for_web/CHANGELOG.md index 89e186abe1cb..83dc386a0314 100644 --- a/packages/connectivity/connectivity_for_web/CHANGELOG.md +++ b/packages/connectivity/connectivity_for_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.1 + +* Use NetworkInformation API from dart:html, instead of the JS-interop version. + ## 0.3.0 * Rename from "experimental_connectivity_web" to "connectivity_for_web", and move to flutter/plugins master. diff --git a/packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart b/packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart deleted file mode 100644 index c4045b3ec1fc..000000000000 --- a/packages/connectivity/connectivity_for_web/lib/src/generated/network_information_types.dart +++ /dev/null @@ -1,78 +0,0 @@ -@JS() -library network_information_types; - -import "package:js/js.dart"; -import "dart:html" show EventListener, EventTarget; - -/// W3C Spec Draft http://wicg.github.io/netinfo/ -/// Edition: Draft Community Group Report 20 February 2019 - -/// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface -@anonymous -@JS() -abstract class Navigator implements NavigatorNetworkInformation {} - -@anonymous -@JS() -abstract class WorkerNavigator implements NavigatorNetworkInformation { - external factory WorkerNavigator({NetworkInformation connection}); -} - -/// http://wicg.github.io/netinfo/#navigatornetworkinformation-interface -@anonymous -@JS() -abstract class NavigatorNetworkInformation { - external NetworkInformation get connection; - external factory NavigatorNetworkInformation({NetworkInformation connection}); -} - -/// http://wicg.github.io/netinfo/#connection-types -/*type ConnectionType = - | 'bluetooth' - | 'cellular' - | 'ethernet' - | 'mixed' - | 'none' - | 'other' - | 'unknown' - | 'wifi' - | 'wimax'; -*/ - -/// http://wicg.github.io/netinfo/#effectiveconnectiontype-enum -/*type EffectiveConnectionType = '2g' | '3g' | '4g' | 'slow-2g';*/ - -/// http://wicg.github.io/netinfo/#dom-megabit -/*type Megabit = number;*/ -/// http://wicg.github.io/netinfo/#dom-millisecond -/*type Millisecond = number;*/ - -/// http://wicg.github.io/netinfo/#networkinformation-interface -@anonymous -@JS() -abstract class NetworkInformation implements EventTarget { - /// http://wicg.github.io/netinfo/#type-attribute - external String /*'bluetooth'|'cellular'|'ethernet'|'mixed'|'none'|'other'|'unknown'|'wifi'|'wimax'*/ get type; - - /// http://wicg.github.io/netinfo/#effectivetype-attribute - external String /*'2g'|'3g'|'4g'|'slow-2g'*/ get effectiveType; - - /// http://wicg.github.io/netinfo/#downlinkmax-attribute - external num get downlinkMax; - - /// http://wicg.github.io/netinfo/#downlink-attribute - external num get downlink; - - /// http://wicg.github.io/netinfo/#rtt-attribute - external num get rtt; - - /// http://wicg.github.io/netinfo/#savedata-attribute - external bool get saveData; - - /// http://wicg.github.io/netinfo/#handling-changes-to-the-underlying-connection - external EventListener get onchange; - external set onchange(EventListener v); -} - -@JS() -external Navigator get navigator; diff --git a/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart b/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart index d88487b9c406..99bac2ab8d30 100644 --- a/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart +++ b/packages/connectivity/connectivity_for_web/lib/src/network_information_api_connectivity_plugin.dart @@ -1,28 +1,29 @@ import 'dart:async'; +import 'dart:html' as html show window, NetworkInformation; +import 'dart:js'; +import 'dart:js_util'; import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; import 'package:connectivity_for_web/connectivity_for_web.dart'; import 'package:flutter/foundation.dart'; -import 'package:js/js.dart'; -import 'generated/network_information_types.dart' as dom; import 'utils/connectivity_result.dart'; /// The web implementation of the ConnectivityPlatform of the Connectivity plugin. class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin { - final dom.NetworkInformation _networkInformation; + final html.NetworkInformation _networkInformation; /// A check to determine if this version of the plugin can be used. - static bool isSupported() => dom.navigator?.connection != null; + static bool isSupported() => html.window.navigator.connection != null; /// The constructor of the plugin. NetworkInformationApiConnectivityPlugin() - : this.withConnection(dom.navigator?.connection); + : this.withConnection(html.window.navigator.connection); /// Creates the plugin, with an override of the NetworkInformation object. @visibleForTesting NetworkInformationApiConnectivityPlugin.withConnection( - dom.NetworkInformation connection) + html.NetworkInformation connection) : _networkInformation = connection; /// Checks the connection status of the device. @@ -31,18 +32,30 @@ class NetworkInformationApiConnectivityPlugin extends ConnectivityPlugin { return networkInformationToConnectivityResult(_networkInformation); } - StreamController _connectivityResult; + StreamController _connectivityResultStreamController; + Stream _connectivityResultStream; /// Returns a Stream of ConnectivityResults changes. @override Stream get onConnectivityChanged { - if (_connectivityResult == null) { - _connectivityResult = StreamController(); - _networkInformation.onchange = allowInterop((_) { - _connectivityResult + if (_connectivityResultStreamController == null) { + _connectivityResultStreamController = + StreamController(); + setProperty(_networkInformation, 'onchange', allowInterop((_) { + _connectivityResultStreamController .add(networkInformationToConnectivityResult(_networkInformation)); - }); + })); + // TODO: Implement the above with _networkInformation.onChange: + // _networkInformation.onChange.listen((_) { + // _connectivityResult + // .add(networkInformationToConnectivityResult(_networkInformation)); + // }); + // Once we can detect when to *cancel* a subscription to the _networkInformation + // onChange Stream upon hot restart. + // https://github.com/dart-lang/sdk/issues/42679 + _connectivityResultStream = + _connectivityResultStreamController.stream.asBroadcastStream(); } - return _connectivityResult.stream; + return _connectivityResultStream; } } diff --git a/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart b/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart index 28943ef5c7e1..efefd8d52440 100644 --- a/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart +++ b/packages/connectivity/connectivity_for_web/lib/src/utils/connectivity_result.dart @@ -1,27 +1,22 @@ +import 'dart:html' as html show NetworkInformation; import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; /// Converts an incoming NetworkInformation object into the correct ConnectivityResult. -// -// We can't be more specific on the signature of this method because the API is odd, -// data can come from a static value in the DOM, or as the 'target' of a DOM Event. -// -// If we type info as `NetworkInformation`, Dart will complain with: -// "Uncaught Error: Expected a value of type 'NetworkInformation', -// but got one of type 'NetworkInformation'" ConnectivityResult networkInformationToConnectivityResult( - dynamic /* NetworkInformation */ info) { + html.NetworkInformation info, +) { if (info == null) { return ConnectivityResult.none; } if (info.downlink == 0 && info.rtt == 0) { return ConnectivityResult.none; } - if (info.type != null) { - return _typeToConnectivityResult(info.type); - } if (info.effectiveType != null) { return _effectiveTypeToConnectivityResult(info.effectiveType); } + if (info.type != null) { + return _typeToConnectivityResult(info.type); + } return ConnectivityResult.none; } diff --git a/packages/connectivity/connectivity_for_web/pubspec.yaml b/packages/connectivity/connectivity_for_web/pubspec.yaml index e4a1673e40c2..e1142a75c91f 100644 --- a/packages/connectivity/connectivity_for_web/pubspec.yaml +++ b/packages/connectivity/connectivity_for_web/pubspec.yaml @@ -1,6 +1,6 @@ name: connectivity_for_web description: An implementation for the web platform of the Flutter `connectivity` plugin. This uses the NetworkInformation Web API, with a fallback to Navigator.onLine. -version: 0.3.0 +version: 0.3.1 homepage: https://github.com/ditman/plugins/tree/connectivity-web/packages/connectivity/experimental_connectivity_web flutter: @@ -12,7 +12,6 @@ flutter: dependencies: connectivity_platform_interface: ^1.0.3 - js: ^0.6.1+1 flutter_web_plugins: sdk: flutter flutter: @@ -25,6 +24,7 @@ dev_dependencies: flutter_test: sdk: flutter e2e: ^0.2.4+3 + mockito: ^4.1.1 environment: sdk: ">=2.6.0 <3.0.0" diff --git a/packages/connectivity/connectivity_for_web/test/lib/main.dart b/packages/connectivity/connectivity_for_web/test/lib/main.dart index 21621a947ee6..93b9a73cf31b 100644 --- a/packages/connectivity/connectivity_for_web/test/lib/main.dart +++ b/packages/connectivity/connectivity_for_web/test/lib/main.dart @@ -3,6 +3,8 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:connectivity_platform_interface/connectivity_platform_interface.dart'; import 'package:connectivity_for_web/src/network_information_api_connectivity_plugin.dart'; +import 'package:mockito/mockito.dart'; + import 'src/connectivity_mocks.dart'; void main() { @@ -16,11 +18,12 @@ void main() { num rtt = 50, ConnectivityResult expected, }) { - MockNetworkInformation connection = MockNetworkInformation( - type: type, - effectiveType: effectiveType, - downlink: downlink, - rtt: rtt); + final connection = MockNetworkInformation(); + when(connection.type).thenReturn(type); + when(connection.effectiveType).thenReturn(effectiveType); + when(connection.downlink).thenReturn(downlink); + when(connection.rtt).thenReturn(downlink); + NetworkInformationApiConnectivityPlugin plugin = NetworkInformationApiConnectivityPlugin.withConnection(connection); expect(plugin.checkConnectivity(), completion(equals(expected))); @@ -53,16 +56,16 @@ void main() { group('get onConnectivityChanged', () { test('puts change events in a Stream', () async { - MockNetworkInformation connection = - MockNetworkInformation(effectiveType: '4g', downlink: 10, rtt: 50); + final connection = MockNetworkInformation(); NetworkInformationApiConnectivityPlugin plugin = NetworkInformationApiConnectivityPlugin.withConnection(connection); Stream results = plugin.onConnectivityChanged; // Fake a disconnect-reconnect - connection.mockChangeValue(downlink: 0, rtt: 0); - connection.mockChangeValue(downlink: 10, rtt: 50); + await connection.mockChangeValue(downlink: 0, rtt: 0); + await connection.mockChangeValue( + downlink: 10, rtt: 50, effectiveType: '4g'); // The stream of results is infinite, so we need to .take(2) for this test to complete. expect( diff --git a/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart b/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart index 9ce2e811d461..7b82b512065b 100644 --- a/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart +++ b/packages/connectivity/connectivity_for_web/test/lib/src/connectivity_mocks.dart @@ -1,33 +1,12 @@ import 'dart:html'; -import 'package:connectivity_for_web/src/generated/network_information_types.dart' - as dom; +import 'package:mockito/mockito.dart'; /// A Mock implementation of the NetworkInformation API that allows /// for external modification of its values. -class MockNetworkInformation extends dom.NetworkInformation { - @override - String type; - - @override - String effectiveType; - - @override - num downlink; - - @override - num rtt; - - @override - EventListener onchange; - - /// Constructor of mocked instances... - MockNetworkInformation({ - this.type, - this.effectiveType, - this.downlink, - this.rtt, - }); +class MockNetworkInformation extends Mock implements NetworkInformation { + /// The callback that will fire after the network information values change. + Function onchange; /// Changes the desired values, and triggers the change event listener. void mockChangeValue({ @@ -35,26 +14,12 @@ class MockNetworkInformation extends dom.NetworkInformation { String effectiveType, num downlink, num rtt, - }) { - this.type = type ?? this.type; - this.effectiveType = effectiveType ?? this.effectiveType; - this.downlink = downlink ?? this.downlink; - this.rtt = rtt ?? this.rtt; + }) async { + when(this.type).thenAnswer((_) => type); + when(this.effectiveType).thenAnswer((_) => effectiveType); + when(this.downlink).thenAnswer((_) => downlink); + when(this.rtt).thenAnswer((_) => rtt); onchange(Event('change')); } - - @override - void addEventListener(String type, listener, [bool useCapture]) {} - - @override - bool dispatchEvent(Event event) { - return true; - } - - @override - Events get on => null; - - @override - void removeEventListener(String type, listener, [bool useCapture]) {} } diff --git a/packages/connectivity/connectivity_for_web/test/pubspec.yaml b/packages/connectivity/connectivity_for_web/test/pubspec.yaml index 4d7d10a775e2..44f4b552b443 100644 --- a/packages/connectivity/connectivity_for_web/test/pubspec.yaml +++ b/packages/connectivity/connectivity_for_web/test/pubspec.yaml @@ -1,6 +1,6 @@ name: connectivity_web_example description: Example web app for the connectivity plugin -version: 0.1.0 +version: 0.1.1 homepage: https://github.com/flutter/plugins/tree/master/packages/connectivity/connectivity_web dependencies: @@ -18,6 +18,7 @@ dev_dependencies: flutter_driver: sdk: flutter e2e: ^0.2.4+3 + mockito: ^4.1.1 environment: sdk: ">=2.6.0 <3.0.0" diff --git a/packages/connectivity/connectivity_for_web/ts/.gitignore b/packages/connectivity/connectivity_for_web/ts/.gitignore deleted file mode 100644 index de4d1f007dd1..000000000000 --- a/packages/connectivity/connectivity_for_web/ts/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dist -node_modules diff --git a/packages/connectivity/connectivity_for_web/ts/README.md b/packages/connectivity/connectivity_for_web/ts/README.md deleted file mode 100644 index 3372ad2f3790..000000000000 --- a/packages/connectivity/connectivity_for_web/ts/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# JS Facade generator - -This npm script takes the `network-information-types` npm package, and runs it through Dart's `dart_js_facade_gen` to auto-generate (most) of the JS facades used by this plugin. - -The process is not completely automated yet, but it should be pretty close. - -To generate the facades, and after [installing `npm`](https://www.npmjs.com/get-npm), do: - -``` -npm install -npm run build -``` - -The above will fetch the required dependencies, and generate a `dist/network_information_types.dart` file that you can use with the plugin. - -``` -cp dist/*.dart ../lib/src/generated -``` - -This script should come handy once the Network Information Web API changes, or becomes stable, so the JS-interop part of this plugin can be regenerated more easily. - -Read more: - -* [Dart JS Interop](https://dart.dev/web/js-interop) -* [dart_js_facade_gen](https://www.npmjs.com/package/dart_js_facade_gen) \ No newline at end of file diff --git a/packages/connectivity/connectivity_for_web/ts/package-lock.json b/packages/connectivity/connectivity_for_web/ts/package-lock.json deleted file mode 100644 index 45293a400492..000000000000 --- a/packages/connectivity/connectivity_for_web/ts/package-lock.json +++ /dev/null @@ -1,117 +0,0 @@ -{ - "name": "network-information-types-to-dart-generator", - "version": "1.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/chai": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.9.tgz", - "integrity": "sha512-NeXgZj+MFL4izGqA4sapdYzkzQG+MtGra9vhQ58dnmDY++VgJaRUws+aLVV5zRJCYJl/8s9IjMmhiUw1WsKSmw==" - }, - "@types/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", - "requires": { - "@types/node": "*" - } - }, - "@types/minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=" - }, - "@types/mocha": { - "version": "5.2.7", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", - "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==" - }, - "@types/node": { - "version": "12.12.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.28.tgz", - "integrity": "sha512-g73GJYJDXgf0jqg+P9S8h2acWbDXNkoCX8DLtJVu7Fkn788pzQ/oJsrdJz/2JejRf/SjfZaAhsw+3nd1D5EWGg==" - }, - "@types/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LrnsgZIfJaysFkv9rRJp4/uAyqw87oVed3s1hhF83nwbo9c7MG9g5DqR0seHP+lkX4ldmMrVolPjQSe2ZfD0yA==", - "requires": { - "source-map": "*" - } - }, - "@types/source-map-support": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.1.tgz", - "integrity": "sha512-VDqnZe9D2zR19qbeRvwYyHSp7AtUtCkTaRVFQ8wzwH9TXw9kKKq/vBhfEnFEXVupO2M0lBMA9mr/XyQ6gEkUOA==", - "requires": { - "@types/node": "*" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" - }, - "dart-style": { - "version": "1.3.2-dev", - "resolved": "https://registry.npmjs.org/dart-style/-/dart-style-1.3.2-dev.tgz", - "integrity": "sha512-NFI4UQYvG32t/cEkQAdkXT2ZT72tjF61tMWoALmnGwj03d2Co94zwGfbnFfdQUQvrhUNx8Wz2jKSVxGrmFaVJQ==" - }, - "dart_js_facade_gen": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/dart_js_facade_gen/-/dart_js_facade_gen-0.0.7.tgz", - "integrity": "sha512-AZiWsccbzhgJWmBjbFTPuvBhwGXk7AN8nOP91/I8PqUfSvVALiWshDc66TvywNkdNogAE5X8zlxjodw1C3iHpA==", - "requires": { - "@types/chai": "^4.2.3", - "@types/fs-extra": "^8.0.0", - "@types/minimist": "^1.2.0", - "@types/mocha": "^5.2.7", - "@types/node": "^12.7.8", - "@types/source-map": "^0.5.7", - "@types/source-map-support": "^0.5.0", - "dart-style": "^1.3.2-dev", - "minimist": "^1.2.0", - "source-map": "^0.7.3", - "source-map-support": "^0.5.13", - "typescript": "^3.6.3" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "network-information-types": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/network-information-types/-/network-information-types-0.1.0.tgz", - "integrity": "sha512-cRUCYZoRHTMjYcgk5MbwqM0h0Za34panRxAJKY8n+mQ+NLMuRIw7aKzmaZqkC/cte7bnRcdfTwFA27GgN62EtQ==" - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - }, - "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, - "typescript": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz", - "integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==" - } - } -} diff --git a/packages/connectivity/connectivity_for_web/ts/package.json b/packages/connectivity/connectivity_for_web/ts/package.json deleted file mode 100644 index 665c89d6afbb..000000000000 --- a/packages/connectivity/connectivity_for_web/ts/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "network-information-types-to-dart-generator", - "version": "1.0.0", - "description": "Use dart_js_facade_gen to generate the facade for the network-information-types package.", - "main": "index.js", - "private": true, - "scripts": { - "build": "./scripts/run_facade_gen.sh", - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "MIT", - "dependencies": { - "network-information-types": "0.1.0", - "dart_js_facade_gen": "^0.0.7" - } -} diff --git a/packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh b/packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh deleted file mode 100755 index c74b8ba171b2..000000000000 --- a/packages/connectivity/connectivity_for_web/ts/scripts/run_facade_gen.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -INDEX_PATH=node_modules/network-information-types/dist-types/index.d.ts -WORK_PATH=network_information_types.d.ts -DIST_PATH=dist - -# Create dist if it doesn't exist already -mkdir -p $DIST_PATH - -# Copy the input file(s) into our work path -cp $INDEX_PATH $WORK_PATH - -# Run dart_js_facade_gen -dart_js_facade_gen $WORK_PATH --trust-js-types --generate-html --destination . - -# Move output to the right place, and clean after yourself -mv *.dart $DIST_PATH -rm $WORK_PATH From f0e286b7a38c5debdf85ff38fff4b73df7018e11 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Mon, 13 Jul 2020 17:32:57 -0700 Subject: [PATCH 40/69] [google_sign_in] Bridge google_sign_in and googleapis. (#2824) Introduce the first version of the `extension_google_sign_in_as_googleapis_auth` package. This package lets users of the `google_sign_in` plugin to create an authenticated HTTP clients (from `googleapis_auth`) that can access any APIs from the `googleapis` package. --- .../.gitignore | 75 +++ .../.metadata | 10 + .../CHANGELOG.md | 3 + .../LICENSE | 27 + .../README.md | 46 ++ .../example/README.md | 8 + .../example/android/app/build.gradle | 64 +++ .../example/android/app/google-services.json | 246 +++++++++ .../gradle/wrapper/gradle-wrapper.properties | 5 + .../android/app/src/main/AndroidManifest.xml | 25 + .../main/java/io/flutter/plugins/.gitignore | 1 + .../EmbeddingV1Activity.java | 19 + .../EmbeddingV1ActivityTest.java | 17 + .../FlutterActivityTest.java | 17 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values/strings.xml | 4 + .../example/android/build.gradle | 29 + .../example/android/gradle.properties | 4 + .../gradle/wrapper/gradle-wrapper.properties | 5 + .../example/android/settings.gradle | 15 + .../ios/Flutter/AppFrameworkInfo.plist | 30 ++ .../example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + .../ios/GoogleSignInPluginTest/Info.plist | 22 + .../ios/Runner.xcodeproj/project.pbxproj | 502 ++++++++++++++++++ .../contents.xcworkspacedata | 10 + .../xcshareddata/xcschemes/Runner.xcscheme | 87 +++ .../contents.xcworkspacedata | 10 + .../example/ios/Runner/AppDelegate.h | 10 + .../example/ios/Runner/AppDelegate.m | 17 + .../AppIcon.appiconset/Contents.json | 116 ++++ .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 564 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 1588 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 1025 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 1716 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 1920 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 1283 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 1895 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 2665 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 3831 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 1888 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 3294 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 3612 bytes .../Runner/Base.lproj/LaunchScreen.storyboard | 27 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + .../ios/Runner/GoogleService-Info.plist | 44 ++ .../example/ios/Runner/Info.plist | 62 +++ .../example/ios/Runner/main.m | 13 + .../example/lib/main.dart | 149 ++++++ .../example/pubspec.yaml | 23 + .../example/web/index.html | 11 + ...ion_google_sign_in_as_googleapis_auth.dart | 36 ++ .../pubspec.yaml | 28 + ...oogle_sign_in_as_googleapis_auth_test.dart | 46 ++ script/build_all_plugins_app.sh | 1 + 61 files changed, 1894 insertions(+) create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.gitignore create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.metadata create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/LICENSE create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/README.md create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/README.md create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/google-services.json create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/.gitignore create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/values/strings.xml create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/build.gradle create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle.properties create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/settings.gradle create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/AppFrameworkInfo.plist create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Debug.xcconfig create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Release.xcconfig create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/GoogleSignInPluginTest/Info.plist create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.pbxproj create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.h create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.m create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Base.lproj/Main.storyboard create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/GoogleService-Info.plist create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Info.plist create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/main.m create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/lib/main.dart create mode 100755 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/pubspec.yaml create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/web/index.html create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/lib/extension_google_sign_in_as_googleapis_auth.dart create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml create mode 100644 packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/test/extension_google_sign_in_as_googleapis_auth_test.dart diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.gitignore b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.gitignore new file mode 100644 index 000000000000..bb431f0d5b47 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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 +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.metadata b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.metadata new file mode 100644 index 000000000000..2c91cc0fc35a --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/.metadata @@ -0,0 +1,10 @@ +# 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: 5e3e5a2a1a977c34b22f3709109fd237b5cab9c6 + channel: master + +project_type: package diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md new file mode 100644 index 000000000000..7f35c8490e1f --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +* First published version. diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/LICENSE b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/LICENSE new file mode 100644 index 000000000000..14fbc7c48e1c --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/LICENSE @@ -0,0 +1,27 @@ +Copyright 2020 The Flutter Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google LLC nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/README.md b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/README.md new file mode 100644 index 000000000000..78ddb4bdc91b --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/README.md @@ -0,0 +1,46 @@ +# extension_google_sign_in_as_googleapis_auth + +A bridge package between Flutter's [`google_sign_in` plugin](https://pub.dev/packages/google_sign_in) and Dart's [`googleapis` package](https://pub.dev/packages/googleapis), that is able to create [`googleapis_auth`-like `AuthClient` instances](https://pub.dev/documentation/googleapis_auth/latest/googleapis_auth.auth/AuthClient-class.html) directly from the `GoogleSignIn` plugin. + +## Usage + +This package is implemented as an [extension method](https://dart.dev/guides/language/extension-methods) on top of the `GoogleSignIn` plugin. + +In order to use it, you need to add a `dependency` to your `pubspec.yaml`. Then, wherever you're importing `package:google_sign_in/google_sign_in.dart`, add the following: + +```dart +... +import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sign_in_as_googleapis_auth.dart'; +... +``` + +From that moment on, your `GoogleSignIn` instance will have an additional `Future authenticatedClient()` method that you can call once your sign in is successful to retrieve an `AuthClient`. + +That object can then be used to create instances of `googleapis` API clients: + +```dart +... +final peopleApi = PeopleApi(await _googleSignIn.authenticatedClient()); +final response = await peopleApi.people.connections.list( + 'people/me', + personFields: 'names', +); +... +``` + +## Example + +This package contains a modified version of Flutter's Google Sign In example app that uses `package:googleapis`' API clients, instead of raw http requests. + +See it [here](https://github.com/flutter/plugins/blob/master/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/lib/main.dart). + +The original code (and its license) can be seen [here](https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in/example/lib/main.dart). + +## Testing + +Run tests with `flutter test`. + +## Issues and feedback + +Please file [issues](https://github.com/flutter/flutter/issues/new) +to send feedback or report a bug. Thank you! diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/README.md b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/README.md new file mode 100755 index 000000000000..689071dcfe8d --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/README.md @@ -0,0 +1,8 @@ +# extension_google_sign_in_example + +Demonstrates how to use the google_sign_in plugin with the `googleapis` package. + +## Getting Started + +For help getting started with Flutter, view our online +[documentation](http://flutter.io/). diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle new file mode 100755 index 000000000000..e6da1a0aebf5 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/build.gradle @@ -0,0 +1,64 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + compileSdkVersion 28 + + lintOptions { + disable 'InvalidPackage' + } + + defaultConfig { + applicationId "io.flutter.plugins.googlesigninexample" + minSdkVersion 16 + targetSdkVersion 28 + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } + + testOptions { + unitTests.returnDefaultValues = true + } +} + +flutter { + source '../..' +} + +dependencies { + implementation 'com.google.android.gms:play-services-auth:16.0.1' + testImplementation'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:2.17.0' +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/google-services.json b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/google-services.json new file mode 100644 index 000000000000..efa524535553 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/google-services.json @@ -0,0 +1,246 @@ +{ + "project_info": { + "project_number": "479882132969", + "firebase_url": "https://my-flutter-proj.firebaseio.com", + "project_id": "my-flutter-proj", + "storage_bucket": "my-flutter-proj.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:c73fd19ff7e2c0be", + "android_client_info": { + "package_name": "io.flutter.plugins.cameraexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:632cdf3fc0a17139", + "android_client_info": { + "package_name": "io.flutter.plugins.firebasedynamiclinksexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-32qusitiag53931ck80h121ajhlc5a7e.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebasedynamiclinksexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:ae50362b4bc06086", + "android_client_info": { + "package_name": "io.flutter.plugins.firebasemlvisionexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-9pp74fkgmtvt47t9rikc1p861v7n85tn.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebasemlvisionexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:215a22700e1b466b", + "android_client_info": { + "package_name": "io.flutter.plugins.firebaseperformanceexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-8h4kiv8m7ho4tvn6uuujsfcrf69unuf7.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.firebaseperformanceexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + }, + { + "client_info": { + "mobilesdk_app_id": "1:479882132969:android:5e9f1f89e134dc86", + "android_client_info": { + "package_name": "io.flutter.plugins.googlesigninexample" + } + }, + "oauth_client": [ + { + "client_id": "479882132969-90ml692hkonp587sl0v0rurmnvkekgrg.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "io.flutter.plugins.googlesigninexample", + "certificate_hash": "e733b7a303250b63e06de6f7c9767c517d69cfa0" + } + }, + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyCrZz9T0Pg0rDnpxfNuPBrOxGhXskfebXs" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 2, + "other_platform_oauth_client": [ + { + "client_id": "479882132969-0d20fkjtr1p8evfomfkf3vmi50uajml2.apps.googleusercontent.com", + "client_type": 3 + }, + { + "client_id": "479882132969-gjp4e63ogu2h6guttj2ie6t3f10ic7i8.apps.googleusercontent.com", + "client_type": 2, + "ios_info": { + "bundle_id": "io.flutter.plugins.firebaseMlVisionExample" + } + } + ] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..9a4163a4f5ee --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml new file mode 100755 index 000000000000..df80f829c1e7 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/.gitignore b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/.gitignore new file mode 100755 index 000000000000..9eb4563d2ae1 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/.gitignore @@ -0,0 +1 @@ +GeneratedPluginRegistrant.java diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java new file mode 100644 index 000000000000..f7ea0c4043a6 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java @@ -0,0 +1,19 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.googlesigninexample; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.googlesignin.GoogleSignInPlugin; +import io.flutter.view.FlutterMain; + +public class EmbeddingV1Activity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + FlutterMain.startInitialization(this); + super.onCreate(savedInstanceState); + GoogleSignInPlugin.registerWith(registrarFor("io.flutter.plugins.googlesignin")); + } +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java new file mode 100644 index 000000000000..8bddbff7ce27 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java @@ -0,0 +1,17 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.googlesigninexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class EmbeddingV1ActivityTest { + @Rule + public ActivityTestRule rule = + new ActivityTestRule<>(EmbeddingV1Activity.class); +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java new file mode 100644 index 000000000000..77cdcee9bcdb --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java @@ -0,0 +1,17 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package io.flutter.plugins.googlesigninexample; + +import androidx.test.rule.ActivityTestRule; +import dev.flutter.plugins.e2e.FlutterRunner; +import io.flutter.embedding.android.FlutterActivity; +import org.junit.Rule; +import org.junit.runner.RunWith; + +@RunWith(FlutterRunner.class) +public class FlutterActivityTest { + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(FlutterActivity.class); +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/values/strings.xml b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/values/strings.xml new file mode 100644 index 000000000000..c7e28ffcedd1 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + YOUR_WEB_CLIENT_ID + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/build.gradle b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/build.gradle new file mode 100755 index 000000000000..541636cc492a --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/build.gradle @@ -0,0 +1,29 @@ +buildscript { + repositories { + google() + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.3.0' + } +} + +allprojects { + repositories { + google() + jcenter() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle.properties b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle.properties new file mode 100755 index 000000000000..38c8d4544ff1 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle.properties @@ -0,0 +1,4 @@ +org.gradle.jvmargs=-Xmx1536M +android.enableR8=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000000..019065d1d650 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/settings.gradle b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/settings.gradle new file mode 100755 index 000000000000..115da6cb4f4d --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/android/settings.gradle @@ -0,0 +1,15 @@ +include ':app' + +def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + +def plugins = new Properties() +def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') +if (pluginsFile.exists()) { + pluginsFile.withInputStream { stream -> plugins.load(stream) } +} + +plugins.each { name, path -> + def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() + include ":$name" + project(":$name").projectDir = pluginDirectory +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/AppFrameworkInfo.plist b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/AppFrameworkInfo.plist new file mode 100755 index 000000000000..6c2de8086bcd --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + UIRequiredDeviceCapabilities + + arm64 + + MinimumOSVersion + 8.0 + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Debug.xcconfig b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Debug.xcconfig new file mode 100755 index 000000000000..9803018ca79d --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Release.xcconfig b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Release.xcconfig new file mode 100755 index 000000000000..a4a8c604e13d --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Generated.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/GoogleSignInPluginTest/Info.plist b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/GoogleSignInPluginTest/Info.plist new file mode 100644 index 000000000000..64d65ca49577 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/GoogleSignInPluginTest/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.pbxproj b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..faaaa58070bd --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,502 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */; }; + 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */; }; + 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2FB9CBA01DB0A2DE5F31E12 /* libPods-Runner.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 840012C8B5EDBCF56B0E4AC1 /* Pods */ = { + isa = PBXGroup; + children = ( + 5A76713E622F06379AEDEBFA /* Pods-Runner.debug.xcconfig */, + F582639B44581540871D9BB0 /* Pods-Runner.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 7ACDFB0D1E8944C400BE2D00 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 840012C8B5EDBCF56B0E4AC1 /* Pods */, + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 5C6F5A6C1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.h */, + 5C6F5A6D1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m */, + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 7A303C2D1E89D76400B1F19E /* GoogleService-Info.plist */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + CF3B75C9A7D2FA2A4C99F110 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0263E28FA425D1CE928BDE15 /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 95BB15E9E1769C0D146AA592 /* [CP] Embed Pods Frameworks */, + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A303C2E1E89D76400B1F19E /* GoogleService-Info.plist in Resources */, + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 7ACDFB0E1E8944C400BE2D00 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed\n/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin\n"; + }; + 532EA9D341340B1DCD08293D /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh", + "${PODS_ROOT}/GoogleSignIn/Resources/GoogleSignIn.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/GoogleSignIn.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 95BB15E9E1769C0D146AA592 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AB1344B0443C71CD721E1BB7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + 5C6F5A6E1EC3B4CB008D64B5 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + 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_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.googleSignInExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.flutter.plugins.googleSignInExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100755 index 000000000000..3bb3697ef41c --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100755 index 000000000000..21a3cc14c74e --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.h b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.h new file mode 100644 index 000000000000..d9e18e990f2e --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.h @@ -0,0 +1,10 @@ +// Copyright 2017 The Chromium 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 +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.m b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.m new file mode 100644 index 000000000000..f08675707182 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/AppDelegate.m @@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium 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 "AppDelegate.h" +#include "GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + // Override point for customization after application launch. + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 000000000000..d22f10b2ab63 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100755 index 0000000000000000000000000000000000000000..28c6bf03016f6c994b70f38d1b7346e5831b531f GIT binary patch literal 564 zcmV-40?Yl0P)Px$?ny*JR5%f>l)FnDQ543{x%ZCiu33$Wg!pQFfT_}?5Q|_VSlIbLC`dpoMXL}9 zHfd9&47Mo(7D231gb+kjFxZHS4-m~7WurTH&doVX2KI5sU4v(sJ1@T9eCIKPjsqSr z)C01LsCxk=72-vXmX}CQD#BD;Cthymh&~=f$Q8nn0J<}ZrusBy4PvRNE}+1ceuj8u z0mW5k8fmgeLnTbWHGwfKA3@PdZxhn|PypR&^p?weGftrtCbjF#+zk_5BJh7;0`#Wr zgDpM_;Ax{jO##IrT`Oz;MvfwGfV$zD#c2xckpcXC6oou4ML~ezCc2EtnsQTB4tWNg z?4bkf;hG7IMfhgNI(FV5Gs4|*GyMTIY0$B=_*mso9Ityq$m^S>15>-?0(zQ<8Qy<_TjHE33(?_M8oaM zyc;NxzRVK@DL6RJnX%U^xW0Gpg(lXp(!uK1v0YgHjs^ZXSQ|m#lV7ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..f091b6b0bca859a3f474b03065bef75ba58a9e4c GIT binary patch literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..d0ef06e7edb86cdfe0d15b4b0d98334a86163658 GIT binary patch literal 1716 zcmds$`#;kQ7{|XelZftyR5~xW7?MLxS4^|Hw3&P7^y)@A9Fj{Xm1~_CIV^XZ%SLBn zA;!r`GqGHg=7>xrB{?psZQs88ZaedDoagm^KF{a*>G|dJWRSe^I$DNW008I^+;Kjt z>9p3GNR^I;v>5_`+91i(*G;u5|L+Bu6M=(afLjtkya#yZ175|z$pU~>2#^Z_pCZ7o z1c6UNcv2B3?; zX%qdxCXQpdKRz=#b*q0P%b&o)5ZrNZt7$fiETSK_VaY=mb4GK`#~0K#~9^ zcY!`#Af+4h?UMR-gMKOmpuYeN5P*RKF!(tb`)oe0j2BH1l?=>y#S5pMqkx6i{*=V9JF%>N8`ewGhRE(|WohnD59R^$_36{4>S zDFlPC5|k?;SPsDo87!B{6*7eqmMdU|QZ84>6)Kd9wNfh90=y=TFQay-0__>=<4pk& zYDjgIhL-jQ9o>z32K)BgAH+HxamL{ZL~ozu)Qqe@a`FpH=oQRA8=L-m-1dam(Ix2V z?du;LdMO+ooBelr^_y4{|44tmgH^2hSzPFd;U^!1p>6d|o)(-01z{i&Kj@)z-yfWQ)V#3Uo!_U}q3u`(fOs`_f^ueFii1xBNUB z6MecwJN$CqV&vhc+)b(p4NzGGEgwWNs z@*lUV6LaduZH)4_g!cE<2G6#+hJrWd5(|p1Z;YJ7ifVHv+n49btR}dq?HHDjl{m$T z!jLZcGkb&XS2OG~u%&R$(X+Z`CWec%QKt>NGYvd5g20)PU(dOn^7%@6kQb}C(%=vr z{?RP(z~C9DPnL{q^@pVw@|Vx~@3v!9dCaBtbh2EdtoNHm4kGxp>i#ct)7p|$QJs+U z-a3qtcPvhihub?wnJqEt>zC@)2suY?%-96cYCm$Q8R%-8$PZYsx3~QOLMDf(piXMm zB=<63yQk1AdOz#-qsEDX>>c)EES%$owHKue;?B3)8aRd}m~_)>SL3h2(9X;|+2#7X z+#2)NpD%qJvCQ0a-uzZLmz*ms+l*N}w)3LRQ*6>|Ub-fyptY(keUxw+)jfwF5K{L9 z|Cl_w=`!l_o><384d&?)$6Nh(GAm=4p_;{qVn#hI8lqewW7~wUlyBM-4Z|)cZr?Rh z=xZ&Ol>4(CU85ea(CZ^aO@2N18K>ftl8>2MqetAR53_JA>Fal`^)1Y--Am~UDa4th zKfCYpcXky$XSFDWBMIl(q=Mxj$iMBX=|j9P)^fDmF(5(5$|?Cx}DKEJa&XZP%OyE`*GvvYQ4PV&!g2|L^Q z?YG}tx;sY@GzMmsY`7r$P+F_YLz)(e}% zyakqFB<6|x9R#TdoP{R$>o7y(-`$$p0NxJ6?2B8tH)4^yF(WhqGZlM3=9Ibs$%U1w zWzcss*_c0=v_+^bfb`kBFsI`d;ElwiU%frgRB%qBjn@!0U2zZehBn|{%uNIKBA7n= zzE`nnwTP85{g;8AkYxA68>#muXa!G>xH22D1I*SiD~7C?7Za+9y7j1SHiuSkKK*^O zsZ==KO(Ua#?YUpXl{ViynyT#Hzk=}5X$e04O@fsMQjb}EMuPWFO0e&8(2N(29$@Vd zn1h8Yd>6z(*p^E{c(L0Lg=wVdupg!z@WG;E0k|4a%s7Up5C0c)55XVK*|x9RQeZ1J@1v9MX;>n34(i>=YE@Iur`0Vah(inE3VUFZNqf~tSz{1fz3Fsn_x4F>o(Yo;kpqvBe-sbwH(*Y zu$JOl0b83zu$JMvy<#oH^Wl>aWL*?aDwnS0iEAwC?DK@aT)GHRLhnz2WCvf3Ba;o=aY7 z2{Asu5MEjGOY4O#Ggz@@J;q*0`kd2n8I3BeNuMmYZf{}pg=jTdTCrIIYuW~luKecn z+E-pHY%ohj@uS0%^ z&(OxwPFPD$+#~`H?fMvi9geVLci(`K?Kj|w{rZ9JgthFHV+=6vMbK~0)Ea<&WY-NC zy-PnZft_k2tfeQ*SuC=nUj4H%SQ&Y$gbH4#2sT0cU0SdFs=*W*4hKGpuR1{)mV;Qf5pw4? zfiQgy0w3fC*w&Bj#{&=7033qFR*<*61B4f9K%CQvxEn&bsWJ{&winp;FP!KBj=(P6 z4Z_n4L7cS;ao2)ax?Tm|I1pH|uLpDSRVghkA_UtFFuZ0b2#>!8;>-_0ELjQSD-DRd z4im;599VHDZYtnWZGAB25W-e(2VrzEh|etsv2YoP#VbIZ{aFkwPrzJ#JvCvA*mXS& z`}Q^v9(W4GiSs}#s7BaN!WA2bniM$0J(#;MR>uIJ^uvgD3GS^%*ikdW6-!VFUU?JV zZc2)4cMsX@j z5HQ^e3BUzOdm}yC-xA%SY``k$rbfk z;CHqifhU*jfGM@DkYCecD9vl*qr58l6x<8URB=&%{!Cu3RO*MrKZ4VO}V6R0a zZw3Eg^0iKWM1dcTYZ0>N899=r6?+adUiBKPciJw}L$=1f4cs^bio&cr9baLF>6#BM z(F}EXe-`F=f_@`A7+Q&|QaZ??Txp_dB#lg!NH=t3$G8&06MFhwR=Iu*Im0s_b2B@| znW>X}sy~m#EW)&6E&!*0%}8UAS)wjt+A(io#wGI@Z2S+Ms1Cxl%YVE800007ip7{`C_J2TxPmfw%h$|%acrYHt)Re^PB%O&&=~a zhS(%I#+V>J-vjIib^<+s%ludY7y^C(P8nmqn9fp!i+?vr`bziDE=bx`%2W#Xyrj|i z!XQ4v1%L`m{7KT7q+LZNB^h8Ha2e=`Wp65^0;J00)_^G=au=8Yo;1b`CV&@#=jIBo zjN^JNVfYSs)+kDdGe7`1&8!?MQYKS?DuHZf3iogk_%#9E|5S zWeHrmAo>P;ejX7mwq#*}W25m^ZI+{(Z8fI?4jM_fffY0nok=+88^|*_DwcW>mR#e+ zX$F_KMdb6sRz!~7KkyN0G(3XQ+;z3X%PZ4gh;n-%62U<*VUKNv(D&Q->Na@Xb&u5Q3`3DGf+a8O5x7c#7+R+EAYl@R5us)CIw z7sT@_y~Ao@uL#&^LIh&QceqiT^+lb0YbFZt_SHOtWA%mgPEKVNvVgCsXy{5+zl*X8 zCJe)Q@y>wH^>l4;h1l^Y*9%-23TSmE>q5nI@?mt%n;Sj4Qq`Z+ib)a*a^cJc%E9^J zB;4s+K@rARbcBLT5P=@r;IVnBMKvT*)ew*R;&8vu%?Z&S>s?8?)3*YawM0P4!q$Kv zMmKh3lgE~&w&v%wVzH3Oe=jeNT=n@Y6J6TdHWTjXfX~-=1A1Bw`EW8rn}MqeI34nh zexFeA?&C3B2(E?0{drE@DA2pu(A#ElY&6el60Rn|Qpn-FkfQ8M93AfWIr)drgDFEU zghdWK)^71EWCP(@(=c4kfH1Y(4iugD4fve6;nSUpLT%!)MUHs1!zJYy4y||C+SwQ! z)KM&$7_tyM`sljP2fz6&Z;jxRn{Wup8IOUx8D4uh&(=O zx-7$a;U><*5L^!%xRlw)vAbh;sdlR||& ze}8_8%)c2Fwy=F&H|LM+p{pZB5DKTx>Y?F1N%BlZkXf!}JeGuMZk~LPi7{cidvUGB zAJ4LVeNV%XO>LTrklB#^-;8nb;}6l;1oW&WS=Mz*Az!4cqqQzbOSFq`$Q%PfD7srM zpKgP-D_0XPTRX*hAqeq0TDkJ;5HB1%$3Np)99#16c{ zJImlNL(npL!W|Gr_kxl1GVmF5&^$^YherS7+~q$p zt}{a=*RiD2Ikv6o=IM1kgc7zqpaZ;OB)P!1zz*i3{U()Dq#jG)egvK}@uFLa`oyWZ zf~=MV)|yJn`M^$N%ul5);JuQvaU1r2wt(}J_Qgyy`qWQI`hEeRX0uC@c1(dQ2}=U$ tNIIaX+dr)NRWXcxoR{>fqI{SF_dm1Ylv~=3YHI)h002ovPDHLkV1g(pWS;;4 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..c8f9ed8f5cee1c98386d13b17e89f719e83555b2 GIT binary patch literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100755 index 0000000000000000000000000000000000000000..a6d6b8609df07bf62e5100a53a01510388bd2b22 GIT binary patch literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100755 index 0000000000000000000000000000000000000000..75b2d164a5a98e212cca15ea7bf2ab5de5108680 GIT binary patch literal 3831 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 literal 0 HcmV?d00001 diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100755 index 0000000000000000000000000000000000000000..c4df70d39da7941ef3f6dcb7f06a192d8dcb308d GIT binary patch literal 1888 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 iW8 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Base.lproj/Main.storyboard b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Base.lproj/Main.storyboard new file mode 100755 index 000000000000..f3c28516fb38 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/GoogleService-Info.plist b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/GoogleService-Info.plist new file mode 100644 index 000000000000..6042aab908af --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/GoogleService-Info.plist @@ -0,0 +1,44 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + ca-app-pub-3940256099942544/2934735716 + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + ca-app-pub-3940256099942544/4411468910 + CLIENT_ID + 479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u + ANDROID_CLIENT_ID + 479882132969-jie8r1me6dsra60pal6ejaj8dgme3tg0.apps.googleusercontent.com + API_KEY + AIzaSyBECOwLTAN6PU4Aet1b2QLGIb3kRK8Xjew + GCM_SENDER_ID + 479882132969 + PLIST_VERSION + 1 + BUNDLE_ID + io.flutter.plugins.googleSignInExample + PROJECT_ID + my-flutter-proj + STORAGE_BUCKET + my-flutter-proj.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:479882132969:ios:2643f950e0a0da08 + DATABASE_URL + https://my-flutter-proj.firebaseio.com + SERVER_CLIENT_ID + YOUR_SERVER_CLIENT_ID + + \ No newline at end of file diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Info.plist b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Info.plist new file mode 100755 index 000000000000..e03ccfe55e37 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Google Sign-In Example + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + GoogleSignInExample + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + com.googleusercontent.apps.479882132969-9i9aqik3jfjd7qhci1nqf0bm2g71rm1u + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/main.m b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/main.m new file mode 100644 index 000000000000..bec320c0bee0 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/ios/Runner/main.m @@ -0,0 +1,13 @@ +// Copyright 2017 The Chromium 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 +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/lib/main.dart b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/lib/main.dart new file mode 100755 index 000000000000..a238ca3bf8b5 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/lib/main.dart @@ -0,0 +1,149 @@ +// Copyright 2019 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. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:google_sign_in/google_sign_in.dart'; + +import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sign_in_as_googleapis_auth.dart'; +import 'package:googleapis/people/v1.dart'; + +GoogleSignIn _googleSignIn = GoogleSignIn( + scopes: [ + 'email', + 'https://www.googleapis.com/auth/contacts.readonly', + ], +); + +void main() { + runApp( + MaterialApp( + title: 'Google Sign In', + home: SignInDemo(), + ), + ); +} + +class SignInDemo extends StatefulWidget { + @override + State createState() => SignInDemoState(); +} + +class SignInDemoState extends State { + GoogleSignInAccount _currentUser; + String _contactText; + + @override + void initState() { + super.initState(); + _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) { + setState(() { + _currentUser = account; + }); + if (_currentUser != null) { + _handleGetContact(); + } + }); + _googleSignIn.signInSilently(); + } + + Future _handleGetContact() async { + setState(() { + _contactText = 'Loading contact info...'; + }); + + final peopleApi = PeopleApi(await _googleSignIn.authenticatedClient()); + final response = await peopleApi.people.connections.list( + 'people/me', + personFields: 'names', + ); + + final firstNamedContactName = _pickFirstNamedContact(response.connections); + + setState(() { + if (firstNamedContactName != null) { + _contactText = 'I see you know $firstNamedContactName!'; + } else { + _contactText = 'No contacts to display.'; + } + }); + } + + String _pickFirstNamedContact(List connections) { + return connections + ?.firstWhere( + (person) => person.names != null, + orElse: () => null, + ) + ?.names + ?.firstWhere( + (name) => name.displayName != null, + orElse: () => null, + ) + ?.displayName; + } + + Future _handleSignIn() async { + try { + await _googleSignIn.signIn(); + } catch (error) { + print(error); + } + } + + Future _handleSignOut() => _googleSignIn.disconnect(); + + Widget _buildBody() { + if (_currentUser != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ListTile( + leading: GoogleUserCircleAvatar( + identity: _currentUser, + ), + title: Text(_currentUser.displayName ?? ''), + subtitle: Text(_currentUser.email ?? ''), + ), + const Text('Signed in successfully.'), + Text(_contactText ?? ''), + RaisedButton( + child: const Text('SIGN OUT'), + onPressed: _handleSignOut, + ), + RaisedButton( + child: const Text('REFRESH'), + onPressed: _handleGetContact, + ), + ], + ); + } else { + return Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text('You are not currently signed in.'), + RaisedButton( + child: const Text('SIGN IN'), + onPressed: _handleSignIn, + ), + ], + ); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Google Sign In'), + ), + body: ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: _buildBody(), + )); + } +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/pubspec.yaml b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/pubspec.yaml new file mode 100755 index 000000000000..aee073971d2c --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/pubspec.yaml @@ -0,0 +1,23 @@ +name: extension_google_sign_in_example +description: Example of Google Sign-In plugin and googleapis. + +dependencies: + flutter: + sdk: flutter + google_sign_in: ^4.4.1 + extension_google_sign_in_as_googleapis_auth: + path: ../ + googleapis: ^0.55.0 + +dev_dependencies: + pedantic: ^1.8.0 + e2e: ^0.2.1 + flutter_driver: + sdk: flutter + +flutter: + uses-material-design: true + +environment: + sdk: ">=2.0.0-dev.28.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4 <2.0.0" diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/web/index.html b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/web/index.html new file mode 100644 index 000000000000..42a7d93582ba --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/example/web/index.html @@ -0,0 +1,11 @@ + + + + + + Google Sign-in Example + + + + + diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/lib/extension_google_sign_in_as_googleapis_auth.dart b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/lib/extension_google_sign_in_as_googleapis_auth.dart new file mode 100644 index 000000000000..eec45cc0e89a --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/lib/extension_google_sign_in_as_googleapis_auth.dart @@ -0,0 +1,36 @@ +// Copyright 2020 The Flutter Authors +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +import 'package:meta/meta.dart'; +import 'package:google_sign_in/google_sign_in.dart'; +import 'package:googleapis_auth/auth.dart' as googleapis_auth; +import 'package:http/http.dart' as http; + +/// Extension on [GoogleSignIn] that adds an `authenticatedClient` method. +/// +/// This method can be used to retrieve an authenticated [googleapis_auth.AuthClient] +/// client that can be used with the rest of the `googleapis` libraries. +extension GoogleApisGoogleSignInAuth on GoogleSignIn { + /// Retrieve a `googleapis` authenticated client. + Future authenticatedClient({ + @visibleForTesting GoogleSignInAuthentication debugAuthentication, + @visibleForTesting List debugScopes = const [], + }) async { + final auth = debugAuthentication ?? await currentUser.authentication; + final credentials = googleapis_auth.AccessCredentials( + googleapis_auth.AccessToken( + 'Bearer', + auth.accessToken, + // We don't know when the token expires, so we assume "never" + DateTime.now().toUtc().add(Duration(days: 365)), + ), + null, // We don't have a refreshToken + debugScopes ?? this.scopes, + ); + + return googleapis_auth.authenticatedClient(http.Client(), credentials); + } +} diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml new file mode 100644 index 000000000000..f94b5e505946 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/pubspec.yaml @@ -0,0 +1,28 @@ +# Copyright 2020 The Flutter Authors +# +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file or at +# https://developers.google.com/open-source/licenses/bsd + +name: extension_google_sign_in_as_googleapis_auth +description: A bridge package between google_sign_in and googleapis_auth, to create Authenticated Clients from google_sign_in user credentials. +version: 1.0.0 +homepage: https://github.com/flutter/plugins/google_sign_in/extension_google_sign_in_as_googleapis_auth + +dependencies: + flutter: + sdk: flutter + google_sign_in: ^4.4.1 + googleapis_auth: ^0.2.11+1 + meta: ^1.1.8 + http: ^0.12.1 + +dev_dependencies: + mockito: ^4.1.1 + pedantic: ^1.9.0 + flutter_test: + sdk: flutter + +environment: + sdk: ">=2.7.0 <3.0.0" + flutter: ">=1.12.13+hotfix.4 <2.0.0" diff --git a/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/test/extension_google_sign_in_as_googleapis_auth_test.dart b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/test/extension_google_sign_in_as_googleapis_auth_test.dart new file mode 100644 index 000000000000..9f86703d4bb8 --- /dev/null +++ b/packages/google_sign_in/extension_google_sign_in_as_googleapis_auth/test/extension_google_sign_in_as_googleapis_auth_test.dart @@ -0,0 +1,46 @@ +// Copyright 2020 The Flutter Authors +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +import 'package:google_sign_in/google_sign_in.dart'; +import 'package:googleapis_auth/auth.dart' as auth; +import 'package:extension_google_sign_in_as_googleapis_auth/extension_google_sign_in_as_googleapis_auth.dart'; +import 'package:mockito/mockito.dart'; +import 'package:flutter_test/flutter_test.dart'; + +// Mocks so I don't have to prepare all the GoogleSignIn environment. +class MockGoogleSignIn extends Mock implements GoogleSignIn {} + +class MockGoogleSignInAuthentication extends Mock + implements GoogleSignInAuthentication {} + +const SOME_FAKE_ACCESS_TOKEN = 'this-is-something-not-null'; +const SOME_FAKE_SCOPES = ['some-scope', 'another-scope']; + +void main() { + GoogleSignIn signIn = MockGoogleSignIn(); + final authMock = MockGoogleSignInAuthentication(); + + setUp(() { + when(authMock.accessToken).thenReturn(SOME_FAKE_ACCESS_TOKEN); + }); + + test('authenticatedClient returns an authenticated client', () async { + final client = await signIn.authenticatedClient( + debugAuthentication: authMock, + ); + expect(client, isA()); + }); + + test('authenticatedClient returned client contains the passed-in credentials', + () async { + final client = await signIn.authenticatedClient( + debugAuthentication: authMock, + debugScopes: SOME_FAKE_SCOPES, + ); + expect(client.credentials.accessToken.data, equals(SOME_FAKE_ACCESS_TOKEN)); + expect(client.credentials.scopes, equals(SOME_FAKE_SCOPES)); + }); +} diff --git a/script/build_all_plugins_app.sh b/script/build_all_plugins_app.sh index 961cecce7903..f1f68ddbe985 100755 --- a/script/build_all_plugins_app.sh +++ b/script/build_all_plugins_app.sh @@ -14,6 +14,7 @@ readonly EXCLUDED_PLUGINS_LIST=( "connectivity_macos" "connectivity_platform_interface" "connectivity_web" + "extension_google_sign_in_as_googleapis_auth" "flutter_plugin_android_lifecycle" "google_maps_flutter_platform_interface" "google_maps_flutter_web" From 1350f8e6ea1a470bce899edbb69683ff84d795f3 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 13:32:14 -0300 Subject: [PATCH 41/69] Web VTT Supported Added --- .../video_player/example/assets/sample.vtt | 40 +++++ .../video_player/example/pubspec.yaml | 7 +- .../lib/src/closed_caption_file.dart | 4 + .../video_player/lib/src/web_vtt.dart | 168 ++++++++++++++++++ .../video_player/video_player/pubspec.yaml | 3 +- .../video_player/test/web_vtt.test.dart | 119 +++++++++++++ 6 files changed, 337 insertions(+), 4 deletions(-) create mode 100644 packages/video_player/video_player/example/assets/sample.vtt create mode 100644 packages/video_player/video_player/lib/src/web_vtt.dart create mode 100644 packages/video_player/video_player/test/web_vtt.test.dart diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt new file mode 100644 index 000000000000..2e5c750a7cee --- /dev/null +++ b/packages/video_player/video_player/example/assets/sample.vtt @@ -0,0 +1,40 @@ +WEBVTT + +00:00.500 --> 00:01.000 +We are in New York City + +00:01.200 --> 00:02.000 +We’re actually at the Lucern Hotel, just down the street + +00:16.000 --> 00:18.000 +from the American Museum of Natural History + +00:18.000 --> 00:20.000 +And with me is Neil deGrasse Tyson + +00:20.000 --> 00:22.000 +Astrophysicist, Director of the Hayden Planetarium + +00:22.000 --> 00:24.000 +at the AMNH. + +00:24.000 --> 00:26.000 +Thank you for walking down here. + +00:27.000 --> 00:30.000 +And I want to do a follow-up on the last conversation we did. + +00:30.000 --> 00:31.500 align:right size:50% +When we e-mailed— + +00:30.500 --> 00:32.500 align:left size:50% +Didn’t we talk about enough in that conversation? + +00:32.000 --> 00:35.500 align:right size:50% +No! No no no no; 'cos 'cos obviously 'cos + +00:32.500 --> 00:33.500 align:left size:50% +Laughs + +00:35.500 --> 00:38.000 +You know I’m so excited my glasses are falling off here. \ No newline at end of file diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 29f1c91e4af3..283a484be711 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -19,6 +19,7 @@ dev_dependencies: flutter: uses-material-design: true assets: - - assets/flutter-mark-square-64.png - - assets/Butterfly-209.mp4 - - assets/bumble_bee_captions.srt + - assets/flutter-mark-square-64.png + - assets/Butterfly-209.mp4 + - assets/bumble_bee_captions.srt + - assets/sample.vtt diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 2d9242a675d5..8cbe5dee896f 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -5,6 +5,9 @@ import 'sub_rip.dart'; export 'sub_rip.dart' show SubRipCaptionFile; +import 'web_vtt.dart'; +export 'web_vtt.dart' show WebVTTCaptionFile; + /// A structured representation of a parsed closed caption file. /// /// A closed caption file includes a list of captions, each with a start and end @@ -15,6 +18,7 @@ export 'sub_rip.dart' show SubRipCaptionFile; /// /// See: /// * [SubRipCaptionFile]. +/// * [WebVTTCaptionFile]. abstract class ClosedCaptionFile { /// The full list of captions from a given file. /// diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart new file mode 100644 index 000000000000..d9978c259148 --- /dev/null +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -0,0 +1,168 @@ +// Copyright 2020 The Chromium 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 'dart:convert'; + +import 'closed_caption_file.dart'; +import 'package:html/parser.dart'; + +/// Represents a [ClosedCaptionFile], parsed from the WebVTT file format. +/// See: https://en.wikipedia.org/wiki/WebVTT +class WebVTTCaptionFile extends ClosedCaptionFile { + /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in + /// the WebVTT file format. + /// * See: https://en.wikipedia.org/wiki/WebVTT + WebVTTCaptionFile(this.fileContents) + : _captions = _parseCaptionsFromWebVTTString(fileContents); + + /// The entire body of the SubRip file. + final String fileContents; + + @override + List get captions => _captions; + + final List _captions; +} + +List _parseCaptionsFromWebVTTString(String file) { + final List captions = []; + int number = 1; + for (List captionLines in _readWebVTTFile(file)) { + if (captionLines.length < 2) continue; + print(captionLines); + + final int captionNumber = number; + final _StartAndEnd startAndEnd = + _StartAndEnd.fromWebVTTString(captionLines[0]); + + final String text = captionLines.sublist(1).join('\n'); + + //TODO: Handle text format + final String textWithoutFormat = _parseHtmlString(text); + + final Caption newCaption = Caption( + number: captionNumber, + start: startAndEnd.start, + end: startAndEnd.end, + text: textWithoutFormat, + ); + + if (newCaption.start != null && newCaption.end != null) { + captions.add(newCaption); + number++; + } + } + + return captions; +} + +class _StartAndEnd { + final Duration start; + final Duration end; + + _StartAndEnd(this.start, this.end); + + // Assumes format from an VTT file. + // For example: + // 00:09.000 --> 00:11.000 + static _StartAndEnd fromWebVTTString(String line) { + final RegExp format = + RegExp(_webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp); + + if (!format.hasMatch(line)) { + return _StartAndEnd(null, null); + } + + final List times = line.split(_webVTTArrow); + + final Duration start = _parseWebVTTTimestamp(times[0]); + final Duration end = _parseWebVTTTimestamp(times[1]); + + return _StartAndEnd(start, end); + } +} + +String _parseHtmlString(String htmlString) { + var document = parse(htmlString); + String parsedString = parse(document.body.text).documentElement.text; + return parsedString; +} + +// Parses a time stamp in an VTT file into a Duration. +// For example: +// +// _parseWebVTTimestamp('00:01:08.430') +// returns +// Duration(hours: 0, minutes: 1, seconds: 8, milliseconds: 430) +Duration _parseWebVTTTimestamp(String timestampString) { + if (!RegExp(_webVTTTimeStamp).hasMatch(timestampString)) { + return null; + } + + final List dotSections = timestampString.split('.'); + final List hoursMinutesSeconds = dotSections[0].split(':'); + + int hours = 0; + int minutes = 0; + int seconds = 0; + List styles; + + if (hoursMinutesSeconds.length > 2) { + // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] + hours = int.parse(hoursMinutesSeconds[0]); + minutes = int.parse(hoursMinutesSeconds[1]); + seconds = int.parse(hoursMinutesSeconds[2]); + } else if (int.parse(hoursMinutesSeconds[0]) > 59) { + // Timestamp takes the form of [hours]:[minutes].[milliseconds] + // First position is hours as it's over 59. + hours = int.parse(hoursMinutesSeconds[0]); + minutes = int.parse(hoursMinutesSeconds[1]); + } else { + // Timestamp takes the form of [minutes]:[seconds].[milliseconds] + minutes = int.parse(hoursMinutesSeconds[0]); + seconds = int.parse(hoursMinutesSeconds[1]); + } + + List milisecondsStyles = dotSections[1].split(" "); + //TODO: Handle styles data on timestamp + if (milisecondsStyles.length > 1) { + styles = milisecondsStyles.sublist(1); + } + int milliseconds = int.parse(milisecondsStyles[0]); + + return Duration( + hours: hours, + minutes: minutes, + seconds: seconds, + milliseconds: milliseconds, + ); +} + +// Reads on SubRip file and splits it into Lists of strings where each list is one +// caption. +List> _readWebVTTFile(String file) { + final List lines = LineSplitter.split(file).toList(); + + final List> captionStrings = >[]; + List currentCaption = []; + int lineIndex = 0; + for (final String line in lines) { + final bool isLineBlank = line.trim().isEmpty; + if (!isLineBlank) { + currentCaption.add(line); + } + + if (isLineBlank || lineIndex == lines.length - 1) { + captionStrings.add(currentCaption); + currentCaption = []; + } + + lineIndex += 1; + } + + return captionStrings; +} + +const String _webVTTTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; +const String _webVTTArrow = r' --> '; diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 03f71bf4f412..b88edcde5d3c 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -26,7 +26,8 @@ dependencies: # validation, so we set a ^ constraint. # TODO(amirh): Revisit this (either update this part in the design or the pub tool). # https://github.com/flutter/flutter/issues/46264 - video_player_web: '>=0.1.1 <2.0.0' + video_player_web: ">=0.1.1 <2.0.0" + html: ^0.14.0+3 flutter: sdk: flutter diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt.test.dart new file mode 100644 index 000000000000..a9e4cae2e8ed --- /dev/null +++ b/packages/video_player/video_player/test/web_vtt.test.dart @@ -0,0 +1,119 @@ +// Copyright 2020 The Chromium 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_test/flutter_test.dart'; +import 'package:video_player/src/closed_caption_file.dart'; +import 'package:video_player/video_player.dart'; + +void main() { + test('Parses VTT file', () { + final WebVTTCaptionFile parsedFile = WebVTTCaptionFile(_validVTT); + + expect(parsedFile.captions.length, 13); + + final Caption firstCaption = parsedFile.captions.first; + expect(firstCaption.number, 1); + expect(firstCaption.start, Duration(seconds: 9)); + expect(firstCaption.end, Duration(seconds: 11, milliseconds: 430)); + expect(firstCaption.text, 'We are in New York City'); + + final Caption secondCaption = parsedFile.captions[1]; + expect(secondCaption.number, 2); + expect( + secondCaption.start, + Duration(minutes: 0, seconds: 13, milliseconds: 0), + ); + expect( + secondCaption.end, + Duration(minutes: 0, seconds: 16, milliseconds: 0), + ); + expect(secondCaption.text, + "We're actually at the Lucern Hotel, just down the street"); + + //With styles on timestamp + final Caption lastCaption = parsedFile.captions[12]; + expect(lastCaption.number, 13); + expect( + lastCaption.start, + Duration(minutes: 0, seconds: 35, milliseconds: 500), + ); + expect( + lastCaption.end, + Duration(minutes: 0, seconds: 38, milliseconds: 0), + ); + expect(lastCaption.text, + "You know I'm so excited my glasses are falling off here."); + }); + + test('Parses VTT file with malformed input', () { + final ClosedCaptionFile parsedFile = WebVTTCaptionFile(_malformedVTT); + + expect(parsedFile.captions.length, 1); + + final Caption firstCaption = parsedFile.captions.single; + expect(firstCaption.number, 1); + expect(firstCaption.start, Duration(seconds: 13)); + expect(firstCaption.end, Duration(seconds: 16, milliseconds: 0)); + expect(firstCaption.text, 'Valid'); + }); +} + +const String _validVTT = ''' +WEBVTT Kind: captions; Language: en + +00:09.000 --> 00:11.430 +We are in New York City + +00:13.000 --> 00:16.000 +We're actually at the Lucern Hotel, just down the street + +00:16.000 --> 00:18.000 +from the American Museum of Natural History + +00:18.000 --> 00:20.000 +And with me is Neil deGrasse Tyson + +00:20.000 --> 00:22.000 +Astrophysicist, Director of the Hayden Planetarium + +00:22.000 --> 00:24.000 +at the AMNH. + +00:24.000 --> 00:26.000 +Thank you for walking down here. + +00:27.000 --> 00:30.000 +And I want to do a follow-up on the last conversation we did. + +00:30.000 --> 00:31.500 align:end size:50% +When we e-mailed— + +00:30.500 --> 00:32.500 align:start size:50% +Didn't we talk about enough in that conversation? + +00:32.000 --> 00:35.500 align:end size:50% +No! No no no no; 'cos 'cos obviously 'cos + +00:32.500 --> 00:33.500 align:start size:50% +Laughs + +00:35.500 --> 00:38.000 align:start size:50% +You know I'm so excited my glasses are falling off here. + +'''; + +const String _malformedVTT = ''' + +WEBVTT Kind: captions; Language: en + +00:09.000--> 00:11.430 +This one should be ignored because the arrow needs a space. + +00:13.000 --> 00:16.000 +Valid + +00:16.000 --> 00:8.000 +This one should be ignored because the time is missing a digit. + +'''; From c72a06892576c0cb1071b6cdb081ea0854c2ccc9 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 14:10:19 -0300 Subject: [PATCH 42/69] Fixes --- packages/video_player/video_player/lib/src/web_vtt.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index d9978c259148..1b7305f1766e 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -16,7 +16,7 @@ class WebVTTCaptionFile extends ClosedCaptionFile { WebVTTCaptionFile(this.fileContents) : _captions = _parseCaptionsFromWebVTTString(fileContents); - /// The entire body of the SubRip file. + /// The entire body of the VTT file. final String fileContents; @override @@ -139,7 +139,7 @@ Duration _parseWebVTTTimestamp(String timestampString) { ); } -// Reads on SubRip file and splits it into Lists of strings where each list is one +// Reads on VTT file and splits it into Lists of strings where each list is one // caption. List> _readWebVTTFile(String file) { final List lines = LineSplitter.split(file).toList(); From 226b9c683eb0d8bb108d95a7217eec9c2cb1bef7 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 15:24:14 -0300 Subject: [PATCH 43/69] VTT with header added --- .../video_player/example/assets/sample.vtt | 49 +++++-------------- .../example/ios/Flutter/Flutter.podspec | 18 +++++++ .../ios/Runner.xcodeproj/project.pbxproj | 17 ++----- .../video_player/example/lib/main.dart | 6 +-- .../video_player/lib/src/web_vtt.dart | 13 +++-- 5 files changed, 46 insertions(+), 57 deletions(-) create mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt index 2e5c750a7cee..2d00e17e82b4 100644 --- a/packages/video_player/video_player/example/assets/sample.vtt +++ b/packages/video_player/video_player/example/assets/sample.vtt @@ -1,40 +1,15 @@ -WEBVTT +WEBVTT - This file has cues. -00:00.500 --> 00:01.000 -We are in New York City +14 +00:00:01.815 --> 00:00:02.114 +- What? +- Where are we now? -00:01.200 --> 00:02.000 -We’re actually at the Lucern Hotel, just down the street +15 +00:01:18.171 --> 00:01:20.991 +- This is big bat country. -00:16.000 --> 00:18.000 -from the American Museum of Natural History - -00:18.000 --> 00:20.000 -And with me is Neil deGrasse Tyson - -00:20.000 --> 00:22.000 -Astrophysicist, Director of the Hayden Planetarium - -00:22.000 --> 00:24.000 -at the AMNH. - -00:24.000 --> 00:26.000 -Thank you for walking down here. - -00:27.000 --> 00:30.000 -And I want to do a follow-up on the last conversation we did. - -00:30.000 --> 00:31.500 align:right size:50% -When we e-mailed— - -00:30.500 --> 00:32.500 align:left size:50% -Didn’t we talk about enough in that conversation? - -00:32.000 --> 00:35.500 align:right size:50% -No! No no no no; 'cos 'cos obviously 'cos - -00:32.500 --> 00:33.500 align:left size:50% -Laughs - -00:35.500 --> 00:38.000 -You know I’m so excited my glasses are falling off here. \ No newline at end of file +16 +00:01:21.058 --> 00:01:23.868 +- [ Bats Screeching ] +- They won't get in your hair. They're after the bugs. \ No newline at end of file diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec new file mode 100644 index 000000000000..5ca30416bac0 --- /dev/null +++ b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.description = <<-DESC +Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. + DESC + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + s.vendored_frameworks = 'Flutter.framework' +end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 9f0a7ef189b9..8e17be05e07b 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,13 +35,11 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -63,8 +55,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -92,9 +82,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -229,7 +217,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -237,9 +225,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index bfe81b9056fb..e7e29f7c7780 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -209,9 +209,9 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { VideoPlayerController _controller; Future _loadCaptions() async { - final String fileContents = await DefaultAssetBundle.of(context) - .loadString('assets/bumble_bee_captions.srt'); - return SubRipCaptionFile(fileContents); + final String fileContents = + await DefaultAssetBundle.of(context).loadString('assets/sample.vtt'); + return WebVTTCaptionFile(fileContents); } @override diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 1b7305f1766e..22abc081943a 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -30,13 +30,18 @@ List _parseCaptionsFromWebVTTString(String file) { int number = 1; for (List captionLines in _readWebVTTFile(file)) { if (captionLines.length < 2) continue; - print(captionLines); + + // Caption has header + bool hasHeader = captionLines.length > 2; + if (hasHeader) { + number = int.parse(captionLines[0]); + } final int captionNumber = number; - final _StartAndEnd startAndEnd = - _StartAndEnd.fromWebVTTString(captionLines[0]); + final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVTTString( + hasHeader ? captionLines[1] : captionLines[0]); - final String text = captionLines.sublist(1).join('\n'); + final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); //TODO: Handle text format final String textWithoutFormat = _parseHtmlString(text); From 45547718aa5b2c18ec4a51254b3053e56c7dd6b2 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 15:25:09 -0300 Subject: [PATCH 44/69] VTT with header added --- .../video_player/example/assets/sample.vtt | 49 ++++++++++++++----- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt index 2d00e17e82b4..2e5c750a7cee 100644 --- a/packages/video_player/video_player/example/assets/sample.vtt +++ b/packages/video_player/video_player/example/assets/sample.vtt @@ -1,15 +1,40 @@ -WEBVTT - This file has cues. +WEBVTT -14 -00:00:01.815 --> 00:00:02.114 -- What? -- Where are we now? +00:00.500 --> 00:01.000 +We are in New York City -15 -00:01:18.171 --> 00:01:20.991 -- This is big bat country. +00:01.200 --> 00:02.000 +We’re actually at the Lucern Hotel, just down the street -16 -00:01:21.058 --> 00:01:23.868 -- [ Bats Screeching ] -- They won't get in your hair. They're after the bugs. \ No newline at end of file +00:16.000 --> 00:18.000 +from the American Museum of Natural History + +00:18.000 --> 00:20.000 +And with me is Neil deGrasse Tyson + +00:20.000 --> 00:22.000 +Astrophysicist, Director of the Hayden Planetarium + +00:22.000 --> 00:24.000 +at the AMNH. + +00:24.000 --> 00:26.000 +Thank you for walking down here. + +00:27.000 --> 00:30.000 +And I want to do a follow-up on the last conversation we did. + +00:30.000 --> 00:31.500 align:right size:50% +When we e-mailed— + +00:30.500 --> 00:32.500 align:left size:50% +Didn’t we talk about enough in that conversation? + +00:32.000 --> 00:35.500 align:right size:50% +No! No no no no; 'cos 'cos obviously 'cos + +00:32.500 --> 00:33.500 align:left size:50% +Laughs + +00:35.500 --> 00:38.000 +You know I’m so excited my glasses are falling off here. \ No newline at end of file From 0b7dde46a99ac720420539e52309afa7443791cd Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 15:25:41 -0300 Subject: [PATCH 45/69] VTT with header added --- packages/video_player/video_player/example/lib/main.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index e7e29f7c7780..bfe81b9056fb 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -209,9 +209,9 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { VideoPlayerController _controller; Future _loadCaptions() async { - final String fileContents = - await DefaultAssetBundle.of(context).loadString('assets/sample.vtt'); - return WebVTTCaptionFile(fileContents); + final String fileContents = await DefaultAssetBundle.of(context) + .loadString('assets/bumble_bee_captions.srt'); + return SubRipCaptionFile(fileContents); } @override From 7aa17bac0cbd90d89188473d6b301aaa17c20cd6 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 16:14:12 -0300 Subject: [PATCH 46/69] VTT with header added --- .../example/ios/Flutter/Flutter.podspec | 18 ----- .../ios/Runner.xcodeproj/project.pbxproj | 17 ++++- .../video_player/lib/src/web_vtt.dart | 74 +++++++++++-------- 3 files changed, 56 insertions(+), 53 deletions(-) delete mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec deleted file mode 100644 index 5ca30416bac0..000000000000 --- a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.description = <<-DESC -Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. - DESC - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' - s.vendored_frameworks = 'Flutter.framework' -end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 8e17be05e07b..9f0a7ef189b9 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -24,6 +28,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -35,11 +41,13 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -55,6 +63,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -82,7 +92,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -217,7 +229,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -225,12 +237,9 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 22abc081943a..b09289098950 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -4,19 +4,21 @@ import 'dart:convert'; +import 'package:html/dom.dart'; + import 'closed_caption_file.dart'; -import 'package:html/parser.dart'; +import 'package:html/parser.dart' as html_parser; -/// Represents a [ClosedCaptionFile], parsed from the WebVTT file format. -/// See: https://en.wikipedia.org/wiki/WebVTT -class WebVTTCaptionFile extends ClosedCaptionFile { +/// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. +/// See: https://en.wikipedia.org/wiki/WebVtt +class WebVttCaptionFile extends ClosedCaptionFile { /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in - /// the WebVTT file format. - /// * See: https://en.wikipedia.org/wiki/WebVTT - WebVTTCaptionFile(this.fileContents) - : _captions = _parseCaptionsFromWebVTTString(fileContents); + /// the WebVtt file format. + /// * See: https://en.wikipedia.org/wiki/WebVtt + WebVttCaptionFile(this.fileContents) + : _captions = _parseCaptionsFromWebVttString(fileContents); - /// The entire body of the VTT file. + /// The entire body of the Vtt file. final String fileContents; @override @@ -25,10 +27,10 @@ class WebVTTCaptionFile extends ClosedCaptionFile { final List _captions; } -List _parseCaptionsFromWebVTTString(String file) { +List _parseCaptionsFromWebVttString(String file) { final List captions = []; int number = 1; - for (List captionLines in _readWebVTTFile(file)) { + for (List captionLines in _readWebVttFile(file)) { if (captionLines.length < 2) continue; // Caption has header @@ -38,12 +40,17 @@ List _parseCaptionsFromWebVTTString(String file) { } final int captionNumber = number; - final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVTTString( - hasHeader ? captionLines[1] : captionLines[0]); + final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVttString( + hasHeader ? captionLines[1] : captionLines[0], + ); final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); - //TODO: Handle text format + /// TODO: Handle text formats + /// Some captions comes with anotations (information about who is speaking) and styles tags. + /// E.g: + + /// Laughs final String textWithoutFormat = _parseHtmlString(text); final Caption newCaption = Caption( @@ -68,40 +75,41 @@ class _StartAndEnd { _StartAndEnd(this.start, this.end); - // Assumes format from an VTT file. + // Assumes format from an Vtt file. // For example: // 00:09.000 --> 00:11.000 - static _StartAndEnd fromWebVTTString(String line) { + static _StartAndEnd fromWebVttString(String line) { final RegExp format = - RegExp(_webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp); + RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); if (!format.hasMatch(line)) { return _StartAndEnd(null, null); } - final List times = line.split(_webVTTArrow); + final List times = line.split(_webVttArrow); - final Duration start = _parseWebVTTTimestamp(times[0]); - final Duration end = _parseWebVTTTimestamp(times[1]); + final Duration start = _parseWebVttTimestamp(times[0]); + final Duration end = _parseWebVttTimestamp(times[1]); return _StartAndEnd(start, end); } } String _parseHtmlString(String htmlString) { - var document = parse(htmlString); - String parsedString = parse(document.body.text).documentElement.text; + final Document document = html_parser.parse(htmlString); + final String parsedString = + html_parser.parse(document.body.text).documentElement.text; return parsedString; } -// Parses a time stamp in an VTT file into a Duration. +// Parses a time stamp in an Vtt file into a Duration. // For example: // -// _parseWebVTTimestamp('00:01:08.430') +// _parseWebVttimestamp('00:01:08.430') // returns // Duration(hours: 0, minutes: 1, seconds: 8, milliseconds: 430) -Duration _parseWebVTTTimestamp(String timestampString) { - if (!RegExp(_webVTTTimeStamp).hasMatch(timestampString)) { +Duration _parseWebVttTimestamp(String timestampString) { + if (!RegExp(_webVttTimeStamp).hasMatch(timestampString)) { return null; } @@ -130,7 +138,11 @@ Duration _parseWebVTTTimestamp(String timestampString) { } List milisecondsStyles = dotSections[1].split(" "); - //TODO: Handle styles data on timestamp + + /// TODO: Handle styles + /// Some captions comes with styles about where/how the caption should be rendered. + /// E.g: + /// 00:32.500 --> 00:33.500 align:left size:50% if (milisecondsStyles.length > 1) { styles = milisecondsStyles.sublist(1); } @@ -144,9 +156,9 @@ Duration _parseWebVTTTimestamp(String timestampString) { ); } -// Reads on VTT file and splits it into Lists of strings where each list is one +// Reads on Vtt file and splits it into Lists of strings where each list is one // caption. -List> _readWebVTTFile(String file) { +List> _readWebVttFile(String file) { final List lines = LineSplitter.split(file).toList(); final List> captionStrings = >[]; @@ -169,5 +181,5 @@ List> _readWebVTTFile(String file) { return captionStrings; } -const String _webVTTTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; -const String _webVTTArrow = r' --> '; +const String _webVttTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; +const String _webVttArrow = r' --> '; From 2f686c2a89588f415d793ff7919c37ba019e34fb Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 17:06:46 -0300 Subject: [PATCH 47/69] New tests added --- .../example/assets/bumble_bee_captions.vtt | 7 + .../video_player/example/assets/sample.vtt | 40 ----- .../lib/src/closed_caption_file.dart | 4 +- .../video_player/lib/src/web_vtt.dart | 10 +- .../video_player/test/web_vtt.test.dart | 149 ++++++++++++------ 5 files changed, 120 insertions(+), 90 deletions(-) create mode 100644 packages/video_player/video_player/example/assets/bumble_bee_captions.vtt delete mode 100644 packages/video_player/video_player/example/assets/sample.vtt diff --git a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt new file mode 100644 index 000000000000..f077a18f289b --- /dev/null +++ b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt @@ -0,0 +1,7 @@ +WEBVTT + +00:00:00,200 --> 00:00:01,750 +[ Birds chirping ] + +00:00:02,300 --> 00:00:05,000 +[ Buzzing ] diff --git a/packages/video_player/video_player/example/assets/sample.vtt b/packages/video_player/video_player/example/assets/sample.vtt deleted file mode 100644 index 2e5c750a7cee..000000000000 --- a/packages/video_player/video_player/example/assets/sample.vtt +++ /dev/null @@ -1,40 +0,0 @@ -WEBVTT - -00:00.500 --> 00:01.000 -We are in New York City - -00:01.200 --> 00:02.000 -We’re actually at the Lucern Hotel, just down the street - -00:16.000 --> 00:18.000 -from the American Museum of Natural History - -00:18.000 --> 00:20.000 -And with me is Neil deGrasse Tyson - -00:20.000 --> 00:22.000 -Astrophysicist, Director of the Hayden Planetarium - -00:22.000 --> 00:24.000 -at the AMNH. - -00:24.000 --> 00:26.000 -Thank you for walking down here. - -00:27.000 --> 00:30.000 -And I want to do a follow-up on the last conversation we did. - -00:30.000 --> 00:31.500 align:right size:50% -When we e-mailed— - -00:30.500 --> 00:32.500 align:left size:50% -Didn’t we talk about enough in that conversation? - -00:32.000 --> 00:35.500 align:right size:50% -No! No no no no; 'cos 'cos obviously 'cos - -00:32.500 --> 00:33.500 align:left size:50% -Laughs - -00:35.500 --> 00:38.000 -You know I’m so excited my glasses are falling off here. \ No newline at end of file diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 8cbe5dee896f..6f146f5d0d38 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -6,7 +6,7 @@ import 'sub_rip.dart'; export 'sub_rip.dart' show SubRipCaptionFile; import 'web_vtt.dart'; -export 'web_vtt.dart' show WebVTTCaptionFile; +export 'web_vtt.dart' show WebVttCaptionFile; /// A structured representation of a parsed closed caption file. /// @@ -18,7 +18,7 @@ export 'web_vtt.dart' show WebVTTCaptionFile; /// /// See: /// * [SubRipCaptionFile]. -/// * [WebVTTCaptionFile]. +/// * [WebVttCaptionFile]. abstract class ClosedCaptionFile { /// The full list of captions from a given file. /// diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index b09289098950..d1a5985d53e7 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -29,10 +29,17 @@ class WebVttCaptionFile extends ClosedCaptionFile { List _parseCaptionsFromWebVttString(String file) { final List captions = []; + + /// Ignore metadata + List metadata = ['HEADER', 'NOTE', 'REGION', 'WEBVTT']; + int number = 1; for (List captionLines in _readWebVttFile(file)) { if (captionLines.length < 2) continue; + String metadaType = captionLines[0]?.split(' ')[0]; + if (metadata.contains(metadaType)) continue; + // Caption has header bool hasHeader = captionLines.length > 2; if (hasHeader) { @@ -47,9 +54,8 @@ List _parseCaptionsFromWebVttString(String file) { final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); /// TODO: Handle text formats - /// Some captions comes with anotations (information about who is speaking) and styles tags. + /// Some captions comes with anotations (information about who/how is the speech being delivered) and styles tags. /// E.g: - /// Laughs final String textWithoutFormat = _parseHtmlString(text); diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt.test.dart index a9e4cae2e8ed..5718b1eb23f5 100644 --- a/packages/video_player/video_player/test/web_vtt.test.dart +++ b/packages/video_player/video_player/test/web_vtt.test.dart @@ -8,46 +8,101 @@ import 'package:video_player/video_player.dart'; void main() { test('Parses VTT file', () { - final WebVTTCaptionFile parsedFile = WebVTTCaptionFile(_validVTT); + final WebVttCaptionFile parsedFile = WebVttCaptionFile(_validVTT); - expect(parsedFile.captions.length, 13); + expect(parsedFile.captions.length, 7); + //[minutes]:[seconds].[milliseconds] final Caption firstCaption = parsedFile.captions.first; expect(firstCaption.number, 1); - expect(firstCaption.start, Duration(seconds: 9)); - expect(firstCaption.end, Duration(seconds: 11, milliseconds: 430)); + expect(firstCaption.start, Duration(seconds: 1)); + expect(firstCaption.end, Duration(seconds: 2, milliseconds: 500)); expect(firstCaption.text, 'We are in New York City'); + //With multiline final Caption secondCaption = parsedFile.captions[1]; expect(secondCaption.number, 2); expect( secondCaption.start, - Duration(minutes: 0, seconds: 13, milliseconds: 0), + Duration(minutes: 0, seconds: 2, milliseconds: 800), ); expect( secondCaption.end, - Duration(minutes: 0, seconds: 16, milliseconds: 0), + Duration(minutes: 0, seconds: 3, milliseconds: 283), ); expect(secondCaption.text, - "We're actually at the Lucern Hotel, just down the street"); + "— It will perforate your stomach.\n— You could die."); - //With styles on timestamp - final Caption lastCaption = parsedFile.captions[12]; - expect(lastCaption.number, 13); + //With Long Text + final Caption thirdCaption = parsedFile.captions[2]; + expect(thirdCaption.number, 3); + expect( + thirdCaption.start, + Duration(minutes: 0, seconds: 4, milliseconds: 0), + ); + expect( + thirdCaption.end, + Duration(minutes: 0, seconds: 4, milliseconds: 900), + ); + expect(thirdCaption.text, + "The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided."); + + //With styles on html style tags + final Caption fourthCaption = parsedFile.captions[3]; + expect(fourthCaption.number, 4); + expect( + fourthCaption.start, + Duration(minutes: 0, seconds: 5, milliseconds: 200), + ); + expect( + fourthCaption.end, + Duration(minutes: 0, seconds: 6, milliseconds: 0), + ); + expect(fourthCaption.text, + "You know I'm so excited my glasses are falling off here."); + + //With format [hours]:[minutes]:[seconds].[milliseconds] + final Caption fifthCaption = parsedFile.captions[4]; + expect(fifthCaption.number, 5); + expect( + fifthCaption.start, + Duration(minutes: 0, seconds: 6, milliseconds: 050), + ); + expect( + fifthCaption.end, + Duration(minutes: 0, seconds: 6, milliseconds: 150), + ); + expect(fifthCaption.text, "I have a different time!"); + + //With custom html tag + final Caption sixthCaption = parsedFile.captions[5]; + expect(sixthCaption.number, 6); + expect( + sixthCaption.start, + Duration(minutes: 0, seconds: 6, milliseconds: 200), + ); + expect( + sixthCaption.end, + Duration(minutes: 0, seconds: 6, milliseconds: 900), + ); + expect(sixthCaption.text, "This is yellow text on a blue background"); + + //With format [hours]:[minutes].[milliseconds] + final Caption lastCaption = parsedFile.captions[6]; + expect(lastCaption.number, 7); expect( lastCaption.start, - Duration(minutes: 0, seconds: 35, milliseconds: 500), + Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 000), ); expect( lastCaption.end, - Duration(minutes: 0, seconds: 38, milliseconds: 0), + Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 900), ); - expect(lastCaption.text, - "You know I'm so excited my glasses are falling off here."); + expect(lastCaption.text, "Hour"); }); test('Parses VTT file with malformed input', () { - final ClosedCaptionFile parsedFile = WebVTTCaptionFile(_malformedVTT); + final ClosedCaptionFile parsedFile = WebVttCaptionFile(_malformedVTT); expect(parsedFile.captions.length, 1); @@ -62,44 +117,46 @@ void main() { const String _validVTT = ''' WEBVTT Kind: captions; Language: en -00:09.000 --> 00:11.430 +REGION +id:bill +width:40% +lines:3 +regionanchor:100%,100% +viewportanchor:90%,90% +scroll:up + +NOTE +This file was written by Jill. I hope +you enjoy reading it. Some things to +bear in mind: +- I was lip-reading, so the cues may +not be 100% accurate +- I didn’t pay too close attention to +when the cues should start or end. + +1 +00:01.000 --> 00:02.500 We are in New York City -00:13.000 --> 00:16.000 -We're actually at the Lucern Hotel, just down the street - -00:16.000 --> 00:18.000 -from the American Museum of Natural History - -00:18.000 --> 00:20.000 -And with me is Neil deGrasse Tyson - -00:20.000 --> 00:22.000 -Astrophysicist, Director of the Hayden Planetarium - -00:22.000 --> 00:24.000 -at the AMNH. - -00:24.000 --> 00:26.000 -Thank you for walking down here. - -00:27.000 --> 00:30.000 -And I want to do a follow-up on the last conversation we did. +2 +00:02.800 --> 00:03.283 +— It will perforate your stomach. +— You could die. -00:30.000 --> 00:31.500 align:end size:50% -When we e-mailed— +00:04.000 --> 00:04.900 +The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided. -00:30.500 --> 00:32.500 align:start size:50% -Didn't we talk about enough in that conversation? +00:05.200 --> 00:06.000 align:start size:50% +You know I'm so excited my glasses are falling off here. -00:32.000 --> 00:35.500 align:end size:50% -No! No no no no; 'cos 'cos obviously 'cos +00:00:06.050 --> 00:00:06.150 +I have a different time! -00:32.500 --> 00:33.500 align:start size:50% -Laughs +00:06.200 --> 00:06.900 +This is yellow text on a blue background -00:35.500 --> 00:38.000 align:start size:50% -You know I'm so excited my glasses are falling off here. +60:01.000 --> 60:01.900 +Hour '''; From 5d09170b278aa43bf8abc8914e547d76eac2b6c8 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:13:33 -0300 Subject: [PATCH 48/69] tests organized by group --- .../video_player/lib/src/web_vtt.dart | 11 +- .../video_player/test/web_vtt.test.dart | 230 ++++++++++-------- 2 files changed, 139 insertions(+), 102 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index d1a5985d53e7..ff76c3034df1 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -33,20 +33,21 @@ List _parseCaptionsFromWebVttString(String file) { /// Ignore metadata List metadata = ['HEADER', 'NOTE', 'REGION', 'WEBVTT']; - int number = 1; + int captionNumber = 1; for (List captionLines in _readWebVttFile(file)) { + // if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; + // if caption has header equal metadata, ignore String metadaType = captionLines[0]?.split(' ')[0]; if (metadata.contains(metadaType)) continue; // Caption has header bool hasHeader = captionLines.length > 2; - if (hasHeader) { - number = int.parse(captionLines[0]); + if (hasHeader && int.tryParse(captionLines[0]) != null) { + captionNumber = int.parse(captionLines[0]); } - final int captionNumber = number; final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVttString( hasHeader ? captionLines[1] : captionLines[0], ); @@ -68,7 +69,7 @@ List _parseCaptionsFromWebVttString(String file) { if (newCaption.start != null && newCaption.end != null) { captions.add(newCaption); - number++; + captionNumber++; } } diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt.test.dart index 5718b1eb23f5..69925da0258d 100644 --- a/packages/video_player/video_player/test/web_vtt.test.dart +++ b/packages/video_player/video_player/test/web_vtt.test.dart @@ -7,98 +7,82 @@ import 'package:video_player/src/closed_caption_file.dart'; import 'package:video_player/video_player.dart'; void main() { - test('Parses VTT file', () { - final WebVttCaptionFile parsedFile = WebVttCaptionFile(_validVTT); - - expect(parsedFile.captions.length, 7); - - //[minutes]:[seconds].[milliseconds] - final Caption firstCaption = parsedFile.captions.first; - expect(firstCaption.number, 1); - expect(firstCaption.start, Duration(seconds: 1)); - expect(firstCaption.end, Duration(seconds: 2, milliseconds: 500)); - expect(firstCaption.text, 'We are in New York City'); - - //With multiline - final Caption secondCaption = parsedFile.captions[1]; - expect(secondCaption.number, 2); - expect( - secondCaption.start, - Duration(minutes: 0, seconds: 2, milliseconds: 800), - ); - expect( - secondCaption.end, - Duration(minutes: 0, seconds: 3, milliseconds: 283), - ); - expect(secondCaption.text, - "— It will perforate your stomach.\n— You could die."); - - //With Long Text - final Caption thirdCaption = parsedFile.captions[2]; - expect(thirdCaption.number, 3); - expect( - thirdCaption.start, - Duration(minutes: 0, seconds: 4, milliseconds: 0), - ); - expect( - thirdCaption.end, - Duration(minutes: 0, seconds: 4, milliseconds: 900), - ); - expect(thirdCaption.text, - "The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided."); - - //With styles on html style tags - final Caption fourthCaption = parsedFile.captions[3]; - expect(fourthCaption.number, 4); - expect( - fourthCaption.start, - Duration(minutes: 0, seconds: 5, milliseconds: 200), - ); - expect( - fourthCaption.end, - Duration(minutes: 0, seconds: 6, milliseconds: 0), - ); - expect(fourthCaption.text, - "You know I'm so excited my glasses are falling off here."); - - //With format [hours]:[minutes]:[seconds].[milliseconds] - final Caption fifthCaption = parsedFile.captions[4]; - expect(fifthCaption.number, 5); - expect( - fifthCaption.start, - Duration(minutes: 0, seconds: 6, milliseconds: 050), - ); - expect( - fifthCaption.end, - Duration(minutes: 0, seconds: 6, milliseconds: 150), - ); - expect(fifthCaption.text, "I have a different time!"); - - //With custom html tag - final Caption sixthCaption = parsedFile.captions[5]; - expect(sixthCaption.number, 6); - expect( - sixthCaption.start, - Duration(minutes: 0, seconds: 6, milliseconds: 200), - ); - expect( - sixthCaption.end, - Duration(minutes: 0, seconds: 6, milliseconds: 900), - ); - expect(sixthCaption.text, "This is yellow text on a blue background"); - - //With format [hours]:[minutes].[milliseconds] - final Caption lastCaption = parsedFile.captions[6]; - expect(lastCaption.number, 7); - expect( - lastCaption.start, - Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 000), - ); - expect( - lastCaption.end, - Duration(hours: 60, minutes: 1, seconds: 0, milliseconds: 900), - ); - expect(lastCaption.text, "Hour"); + group('Parse VTT file', () { + WebVttCaptionFile parsedFile; + + test('with Metadata', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_metadata); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].start, Duration(seconds: 1)); + expect( + parsedFile.captions[0].end, Duration(seconds: 2, milliseconds: 500)); + expect(parsedFile.captions[0].text, 'We are in New York City'); + }); + + test('with Multiline', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_multiline); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].start, + Duration(seconds: 2, milliseconds: 800)); + expect( + parsedFile.captions[0].end, Duration(seconds: 3, milliseconds: 283)); + expect(parsedFile.captions[0].text, + "— It will perforate your stomach.\n— You could die."); + }); + + test('with styles tags', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_styles); + expect(parsedFile.captions.length, 3); + + expect(parsedFile.captions[0].start, + Duration(seconds: 5, milliseconds: 200)); + expect( + parsedFile.captions[0].end, Duration(seconds: 6, milliseconds: 000)); + expect(parsedFile.captions[0].text, + "You know I'm so excited my glasses are falling off here."); + }); + + test('with subtitling features', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_subtitling_features); + expect(parsedFile.captions.length, 3); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(seconds: 4)); + expect(parsedFile.captions.last.end, Duration(seconds: 5)); + expect(parsedFile.captions.last.text, "Transcrit par Célestes™"); + }); + + test('with [hours]:[minutes]:[seconds].[milliseconds]', () { + parsedFile = WebVttCaptionFile(_valid_vtt_with_hours); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(seconds: 1)); + expect(parsedFile.captions.last.end, Duration(seconds: 2)); + expect(parsedFile.captions.last.text, "This is a test."); + }); + + test('with [hours]:[minutes].[milliseconds]', () { + parsedFile = WebVttCaptionFile(_valid_vtt_without_seconds); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(hours: 60)); + expect(parsedFile.captions.last.end, Duration(hours: 60, minutes: 2)); + expect(parsedFile.captions.last.text, "This is a test."); + }); + + test('with [minutes]:[seconds].[milliseconds]', () { + parsedFile = WebVttCaptionFile(_valid_vtt_without_hours); + expect(parsedFile.captions.length, 1); + + expect(parsedFile.captions[0].number, 1); + expect(parsedFile.captions.last.start, Duration(seconds: 3)); + expect(parsedFile.captions.last.end, Duration(seconds: 4)); + expect(parsedFile.captions.last.text, "This is a test."); + }); }); test('Parses VTT file with malformed input', () { @@ -114,7 +98,8 @@ void main() { }); } -const String _validVTT = ''' +/// See https://www.w3.org/TR/webvtt1/#introduction-comments +const String _valid_vtt_with_metadata = ''' WEBVTT Kind: captions; Language: en REGION @@ -137,14 +122,22 @@ when the cues should start or end. 1 00:01.000 --> 00:02.500 We are in New York City +'''; + +/// See https://www.w3.org/TR/webvtt1/#introduction-multiple-lines +const String _valid_vtt_with_multiline = ''' +WEBVTT 2 00:02.800 --> 00:03.283 — It will perforate your stomach. — You could die. -00:04.000 --> 00:04.900 -The Organisation for Sample Public Service Announcements accepts no liability for the content of this advertisement, or for the consequences of any actions taken on the basis of the information provided. +'''; + +/// See https://www.w3.org/TR/webvtt1/#styling +const String _valid_vtt_with_styles = ''' +WEBVTT 00:05.200 --> 00:06.000 align:start size:50% You know I'm so excited my glasses are falling off here. @@ -155,8 +148,51 @@ The Organisation for Sample Public Service Announcements accepts no liability fo 00:06.200 --> 00:06.900 This is yellow text on a blue background -60:01.000 --> 60:01.900 -Hour +'''; + +//See https://www.w3.org/TR/webvtt1/#introduction-other-features +const String _valid_vtt_with_subtitling_features = ''' +WEBVTT + +test +00:00.000 --> 00:02.000 +This is a test. + +Slide 1 +00:00:00.000 --> 00:00:10.700 +Title Slide + +crédit de transcription +00:04.000 --> 00:05.000 +Transcrit par Célestes™ + +'''; + +/// With format [hours]:[minutes]:[seconds].[milliseconds] +const String _valid_vtt_with_hours = ''' +WEBVTT + +test +00:00:01.000 --> 00:00:02.000 +This is a test. + +'''; + +/// With format [hours]:[minutes].[milliseconds] +const String _valid_vtt_without_seconds = ''' +WEBVTT + +60:00.000 --> 60:02.000 +This is a test. + +'''; + +/// With format [minutes]:[seconds].[milliseconds] +const String _valid_vtt_without_hours = ''' +WEBVTT + +00:03.000 --> 00:04.000 +This is a test. '''; From 78098ca33e56f114054b225c5a6089bb4d33e434 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:22:47 -0300 Subject: [PATCH 49/69] Changelog updated --- .../video_player/video_player/CHANGELOG.md | 226 +++++++++--------- .../video_player/lib/src/web_vtt.dart | 13 +- .../video_player/video_player/pubspec.yaml | 2 +- .../{web_vtt.test.dart => web_vtt_test.dart} | 0 4 files changed, 125 insertions(+), 116 deletions(-) rename packages/video_player/video_player/test/{web_vtt.test.dart => web_vtt_test.dart} (100%) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index e3f49e736411..e35c860dd933 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,214 +1,216 @@ +## 0.10.11+3 + +- Support to WebVtt added. + ## 0.10.11+2 -* Fix aspectRatio calculation when size.width or size.height are zero. +- Fix aspectRatio calculation when size.width or size.height are zero. ## 0.10.11+1 -* Post-v2 Android embedding cleanups. +- Post-v2 Android embedding cleanups. ## 0.10.11 -* iOS: Fixed crash when detaching from a dying engine. -* Android: Fixed exception when detaching from any engine. +- iOS: Fixed crash when detaching from a dying engine. +- Android: Fixed exception when detaching from any engine. ## 0.10.10 -* Migrated to [pigeon](https://pub.dev/packages/pigeon). +- Migrated to [pigeon](https://pub.dev/packages/pigeon). ## 0.10.9+2 -* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). +- Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). ## 0.10.9+1 -* Readme updated to include web support and details on how to use for web +- Readme updated to include web support and details on how to use for web ## 0.10.9 -* Remove Android dependencies fallback. -* Require Flutter SDK 1.12.13+hotfix.5 or greater. -* Fix CocoaPods podspec lint warnings. +- Remove Android dependencies fallback. +- Require Flutter SDK 1.12.13+hotfix.5 or greater. +- Fix CocoaPods podspec lint warnings. ## 0.10.8+2 -* Replace deprecated `getFlutterEngine` call on Android. +- Replace deprecated `getFlutterEngine` call on Android. ## 0.10.8+1 -* Make the pedantic dev_dependency explicit. +- Make the pedantic dev_dependency explicit. ## 0.10.8 -* Added support for cleaning up the plugin if used for add-to-app (Flutter +- Added support for cleaning up the plugin if used for add-to-app (Flutter v1.15.3 is required for that feature). - ## 0.10.7 -* `VideoPlayerController` support for reading closed caption files. -* `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. +- `VideoPlayerController` support for reading closed caption files. +- `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. ## 0.10.6 -* `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read +- `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read [SubRip](https://en.wikipedia.org/wiki/SubRip) files into dart objects. ## 0.10.5+3 -* Add integration instructions for the `web` platform. +- Add integration instructions for the `web` platform. ## 0.10.5+2 -* Make sure the plugin is correctly initialized +- Make sure the plugin is correctly initialized ## 0.10.5+1 -* Fixes issue where `initialize()` `Future` stalls when failing to load source +- Fixes issue where `initialize()` `Future` stalls when failing to load source data and does not throw an error. ## 0.10.5 -* Support `web` by default. -* Require Flutter SDK 1.12.13+hotfix.4 or greater. +- Support `web` by default. +- Require Flutter SDK 1.12.13+hotfix.4 or greater. ## 0.10.4+2 -* Remove the deprecated `author:` field form pubspec.yaml -* Migrate the plugin to the pubspec platforms manifest. -* Require Flutter SDK 1.10.0 or greater. +- Remove the deprecated `author:` field form pubspec.yaml +- Migrate the plugin to the pubspec platforms manifest. +- Require Flutter SDK 1.10.0 or greater. ## 0.10.4+1 -* Fix pedantic lints. This fixes some potential race conditions in cases where +- Fix pedantic lints. This fixes some potential race conditions in cases where futures within some video_player methods weren't being awaited correctly. ## 0.10.4 -* Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. +- Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. ## 0.10.3+3 -* Add DartDocs and unit tests. +- Add DartDocs and unit tests. ## 0.10.3+2 -* Update the homepage to point to the new plugin location +- Update the homepage to point to the new plugin location ## 0.10.3+1 -* Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. -* Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. -* Updated the example app to include a new page that pop back after video is done playing. +- Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. +- Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. +- Updated the example app to include a new page that pop back after video is done playing. ## 0.10.3 -* Add support for the v2 Android embedding. This shouldn't impact existing +- Add support for the v2 Android embedding. This shouldn't impact existing functionality. ## 0.10.2+6 -* Remove AndroidX warnings. +- Remove AndroidX warnings. ## 0.10.2+5 -* Update unit test for compatibility with Flutter stable branch. +- Update unit test for compatibility with Flutter stable branch. ## 0.10.2+4 -* Define clang module for iOS. +- Define clang module for iOS. ## 0.10.2+3 -* Fix bug where formatHint was not being pass down to network sources. +- Fix bug where formatHint was not being pass down to network sources. ## 0.10.2+2 -* Update and migrate iOS example project. +- Update and migrate iOS example project. ## 0.10.2+1 -* Use DefaultHttpDataSourceFactory only when network schemas and use -DefaultHttpDataSourceFactory by default. +- Use DefaultHttpDataSourceFactory only when network schemas and use + DefaultHttpDataSourceFactory by default. ## 0.10.2 -* **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. +- **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. ## 0.10.1+7 -* Fix tests by ignoring deprecated member use. +- Fix tests by ignoring deprecated member use. ## 0.10.1+6 -* [iOS] Fixed a memory leak with notification observing. +- [iOS] Fixed a memory leak with notification observing. ## 0.10.1+5 -* Fix race condition while disposing the VideoController. +- Fix race condition while disposing the VideoController. ## 0.10.1+4 -* Fixed syntax error in README.md. +- Fixed syntax error in README.md. ## 0.10.1+3 -* Add missing template type parameter to `invokeMethod` calls. -* Bump minimum Flutter version to 1.5.0. -* Replace invokeMethod with invokeMapMethod wherever necessary. +- Add missing template type parameter to `invokeMethod` calls. +- Bump minimum Flutter version to 1.5.0. +- Replace invokeMethod with invokeMapMethod wherever necessary. ## 0.10.1+2 -* Example: Fixed tab display and added scroll view +- Example: Fixed tab display and added scroll view ## 0.10.1+1 -* iOS: Avoid deprecated `seekToTime` API +- iOS: Avoid deprecated `seekToTime` API ## 0.10.1 -* iOS: Consider a player only `initialized` once duration is determined. +- iOS: Consider a player only `initialized` once duration is determined. ## 0.10.0+8 -* iOS: Fix an issue where the player sends initialization message incorrectly. - -* Fix a few other IDE warnings. +- iOS: Fix an issue where the player sends initialization message incorrectly. +- Fix a few other IDE warnings. ## 0.10.0+7 -* Android: Fix issue where buffering status in percentage instead of milliseconds +- Android: Fix issue where buffering status in percentage instead of milliseconds -* Android: Update buffering status everytime we notify for position change +- Android: Update buffering status everytime we notify for position change ## 0.10.0+6 -* Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. +- Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. ## 0.10.0+5 -* Fixed iOS build warnings about implicit retains. +- Fixed iOS build warnings about implicit retains. ## 0.10.0+4 -* Android: Upgrade ExoPlayer to 2.9.6. +- Android: Upgrade ExoPlayer to 2.9.6. ## 0.10.0+3 -* Fix divide by zero bug on iOS. +- Fix divide by zero bug on iOS. ## 0.10.0+2 -* Added supported format documentation in README. +- Added supported format documentation in README. ## 0.10.0+1 -* Log a more detailed warning at build time about the previous AndroidX +- Log a more detailed warning at build time about the previous AndroidX migration. ## 0.10.0 -* **Breaking change**. Migrate from the deprecated original Android Support +- **Breaking change**. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to [also migrate](https://developer.android.com/jetpack/androidx/migrate) if they're @@ -216,153 +218,153 @@ DefaultHttpDataSourceFactory by default. ## 0.9.0 -* Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded - in portrait mode both in iOS and Android. +- Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded + in portrait mode both in iOS and Android. ## 0.8.0 -* Android: Upgrade ExoPlayer to 2.9.1 -* Android: Use current gradle dependencies -* Android 9 compatibility fixes for Demo App +- Android: Upgrade ExoPlayer to 2.9.1 +- Android: Use current gradle dependencies +- Android 9 compatibility fixes for Demo App ## 0.7.2 -* Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. +- Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. ## 0.7.1 -* Fixed null exception on Android when the video has a width or height of 0. +- Fixed null exception on Android when the video has a width or height of 0. ## 0.7.0 -* Add a unit test for controller and texture changes. This is a breaking change since the interface +- Add a unit test for controller and texture changes. This is a breaking change since the interface had to be cleaned up to facilitate faking. ## 0.6.6 -* Fix the condition where the player doesn't update when attached controller is changed. +- Fix the condition where the player doesn't update when attached controller is changed. ## 0.6.5 -* Eliminate race conditions around initialization: now initialization events are queued and guaranteed +- Eliminate race conditions around initialization: now initialization events are queued and guaranteed to be delivered to the Dart side. VideoPlayer widget is rebuilt upon completion of initialization. ## 0.6.4 -* Android: add support for hls, dash and ss video formats. +- Android: add support for hls, dash and ss video formats. ## 0.6.3 -* iOS: Allow audio playback in silent mode. +- iOS: Allow audio playback in silent mode. ## 0.6.2 -* `VideoPlayerController.seekTo()` is now frame accurate on both platforms. +- `VideoPlayerController.seekTo()` is now frame accurate on both platforms. ## 0.6.1 -* iOS: add missing observer removals to prevent crashes on deallocation. +- iOS: add missing observer removals to prevent crashes on deallocation. ## 0.6.0 -* Android: use ExoPlayer instead of MediaPlayer for better video format support. +- Android: use ExoPlayer instead of MediaPlayer for better video format support. ## 0.5.5 -* **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. -* Updated example in README.md. +- **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. +- Updated example in README.md. ## 0.5.4 -* Updated Gradle tooling to match Android Studio 3.1.2. +- Updated Gradle tooling to match Android Studio 3.1.2. ## 0.5.3 -* Added video buffering status. +- Added video buffering status. ## 0.5.2 -* Fixed a bug on iOS that could lead to missing initialization. -* Added support for HLS video on iOS. +- Fixed a bug on iOS that could lead to missing initialization. +- Added support for HLS video on iOS. ## 0.5.1 -* Fixed bug on video loop feature for iOS. +- Fixed bug on video loop feature for iOS. ## 0.5.0 -* Added the constructor `VideoPlayerController.file`. -* **Breaking change**. Changed `VideoPlayerController.isNetwork` to +- Added the constructor `VideoPlayerController.file`. +- **Breaking change**. Changed `VideoPlayerController.isNetwork` to an enum `VideoPlayerController.dataSourceType`. ## 0.4.1 -* Updated Flutter SDK constraint to reflect the changes in v0.4.0. +- Updated Flutter SDK constraint to reflect the changes in v0.4.0. ## 0.4.0 -* **Breaking change**. Removed the `VideoPlayerController` constructor -* Added two new factory constructors `VideoPlayerController.asset` and +- **Breaking change**. Removed the `VideoPlayerController` constructor +- Added two new factory constructors `VideoPlayerController.asset` and `VideoPlayerController.network` to respectively play a video from the Flutter assets and from a network uri. ## 0.3.0 -* **Breaking change**. Set SDK constraints to match the Flutter beta release. +- **Breaking change**. Set SDK constraints to match the Flutter beta release. ## 0.2.1 -* Fixed some signatures to account for strong mode runtime errors. -* Fixed spelling mistake in toString output. +- Fixed some signatures to account for strong mode runtime errors. +- Fixed spelling mistake in toString output. ## 0.2.0 -* **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. -* Updated documentation of when fields are available on `VideoPlayerController`. -* Updated links in README.md. +- **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. +- Updated documentation of when fields are available on `VideoPlayerController`. +- Updated links in README.md. ## 0.1.1 -* Simplified and upgraded Android project template to Android SDK 27. -* Moved Android package to io.flutter.plugins. -* Fixed warnings from the Dart 2.0 analyzer. +- Simplified and upgraded Android project template to Android SDK 27. +- Moved Android package to io.flutter.plugins. +- Fixed warnings from the Dart 2.0 analyzer. ## 0.1.0 -* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin +- **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in order to use this version of the plugin. Instructions can be found [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). ## 0.0.7 -* Added access to the video size. -* Made the VideoProgressIndicator render using a LinearProgressIndicator. +- Added access to the video size. +- Made the VideoProgressIndicator render using a LinearProgressIndicator. ## 0.0.6 -* Fixed a bug related to hot restart on Android. +- Fixed a bug related to hot restart on Android. ## 0.0.5 -* Added VideoPlayerValue.toString(). -* Added FLT prefix to iOS types. +- Added VideoPlayerValue.toString(). +- Added FLT prefix to iOS types. ## 0.0.4 -* The player will now pause on app pause, and resume on app resume. -* Implemented scrubbing on the progress bar. +- The player will now pause on app pause, and resume on app resume. +- Implemented scrubbing on the progress bar. ## 0.0.3 -* Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). -* Added VideoPlayerController.setVolume(). -* Moved the package to flutter/plugins github repo. +- Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). +- Added VideoPlayerController.setVolume(). +- Moved the package to flutter/plugins github repo. ## 0.0.2 -* Fix meta dependency version. +- Fix meta dependency version. ## 0.0.1 -* Initial release +- Initial release diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index ff76c3034df1..cf1db8f84fed 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -35,14 +35,21 @@ List _parseCaptionsFromWebVttString(String file) { int captionNumber = 1; for (List captionLines in _readWebVttFile(file)) { - // if caption has just header or time, but no text, captionLines.length will be < 1 + /// CaptionLines represent a complete caption + /// E.g + /// [ + /// [00:00.000 --> 01:24.000 align:center] + /// ['Introduction'] + /// ] + + /// if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; - // if caption has header equal metadata, ignore + /// if caption has header equal metadata, ignore String metadaType = captionLines[0]?.split(' ')[0]; if (metadata.contains(metadaType)) continue; - // Caption has header + /// Caption has header bool hasHeader = captionLines.length > 2; if (hasHeader && int.tryParse(captionLines[0]) != null) { captionNumber = int.parse(captionLines[0]); diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index b88edcde5d3c..508a5f5f5b4f 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.10.11+2 +version: 0.10.11+3 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: diff --git a/packages/video_player/video_player/test/web_vtt.test.dart b/packages/video_player/video_player/test/web_vtt_test.dart similarity index 100% rename from packages/video_player/video_player/test/web_vtt.test.dart rename to packages/video_player/video_player/test/web_vtt_test.dart From 46aecd80a67fa44ce056705a79c58039e0e74dc4 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:25:23 -0300 Subject: [PATCH 50/69] Sample vtt renamed --- packages/video_player/video_player/example/lib/main.dart | 3 ++- packages/video_player/video_player/example/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index bfe81b9056fb..8d9358a4ec4e 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -211,7 +211,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { Future _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context) .loadString('assets/bumble_bee_captions.srt'); - return SubRipCaptionFile(fileContents); + return SubRipCaptionFile( + fileContents); // For vtt files, use WebVttCaptionFile } @override diff --git a/packages/video_player/video_player/example/pubspec.yaml b/packages/video_player/video_player/example/pubspec.yaml index 283a484be711..ea6d0be60be2 100644 --- a/packages/video_player/video_player/example/pubspec.yaml +++ b/packages/video_player/video_player/example/pubspec.yaml @@ -22,4 +22,4 @@ flutter: - assets/flutter-mark-square-64.png - assets/Butterfly-209.mp4 - assets/bumble_bee_captions.srt - - assets/sample.vtt + - assets/bumble_bee_captions.vtt From cacf4dc802e9c27b22454af183ac00a6578d2b93 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:31:38 -0300 Subject: [PATCH 51/69] Sample vtt renamed --- .../video_player/video_player/CHANGELOG.md | 224 +++++++++--------- 1 file changed, 113 insertions(+), 111 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index e35c860dd933..7977562af145 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,216 +1,218 @@ ## 0.10.11+3 -- Support to WebVtt added. +* Support to WebVtt added. ## 0.10.11+2 -- Fix aspectRatio calculation when size.width or size.height are zero. +* Fix aspectRatio calculation when size.width or size.height are zero. ## 0.10.11+1 -- Post-v2 Android embedding cleanups. +* Post-v2 Android embedding cleanups. ## 0.10.11 -- iOS: Fixed crash when detaching from a dying engine. -- Android: Fixed exception when detaching from any engine. +* iOS: Fixed crash when detaching from a dying engine. +* Android: Fixed exception when detaching from any engine. ## 0.10.10 -- Migrated to [pigeon](https://pub.dev/packages/pigeon). +* Migrated to [pigeon](https://pub.dev/packages/pigeon). ## 0.10.9+2 -- Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). +* Declare API stability and compatibility with `1.0.0` (more details at: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0). ## 0.10.9+1 -- Readme updated to include web support and details on how to use for web +* Readme updated to include web support and details on how to use for web ## 0.10.9 -- Remove Android dependencies fallback. -- Require Flutter SDK 1.12.13+hotfix.5 or greater. -- Fix CocoaPods podspec lint warnings. +* Remove Android dependencies fallback. +* Require Flutter SDK 1.12.13+hotfix.5 or greater. +* Fix CocoaPods podspec lint warnings. ## 0.10.8+2 -- Replace deprecated `getFlutterEngine` call on Android. +* Replace deprecated `getFlutterEngine` call on Android. ## 0.10.8+1 -- Make the pedantic dev_dependency explicit. +* Make the pedantic dev_dependency explicit. ## 0.10.8 -- Added support for cleaning up the plugin if used for add-to-app (Flutter +* Added support for cleaning up the plugin if used for add-to-app (Flutter v1.15.3 is required for that feature). + ## 0.10.7 -- `VideoPlayerController` support for reading closed caption files. -- `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. +* `VideoPlayerController` support for reading closed caption files. +* `VideoPlayerValue` has a `caption` field for reading the current closed caption at any given time. ## 0.10.6 -- `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read +* `ClosedCaptionFile` and `SubRipCaptionFile` classes added to read [SubRip](https://en.wikipedia.org/wiki/SubRip) files into dart objects. ## 0.10.5+3 -- Add integration instructions for the `web` platform. +* Add integration instructions for the `web` platform. ## 0.10.5+2 -- Make sure the plugin is correctly initialized +* Make sure the plugin is correctly initialized ## 0.10.5+1 -- Fixes issue where `initialize()` `Future` stalls when failing to load source +* Fixes issue where `initialize()` `Future` stalls when failing to load source data and does not throw an error. ## 0.10.5 -- Support `web` by default. -- Require Flutter SDK 1.12.13+hotfix.4 or greater. +* Support `web` by default. +* Require Flutter SDK 1.12.13+hotfix.4 or greater. ## 0.10.4+2 -- Remove the deprecated `author:` field form pubspec.yaml -- Migrate the plugin to the pubspec platforms manifest. -- Require Flutter SDK 1.10.0 or greater. +* Remove the deprecated `author:` field form pubspec.yaml +* Migrate the plugin to the pubspec platforms manifest. +* Require Flutter SDK 1.10.0 or greater. ## 0.10.4+1 -- Fix pedantic lints. This fixes some potential race conditions in cases where +* Fix pedantic lints. This fixes some potential race conditions in cases where futures within some video_player methods weren't being awaited correctly. ## 0.10.4 -- Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. +* Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly. ## 0.10.3+3 -- Add DartDocs and unit tests. +* Add DartDocs and unit tests. ## 0.10.3+2 -- Update the homepage to point to the new plugin location +* Update the homepage to point to the new plugin location ## 0.10.3+1 -- Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. -- Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. -- Updated the example app to include a new page that pop back after video is done playing. +* Dispose `FLTVideoPlayer` in `onTextureUnregistered` callback on iOS. +* Add a temporary fix to dispose the `FLTVideoPlayer` with a delay to avoid race condition. +* Updated the example app to include a new page that pop back after video is done playing. ## 0.10.3 -- Add support for the v2 Android embedding. This shouldn't impact existing +* Add support for the v2 Android embedding. This shouldn't impact existing functionality. ## 0.10.2+6 -- Remove AndroidX warnings. +* Remove AndroidX warnings. ## 0.10.2+5 -- Update unit test for compatibility with Flutter stable branch. +* Update unit test for compatibility with Flutter stable branch. ## 0.10.2+4 -- Define clang module for iOS. +* Define clang module for iOS. ## 0.10.2+3 -- Fix bug where formatHint was not being pass down to network sources. +* Fix bug where formatHint was not being pass down to network sources. ## 0.10.2+2 -- Update and migrate iOS example project. +* Update and migrate iOS example project. ## 0.10.2+1 -- Use DefaultHttpDataSourceFactory only when network schemas and use - DefaultHttpDataSourceFactory by default. +* Use DefaultHttpDataSourceFactory only when network schemas and use +DefaultHttpDataSourceFactory by default. ## 0.10.2 -- **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. +* **Android Only** Adds optional VideoFormat used to signal what format the plugin should try. ## 0.10.1+7 -- Fix tests by ignoring deprecated member use. +* Fix tests by ignoring deprecated member use. ## 0.10.1+6 -- [iOS] Fixed a memory leak with notification observing. +* [iOS] Fixed a memory leak with notification observing. ## 0.10.1+5 -- Fix race condition while disposing the VideoController. +* Fix race condition while disposing the VideoController. ## 0.10.1+4 -- Fixed syntax error in README.md. +* Fixed syntax error in README.md. ## 0.10.1+3 -- Add missing template type parameter to `invokeMethod` calls. -- Bump minimum Flutter version to 1.5.0. -- Replace invokeMethod with invokeMapMethod wherever necessary. +* Add missing template type parameter to `invokeMethod` calls. +* Bump minimum Flutter version to 1.5.0. +* Replace invokeMethod with invokeMapMethod wherever necessary. ## 0.10.1+2 -- Example: Fixed tab display and added scroll view +* Example: Fixed tab display and added scroll view ## 0.10.1+1 -- iOS: Avoid deprecated `seekToTime` API +* iOS: Avoid deprecated `seekToTime` API ## 0.10.1 -- iOS: Consider a player only `initialized` once duration is determined. +* iOS: Consider a player only `initialized` once duration is determined. ## 0.10.0+8 -- iOS: Fix an issue where the player sends initialization message incorrectly. +* iOS: Fix an issue where the player sends initialization message incorrectly. + +* Fix a few other IDE warnings. -- Fix a few other IDE warnings. ## 0.10.0+7 -- Android: Fix issue where buffering status in percentage instead of milliseconds +* Android: Fix issue where buffering status in percentage instead of milliseconds -- Android: Update buffering status everytime we notify for position change +* Android: Update buffering status everytime we notify for position change ## 0.10.0+6 -- Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. +* Android: Fix missing call to `event.put("event", "completed");` which makes it possible to detect when the video is over. ## 0.10.0+5 -- Fixed iOS build warnings about implicit retains. +* Fixed iOS build warnings about implicit retains. ## 0.10.0+4 -- Android: Upgrade ExoPlayer to 2.9.6. +* Android: Upgrade ExoPlayer to 2.9.6. ## 0.10.0+3 -- Fix divide by zero bug on iOS. +* Fix divide by zero bug on iOS. ## 0.10.0+2 -- Added supported format documentation in README. +* Added supported format documentation in README. ## 0.10.0+1 -- Log a more detailed warning at build time about the previous AndroidX +* Log a more detailed warning at build time about the previous AndroidX migration. ## 0.10.0 -- **Breaking change**. Migrate from the deprecated original Android Support +* **Breaking change**. Migrate from the deprecated original Android Support Library to AndroidX. This shouldn't result in any functional changes, but it requires any Android apps using this plugin to [also migrate](https://developer.android.com/jetpack/androidx/migrate) if they're @@ -218,153 +220,153 @@ ## 0.9.0 -- Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded - in portrait mode both in iOS and Android. +* Fixed the aspect ratio and orientation of videos. Videos are now properly displayed when recorded + in portrait mode both in iOS and Android. ## 0.8.0 -- Android: Upgrade ExoPlayer to 2.9.1 -- Android: Use current gradle dependencies -- Android 9 compatibility fixes for Demo App +* Android: Upgrade ExoPlayer to 2.9.1 +* Android: Use current gradle dependencies +* Android 9 compatibility fixes for Demo App ## 0.7.2 -- Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. +* Updated to use factories on exoplayer `MediaSource`s for Android instead of the now-deprecated constructors. ## 0.7.1 -- Fixed null exception on Android when the video has a width or height of 0. +* Fixed null exception on Android when the video has a width or height of 0. ## 0.7.0 -- Add a unit test for controller and texture changes. This is a breaking change since the interface +* Add a unit test for controller and texture changes. This is a breaking change since the interface had to be cleaned up to facilitate faking. ## 0.6.6 -- Fix the condition where the player doesn't update when attached controller is changed. +* Fix the condition where the player doesn't update when attached controller is changed. ## 0.6.5 -- Eliminate race conditions around initialization: now initialization events are queued and guaranteed +* Eliminate race conditions around initialization: now initialization events are queued and guaranteed to be delivered to the Dart side. VideoPlayer widget is rebuilt upon completion of initialization. ## 0.6.4 -- Android: add support for hls, dash and ss video formats. +* Android: add support for hls, dash and ss video formats. ## 0.6.3 -- iOS: Allow audio playback in silent mode. +* iOS: Allow audio playback in silent mode. ## 0.6.2 -- `VideoPlayerController.seekTo()` is now frame accurate on both platforms. +* `VideoPlayerController.seekTo()` is now frame accurate on both platforms. ## 0.6.1 -- iOS: add missing observer removals to prevent crashes on deallocation. +* iOS: add missing observer removals to prevent crashes on deallocation. ## 0.6.0 -- Android: use ExoPlayer instead of MediaPlayer for better video format support. +* Android: use ExoPlayer instead of MediaPlayer for better video format support. ## 0.5.5 -- **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. -- Updated example in README.md. +* **Breaking change** `VideoPlayerController.initialize()` now only completes after the controller is initialized. +* Updated example in README.md. ## 0.5.4 -- Updated Gradle tooling to match Android Studio 3.1.2. +* Updated Gradle tooling to match Android Studio 3.1.2. ## 0.5.3 -- Added video buffering status. +* Added video buffering status. ## 0.5.2 -- Fixed a bug on iOS that could lead to missing initialization. -- Added support for HLS video on iOS. +* Fixed a bug on iOS that could lead to missing initialization. +* Added support for HLS video on iOS. ## 0.5.1 -- Fixed bug on video loop feature for iOS. +* Fixed bug on video loop feature for iOS. ## 0.5.0 -- Added the constructor `VideoPlayerController.file`. -- **Breaking change**. Changed `VideoPlayerController.isNetwork` to +* Added the constructor `VideoPlayerController.file`. +* **Breaking change**. Changed `VideoPlayerController.isNetwork` to an enum `VideoPlayerController.dataSourceType`. ## 0.4.1 -- Updated Flutter SDK constraint to reflect the changes in v0.4.0. +* Updated Flutter SDK constraint to reflect the changes in v0.4.0. ## 0.4.0 -- **Breaking change**. Removed the `VideoPlayerController` constructor -- Added two new factory constructors `VideoPlayerController.asset` and +* **Breaking change**. Removed the `VideoPlayerController` constructor +* Added two new factory constructors `VideoPlayerController.asset` and `VideoPlayerController.network` to respectively play a video from the Flutter assets and from a network uri. ## 0.3.0 -- **Breaking change**. Set SDK constraints to match the Flutter beta release. +* **Breaking change**. Set SDK constraints to match the Flutter beta release. ## 0.2.1 -- Fixed some signatures to account for strong mode runtime errors. -- Fixed spelling mistake in toString output. +* Fixed some signatures to account for strong mode runtime errors. +* Fixed spelling mistake in toString output. ## 0.2.0 -- **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. -- Updated documentation of when fields are available on `VideoPlayerController`. -- Updated links in README.md. +* **Breaking change**. Renamed `VideoPlayerController.isErroneous` to `VideoPlayerController.hasError`. +* Updated documentation of when fields are available on `VideoPlayerController`. +* Updated links in README.md. ## 0.1.1 -- Simplified and upgraded Android project template to Android SDK 27. -- Moved Android package to io.flutter.plugins. -- Fixed warnings from the Dart 2.0 analyzer. +* Simplified and upgraded Android project template to Android SDK 27. +* Moved Android package to io.flutter.plugins. +* Fixed warnings from the Dart 2.0 analyzer. ## 0.1.0 -- **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin +* **Breaking change**. Upgraded to Gradle 4.1 and Android Studio Gradle plugin 3.0.1. Older Flutter projects need to upgrade their Gradle setup as well in order to use this version of the plugin. Instructions can be found [here](https://github.com/flutter/flutter/wiki/Updating-Flutter-projects-to-Gradle-4.1-and-Android-Studio-Gradle-plugin-3.0.1). ## 0.0.7 -- Added access to the video size. -- Made the VideoProgressIndicator render using a LinearProgressIndicator. +* Added access to the video size. +* Made the VideoProgressIndicator render using a LinearProgressIndicator. ## 0.0.6 -- Fixed a bug related to hot restart on Android. +* Fixed a bug related to hot restart on Android. ## 0.0.5 -- Added VideoPlayerValue.toString(). -- Added FLT prefix to iOS types. +* Added VideoPlayerValue.toString(). +* Added FLT prefix to iOS types. ## 0.0.4 -- The player will now pause on app pause, and resume on app resume. -- Implemented scrubbing on the progress bar. +* The player will now pause on app pause, and resume on app resume. +* Implemented scrubbing on the progress bar. ## 0.0.3 -- Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). -- Added VideoPlayerController.setVolume(). -- Moved the package to flutter/plugins github repo. +* Made creating a VideoPlayerController a synchronous operation. Must be followed by a call to initialize(). +* Added VideoPlayerController.setVolume(). +* Moved the package to flutter/plugins github repo. ## 0.0.2 -- Fix meta dependency version. +* Fix meta dependency version. ## 0.0.1 -- Initial release +* Initial release From 552493187a0338948524471d2c8394dbf31ea375 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 19:37:25 -0300 Subject: [PATCH 52/69] Todo Styles commented --- packages/video_player/video_player/lib/src/web_vtt.dart | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index cf1db8f84fed..5f86311b4406 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -133,7 +133,6 @@ Duration _parseWebVttTimestamp(String timestampString) { int hours = 0; int minutes = 0; int seconds = 0; - List styles; if (hoursMinutesSeconds.length > 2) { // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] @@ -157,9 +156,9 @@ Duration _parseWebVttTimestamp(String timestampString) { /// Some captions comes with styles about where/how the caption should be rendered. /// E.g: /// 00:32.500 --> 00:33.500 align:left size:50% - if (milisecondsStyles.length > 1) { - styles = milisecondsStyles.sublist(1); - } + /// if (milisecondsStyles.length > 1) { + /// List styles = milisecondsStyles.sublist(1); + /// } int milliseconds = int.parse(milisecondsStyles[0]); return Duration( From 110c1152f3a84fdfaddceb307ce58d420dfcc5a6 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 21:31:07 -0300 Subject: [PATCH 53/69] Sample VTT file fixed --- .../example/assets/bumble_bee_captions.vtt | 4 ++-- .../example/ios/Flutter/Flutter.podspec | 18 ++++++++++++++++++ .../ios/Runner.xcodeproj/project.pbxproj | 17 ++++------------- .../video_player/example/lib/main.dart | 4 ++-- 4 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt index f077a18f289b..a4f115c72140 100644 --- a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt +++ b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt @@ -1,7 +1,7 @@ WEBVTT -00:00:00,200 --> 00:00:01,750 +00:00:00,200 --> 00:00:01.750 [ Birds chirping ] -00:00:02,300 --> 00:00:05,000 +00:00:02,300 --> 00:00:05.000 [ Buzzing ] diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec new file mode 100644 index 000000000000..5ca30416bac0 --- /dev/null +++ b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec @@ -0,0 +1,18 @@ +# +# NOTE: This podspec is NOT to be published. It is only used as a local source! +# + +Pod::Spec.new do |s| + s.name = 'Flutter' + s.version = '1.0.0' + s.summary = 'High-performance, high-fidelity mobile apps.' + s.description = <<-DESC +Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. + DESC + s.homepage = 'https://flutter.io' + s.license = { :type => 'MIT' } + s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } + s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } + s.ios.deployment_target = '8.0' + s.vendored_frameworks = 'Flutter.framework' +end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 9f0a7ef189b9..8e17be05e07b 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,10 +9,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -28,8 +24,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, - 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -41,13 +35,11 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -63,8 +55,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, - 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -92,9 +82,7 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -229,7 +217,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -237,9 +225,12 @@ files = ( ); inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index 8d9358a4ec4e..ca697cefebd4 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -210,8 +210,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { Future _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context) - .loadString('assets/bumble_bee_captions.srt'); - return SubRipCaptionFile( + .loadString('assets/bumble_bee_captions.vtt'); + return WebVttCaptionFile( fileContents); // For vtt files, use WebVttCaptionFile } From 808b1a06ac5ae84261f221158e80eb142eff8e3f Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 21:31:46 -0300 Subject: [PATCH 54/69] Sample VTT file fixed --- .../video_player/example/assets/bumble_bee_captions.vtt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt index a4f115c72140..1dca2c58695e 100644 --- a/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt +++ b/packages/video_player/video_player/example/assets/bumble_bee_captions.vtt @@ -1,7 +1,7 @@ WEBVTT -00:00:00,200 --> 00:00:01.750 +00:00:00.200 --> 00:00:01.750 [ Birds chirping ] -00:00:02,300 --> 00:00:05.000 +00:00:02.300 --> 00:00:05.000 [ Buzzing ] From a041f54ebff5cecbf03bb26f4908db3a92adc470 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 15 Jul 2020 21:35:55 -0300 Subject: [PATCH 55/69] Sample VTT file fixed --- .../example/ios/Flutter/Flutter.podspec | 18 ------------------ .../ios/Runner.xcodeproj/project.pbxproj | 17 +++++++++++++---- 2 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 packages/video_player/video_player/example/ios/Flutter/Flutter.podspec diff --git a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec b/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec deleted file mode 100644 index 5ca30416bac0..000000000000 --- a/packages/video_player/video_player/example/ios/Flutter/Flutter.podspec +++ /dev/null @@ -1,18 +0,0 @@ -# -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# - -Pod::Spec.new do |s| - s.name = 'Flutter' - s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.description = <<-DESC -Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS. - DESC - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } - s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } - s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' - s.vendored_frameworks = 'Flutter.framework' -end diff --git a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj index 8e17be05e07b..9f0a7ef189b9 100644 --- a/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/video_player/video_player/example/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -24,6 +28,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, + 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -35,11 +41,13 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 20721C28387E1F78689EC502 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -55,6 +63,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, + 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, B0F5C77B94E32FB72444AE9F /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -82,7 +92,9 @@ 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( + 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEBA1CF902C7004384FC /* Flutter.framework */, 9740EEB21CF90195004384FC /* Debug.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */, @@ -217,7 +229,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; 929A04F81CC936396BFCB39E /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; @@ -225,12 +237,9 @@ files = ( ); inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${PODS_ROOT}/../Flutter/Flutter.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; From 2a16e2edae861f7b2373d00ca74387372f1a3d29 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 16 Jul 2020 10:39:57 -0300 Subject: [PATCH 56/69] Credentials added --- packages/video_player/video_player/lib/src/web_vtt.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 5f86311b4406..57c27c9b2bcc 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -41,7 +41,6 @@ List _parseCaptionsFromWebVttString(String file) { /// [00:00.000 --> 01:24.000 align:center] /// ['Introduction'] /// ] - /// if caption has just header or time, but no text, captionLines.length will be < 1 if (captionLines.length < 2) continue; From de3c973f99a249d4be12f87174d4e23a5104ee25 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Fri, 17 Jul 2020 09:05:01 -0300 Subject: [PATCH 57/69] Comments fixed --- .../video_player/lib/src/web_vtt.dart | 54 +++++++++---------- .../video_player/video_player/pubspec.yaml | 1 + 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 57c27c9b2bcc..f0779faefeee 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -9,16 +9,16 @@ import 'package:html/dom.dart'; import 'closed_caption_file.dart'; import 'package:html/parser.dart' as html_parser; -/// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. -/// See: https://en.wikipedia.org/wiki/WebVtt +// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. +// See: https://en.wikipedia.org/wiki/WebVtt class WebVttCaptionFile extends ClosedCaptionFile { - /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in - /// the WebVtt file format. - /// * See: https://en.wikipedia.org/wiki/WebVtt + // Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in + // the WebVtt file format. + // * See: https://en.wikipedia.org/wiki/WebVtt WebVttCaptionFile(this.fileContents) : _captions = _parseCaptionsFromWebVttString(fileContents); - /// The entire body of the Vtt file. + // The entire body of the Vtt file. final String fileContents; @override @@ -30,25 +30,25 @@ class WebVttCaptionFile extends ClosedCaptionFile { List _parseCaptionsFromWebVttString(String file) { final List captions = []; - /// Ignore metadata + // Ignore metadata List metadata = ['HEADER', 'NOTE', 'REGION', 'WEBVTT']; int captionNumber = 1; for (List captionLines in _readWebVttFile(file)) { - /// CaptionLines represent a complete caption - /// E.g - /// [ - /// [00:00.000 --> 01:24.000 align:center] - /// ['Introduction'] - /// ] - /// if caption has just header or time, but no text, captionLines.length will be < 1 + // CaptionLines represent a complete caption + // E.g + // [ + // [00:00.000 --> 01:24.000 align:center] + // ['Introduction'] + // ] + // if caption has just header or time, but no text, captionLines.length will be 1 if (captionLines.length < 2) continue; - /// if caption has header equal metadata, ignore + // if caption has header equal metadata, ignore String metadaType = captionLines[0]?.split(' ')[0]; if (metadata.contains(metadaType)) continue; - /// Caption has header + // Caption has header bool hasHeader = captionLines.length > 2; if (hasHeader && int.tryParse(captionLines[0]) != null) { captionNumber = int.parse(captionLines[0]); @@ -60,10 +60,10 @@ List _parseCaptionsFromWebVttString(String file) { final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); - /// TODO: Handle text formats - /// Some captions comes with anotations (information about who/how is the speech being delivered) and styles tags. - /// E.g: - /// Laughs + // TODO: Handle text formats + // Some captions comes with anotations (information about who/how is the speech being delivered) and styles tags. + // E.g: + // Laughs final String textWithoutFormat = _parseHtmlString(text); final Caption newCaption = Caption( @@ -151,13 +151,13 @@ Duration _parseWebVttTimestamp(String timestampString) { List milisecondsStyles = dotSections[1].split(" "); - /// TODO: Handle styles - /// Some captions comes with styles about where/how the caption should be rendered. - /// E.g: - /// 00:32.500 --> 00:33.500 align:left size:50% - /// if (milisecondsStyles.length > 1) { - /// List styles = milisecondsStyles.sublist(1); - /// } + // TODO: Handle styles + // Some captions comes with styles about where/how the caption should be rendered. + // E.g: + // 00:32.500 --> 00:33.500 align:left size:50% + // if (milisecondsStyles.length > 1) { + // List styles = milisecondsStyles.sublist(1); + // } int milliseconds = int.parse(milisecondsStyles[0]); return Duration( diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 508a5f5f5b4f..2ef3b38571df 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -28,6 +28,7 @@ dependencies: # https://github.com/flutter/flutter/issues/46264 video_player_web: ">=0.1.1 <2.0.0" html: ^0.14.0+3 + http: ^0.12.1 flutter: sdk: flutter From 495b37c02a400b74f80117acde3e150a4d762c8f Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Fri, 17 Jul 2020 09:09:24 -0300 Subject: [PATCH 58/69] Comments fixed --- packages/video_player/video_player/pubspec.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 2ef3b38571df..508a5f5f5b4f 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -28,7 +28,6 @@ dependencies: # https://github.com/flutter/flutter/issues/46264 video_player_web: ">=0.1.1 <2.0.0" html: ^0.14.0+3 - http: ^0.12.1 flutter: sdk: flutter From fc0d04936977b634e4f2a4fbf30d54463f1e8633 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Fri, 17 Jul 2020 15:35:55 -0300 Subject: [PATCH 59/69] Version Changed --- packages/video_player/video_player/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 508a5f5f5b4f..76e6bf14d558 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.10.11+3 +version: 0.10.12 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: From 9db4ec1dfb6fcb0796335f46e30bfc7fc89c319c Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Fri, 17 Jul 2020 15:37:59 -0300 Subject: [PATCH 60/69] Version Changed --- packages/video_player/video_player/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 7977562af145..80d993fbb223 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.10.11+3 +## 0.10.12 * Support to WebVtt added. From c5bbba62efc303be07535d5bf4617f347fd60758 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Wed, 22 Jul 2020 15:29:06 -0300 Subject: [PATCH 61/69] Comments fixed --- packages/video_player/video_player/lib/src/web_vtt.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index f0779faefeee..879c07aec47f 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -9,10 +9,10 @@ import 'package:html/dom.dart'; import 'closed_caption_file.dart'; import 'package:html/parser.dart' as html_parser; -// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. +/// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. // See: https://en.wikipedia.org/wiki/WebVtt class WebVttCaptionFile extends ClosedCaptionFile { - // Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in + /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in // the WebVtt file format. // * See: https://en.wikipedia.org/wiki/WebVtt WebVttCaptionFile(this.fileContents) From 7bb0d53f77f38bc3c445b412c2ebf08dab30590a Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 20 Aug 2020 08:30:59 -0300 Subject: [PATCH 62/69] Public comments fixed --- .../video_player/lib/src/web_vtt.dart | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 879c07aec47f..8ceefd389423 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -10,15 +10,14 @@ import 'closed_caption_file.dart'; import 'package:html/parser.dart' as html_parser; /// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. -// See: https://en.wikipedia.org/wiki/WebVtt +/// See: https://en.wikipedia.org/wiki/WebVtt class WebVttCaptionFile extends ClosedCaptionFile { /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in - // the WebVtt file format. - // * See: https://en.wikipedia.org/wiki/WebVtt - WebVttCaptionFile(this.fileContents) - : _captions = _parseCaptionsFromWebVttString(fileContents); + /// the WebVtt file format. + /// * See: https://en.wikipedia.org/wiki/WebVtt + WebVttCaptionFile(this.fileContents) : _captions = _parseCaptionsFromWebVttString(fileContents); - // The entire body of the Vtt file. + /// The entire body of the Vtt file. final String fileContents; @override @@ -92,8 +91,7 @@ class _StartAndEnd { // For example: // 00:09.000 --> 00:11.000 static _StartAndEnd fromWebVttString(String line) { - final RegExp format = - RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); + final RegExp format = RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); if (!format.hasMatch(line)) { return _StartAndEnd(null, null); @@ -110,8 +108,7 @@ class _StartAndEnd { String _parseHtmlString(String htmlString) { final Document document = html_parser.parse(htmlString); - final String parsedString = - html_parser.parse(document.body.text).documentElement.text; + final String parsedString = html_parser.parse(document.body.text).documentElement.text; return parsedString; } From d47ea7f0d54389159f39652089eaaa5aa0aad99d Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 20 Aug 2020 08:37:32 -0300 Subject: [PATCH 63/69] CHANGELOG.md updated --- packages/video_player/video_player/CHANGELOG.md | 6 +++++- packages/video_player/video_player/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 80d993fbb223..6d1a3f615cfe 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,10 @@ +## 0.10.13 + +* Support to closed caption web vtt format added. + ## 0.10.12 -* Support to WebVtt added. +* Introduce VideoPlayerOptions to set the audio mix mode. ## 0.10.11+2 diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 76e6bf14d558..3816ca3ad83c 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.10.12 +version: 0.10.13 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: From db79449c1cb49e90e49335ccb6822355ca873a92 Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 20 Aug 2020 09:06:17 -0300 Subject: [PATCH 64/69] Format fixed --- packages/video_player/video_player/lib/src/web_vtt.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 8ceefd389423..ed2b5e5c2dc4 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -15,7 +15,8 @@ class WebVttCaptionFile extends ClosedCaptionFile { /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in /// the WebVtt file format. /// * See: https://en.wikipedia.org/wiki/WebVtt - WebVttCaptionFile(this.fileContents) : _captions = _parseCaptionsFromWebVttString(fileContents); + WebVttCaptionFile(this.fileContents) + : _captions = _parseCaptionsFromWebVttString(fileContents); /// The entire body of the Vtt file. final String fileContents; @@ -91,7 +92,8 @@ class _StartAndEnd { // For example: // 00:09.000 --> 00:11.000 static _StartAndEnd fromWebVttString(String line) { - final RegExp format = RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); + final RegExp format = + RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); if (!format.hasMatch(line)) { return _StartAndEnd(null, null); @@ -108,7 +110,8 @@ class _StartAndEnd { String _parseHtmlString(String htmlString) { final Document document = html_parser.parse(htmlString); - final String parsedString = html_parser.parse(document.body.text).documentElement.text; + final String parsedString = + html_parser.parse(document.body.text).documentElement.text; return parsedString; } From fa0d6dc3d4e4235c3d54233dd10734c23b9a27ce Mon Sep 17 00:00:00 2001 From: Rafael Ferraz Date: Thu, 20 Aug 2020 09:37:38 -0300 Subject: [PATCH 65/69] Version updated --- packages/video_player/video_player/CHANGELOG.md | 2 +- packages/video_player/video_player/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 6d1a3f615cfe..8652fedfd659 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.10.13 +## 0.10.12+1 * Support to closed caption web vtt format added. diff --git a/packages/video_player/video_player/pubspec.yaml b/packages/video_player/video_player/pubspec.yaml index 3816ca3ad83c..f8bb1abf19da 100644 --- a/packages/video_player/video_player/pubspec.yaml +++ b/packages/video_player/video_player/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter plugin for displaying inline video with other Flutter # 0.10.y+z is compatible with 1.0.0, if you land a breaking change bump # the version to 2.0.0. # See more details: https://github.com/flutter/flutter/wiki/Package-migration-to-1.0.0 -version: 0.10.13 +version: 0.10.12+1 homepage: https://github.com/flutter/plugins/tree/master/packages/video_player/video_player flutter: From 61a22a532b4a8fe0651b26f63427e3cc40a02d3c Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 13 Sep 2021 14:23:29 -0700 Subject: [PATCH 66/69] add issue links for TODOs --- .../video_player/video_player/lib/src/web_vtt.dart | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 3d3b362f5f2f..70801af9e45f 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -60,10 +60,8 @@ List _parseCaptionsFromWebVttString(String file) { final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); - // TODO: Handle text formats - // Some captions comes with annotations (information about who/how is the speech being delivered) and styles tags. - // E.g: - // Laughs + // TODO(cyanglaz): Handle special syntax in vtt captions. + // https://github.com/flutter/flutter/issues/90007. final String textWithoutFormat = _parseHtmlString(text); final Caption newCaption = Caption( @@ -158,10 +156,8 @@ Duration? _parseWebVttTimestamp(String timestampString) { List milisecondsStyles = dotSections[1].split(" "); - // TODO: Handle styles - // Some captions comes with styles about where/how the caption should be rendered. - // E.g: - // 00:32.500 --> 00:33.500 align:left size:50% + // TODO(cyanglaz): Handle caption styles. + // https://github.com/flutter/flutter/issues/90009. // if (milisecondsStyles.length > 1) { // List styles = milisecondsStyles.sublist(1); // } From 0d6887253797268987b5ab77b7a5b5cd7fb5a3bc Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 21 Sep 2021 11:41:42 -0700 Subject: [PATCH 67/69] review --- .../video_player/video_player/CHANGELOG.md | 2 +- .../video_player/example/lib/main.dart | 4 +- .../lib/src/closed_caption_file.dart | 8 +- .../video_player/lib/src/sub_rip.dart | 20 ++- .../video_player/lib/src/web_vtt.dart | 158 ++++++++++-------- .../video_player/test/web_vtt_test.dart | 93 ++++++++--- 6 files changed, 173 insertions(+), 112 deletions(-) diff --git a/packages/video_player/video_player/CHANGELOG.md b/packages/video_player/video_player/CHANGELOG.md index 9c721278b6ef..b7956ed3c55e 100644 --- a/packages/video_player/video_player/CHANGELOG.md +++ b/packages/video_player/video_player/CHANGELOG.md @@ -1,6 +1,6 @@ ## 2.2.1 -* Support to closed caption web vtt format added. +* Support to closed caption WebVTT format added. ## 2.2.0 diff --git a/packages/video_player/video_player/example/lib/main.dart b/packages/video_player/video_player/example/lib/main.dart index be570d1ac5aa..f035720396dd 100644 --- a/packages/video_player/video_player/example/lib/main.dart +++ b/packages/video_player/video_player/example/lib/main.dart @@ -211,8 +211,8 @@ class _BumbleBeeRemoteVideoState extends State<_BumbleBeeRemoteVideo> { Future _loadCaptions() async { final String fileContents = await DefaultAssetBundle.of(context) .loadString('assets/bumble_bee_captions.vtt'); - return WebVttCaptionFile( - fileContents); // For vtt files, use WebVttCaptionFile + return WebVTTCaptionFile( + fileContents); // For vtt files, use WebVTTCaptionFile } @override diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 5c40da35fb3b..533acf136ddd 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -6,7 +6,7 @@ import 'sub_rip.dart'; export 'sub_rip.dart' show SubRipCaptionFile; import 'web_vtt.dart'; -export 'web_vtt.dart' show WebVttCaptionFile; +export 'web_vtt.dart' show WebVTTCaptionFile; /// A structured representation of a parsed closed caption file. /// @@ -18,7 +18,7 @@ export 'web_vtt.dart' show WebVttCaptionFile; /// /// See: /// * [SubRipCaptionFile]. -/// * [WebVttCaptionFile]. +/// * [WebVTTCaptionFile]. abstract class ClosedCaptionFile { /// The full list of captions from a given file. /// @@ -48,12 +48,12 @@ class Caption { /// When in the given video should this [Caption] begin displaying. /// /// When the value is null, the caption object is invalid. - final Duration? start; + final Duration start; /// When in the given video should this [Caption] be dismissed. /// /// When the value is null, the caption object is invalid. - final Duration? end; + final Duration end; /// The actual text that should appear on screen to be read between [start] /// and [end]. diff --git a/packages/video_player/video_player/lib/src/sub_rip.dart b/packages/video_player/video_player/lib/src/sub_rip.dart index 73cd8266c2e9..5d6863f72bb8 100644 --- a/packages/video_player/video_player/lib/src/sub_rip.dart +++ b/packages/video_player/video_player/lib/src/sub_rip.dart @@ -16,6 +16,8 @@ class SubRipCaptionFile extends ClosedCaptionFile { : _captions = _parseCaptionsFromSubRipString(fileContents); /// The entire body of the SubRip file. + // TODO(cyanglaz): Remove this public member as it doesn't seem need to exist. + // https://github.com/flutter/flutter/issues/90471 final String fileContents; @override @@ -30,15 +32,15 @@ List _parseCaptionsFromSubRipString(String file) { if (captionLines.length < 3) break; final int captionNumber = int.parse(captionLines[0]); - final _StartAndEnd startAndEnd = - _StartAndEnd.fromSubRipString(captionLines[1]); + final _CaptionRange captionRange = + _CaptionRange.fromSubRipString(captionLines[1]); final String text = captionLines.sublist(2).join('\n'); final Caption newCaption = Caption( number: captionNumber, - start: startAndEnd.start, - end: startAndEnd.end, + start: captionRange.start, + end: captionRange.end, text: text, ); if (newCaption.start != newCaption.end) { @@ -49,21 +51,21 @@ List _parseCaptionsFromSubRipString(String file) { return captions; } -class _StartAndEnd { +class _CaptionRange { final Duration start; final Duration end; - _StartAndEnd(this.start, this.end); + _CaptionRange(this.start, this.end); // Assumes format from an SubRip file. // For example: // 00:01:54,724 --> 00:01:56,760 - static _StartAndEnd fromSubRipString(String line) { + static _CaptionRange fromSubRipString(String line) { final RegExp format = RegExp(_subRipTimeStamp + _subRipArrow + _subRipTimeStamp); if (!format.hasMatch(line)) { - return _StartAndEnd(Duration.zero, Duration.zero); + return _CaptionRange(Duration.zero, Duration.zero); } final List times = line.split(_subRipArrow); @@ -71,7 +73,7 @@ class _StartAndEnd { final Duration start = _parseSubRipTimestamp(times[0]); final Duration end = _parseSubRipTimestamp(times[1]); - return _StartAndEnd(start, end); + return _CaptionRange(start, end); } } diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 70801af9e45f..2316f1509e38 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2013 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. @@ -9,17 +9,14 @@ import 'package:html/dom.dart'; import 'closed_caption_file.dart'; import 'package:html/parser.dart' as html_parser; -/// Represents a [ClosedCaptionFile], parsed from the WebVtt file format. -/// See: https://en.wikipedia.org/wiki/WebVtt -class WebVttCaptionFile extends ClosedCaptionFile { +/// Represents a [ClosedCaptionFile], parsed from the WebVTT file format. +/// See: https://en.wikipedia.org/wiki/WebVTT +class WebVTTCaptionFile extends ClosedCaptionFile { /// Parses a string into a [ClosedCaptionFile], assuming [fileContents] is in - /// the WebVtt file format. - /// * See: https://en.wikipedia.org/wiki/WebVtt - WebVttCaptionFile(this.fileContents) - : _captions = _parseCaptionsFromWebVttString(fileContents); - - /// The entire body of the Vtt file. - final String fileContents; + /// the WebVTT file format. + /// * See: https://en.wikipedia.org/wiki/WebVTT + WebVTTCaptionFile(String fileContents) + : _captions = _parseCaptionsFromWebVTTString(fileContents); @override List get captions => _captions; @@ -27,140 +24,153 @@ class WebVttCaptionFile extends ClosedCaptionFile { final List _captions; } -List _parseCaptionsFromWebVttString(String file) { +List _parseCaptionsFromWebVTTString(String file) { final List captions = []; // Ignore metadata - List metadata = ['HEADER', 'NOTE', 'REGION', 'WEBVTT']; + Set metadata = {'HEADER', 'NOTE', 'REGION', 'WEBVTT'}; int captionNumber = 1; - for (List captionLines in _readWebVttFile(file)) { - // CaptionLines represent a complete caption + for (List captionLines in _readWebVTTFile(file)) { + // CaptionLines represent a complete caption. // E.g // [ // [00:00.000 --> 01:24.000 align:center] // ['Introduction'] // ] - // if caption has just header or time, but no text, captionLines.length will be 1 + // If caption has just header or time, but no text, `captionLines.length` will be 1. if (captionLines.length < 2) continue; - // if caption has header equal metadata, ignore + // If caption has header equal metadata, ignore. String metadaType = captionLines[0].split(' ')[0]; if (metadata.contains(metadaType)) continue; // Caption has header bool hasHeader = captionLines.length > 2; - if (hasHeader && int.tryParse(captionLines[0]) != null) { - captionNumber = int.parse(captionLines[0]); + if (hasHeader) { + final int? tryParseCaptionNumber = int.tryParse(captionLines[0]); + if (tryParseCaptionNumber != null) { + captionNumber = tryParseCaptionNumber; + } } - final _StartAndEnd startAndEnd = _StartAndEnd.fromWebVttString( + final _CaptionRange? captionRange = _CaptionRange.fromWebVTTString( hasHeader ? captionLines[1] : captionLines[0], ); + if (captionRange == null) { + continue; + } + final String text = captionLines.sublist(hasHeader ? 2 : 1).join('\n'); - // TODO(cyanglaz): Handle special syntax in vtt captions. + // TODO(cyanglaz): Handle special syntax in VTT captions. // https://github.com/flutter/flutter/issues/90007. - final String textWithoutFormat = _parseHtmlString(text); + final String textWithoutFormat = _extractTextFromHtml(text); final Caption newCaption = Caption( number: captionNumber, - start: startAndEnd.start, - end: startAndEnd.end, + start: captionRange.start, + end: captionRange.end, text: textWithoutFormat, ); - - if (newCaption.start != null && newCaption.end != null) { - captions.add(newCaption); - captionNumber++; - } + captions.add(newCaption); + captionNumber++; } return captions; } -class _StartAndEnd { - // When there's an error parsing the start or end, either could be null. - final Duration? start; - final Duration? end; +class _CaptionRange { + final Duration start; + final Duration end; - _StartAndEnd(this.start, this.end); + _CaptionRange(this.start, this.end); - // Assumes format from an Vtt file. + // Assumes format from an VTT file. // For example: // 00:09.000 --> 00:11.000 - static _StartAndEnd fromWebVttString(String line) { + static _CaptionRange? fromWebVTTString(String line) { final RegExp format = - RegExp(_webVttTimeStamp + _webVttArrow + _webVttTimeStamp); + RegExp(_webVTTTimeStamp + _webVTTArrow + _webVTTTimeStamp); if (!format.hasMatch(line)) { - return _StartAndEnd(null, null); + return null; } - final List times = line.split(_webVttArrow); + final List times = line.split(_webVTTArrow); - final Duration? start = _parseWebVttTimestamp(times[0]); - final Duration? end = _parseWebVttTimestamp(times[1]); + final Duration? start = _parseWebVTTTimestamp(times[0]); + final Duration? end = _parseWebVTTTimestamp(times[1]); - return _StartAndEnd(start, end); + if (start == null || end == null) { + return null; + } + + return _CaptionRange(start, end); } } -String _parseHtmlString(String htmlString) { +String _extractTextFromHtml(String htmlString) { final Document document = html_parser.parse(htmlString); final Element? body = document.body; if (body == null) { return ''; } final Element? bodyElement = html_parser.parse(body.text).documentElement; - if (bodyElement == null) { - return ''; - } - return bodyElement.text; + return bodyElement?.text ?? ''; } -// Parses a time stamp in an Vtt file into a Duration. +// Parses a time stamp in an VTT file into a Duration. // For example: // -// _parseWebVttimestamp('00:01:08.430') +// _parseWebVTTTimestamp('00:01:08.430') // returns // Duration(hours: 0, minutes: 1, seconds: 8, milliseconds: 430) -Duration? _parseWebVttTimestamp(String timestampString) { - if (!RegExp(_webVttTimeStamp).hasMatch(timestampString)) { +Duration? _parseWebVTTTimestamp(String timestampString) { + if (!RegExp(_webVTTTimeStamp).hasMatch(timestampString)) { return null; } final List dotSections = timestampString.split('.'); - final List hoursMinutesSeconds = dotSections[0].split(':'); - + final List timeComponents = dotSections[0].split(':'); + if (timeComponents.length > 3 || timeComponents.length < 2) { + // Invalid WebVTT timestamp format, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid + // WebVTT timestamp format. + return null; + } int hours = 0; - int minutes = 0; - int seconds = 0; - - if (hoursMinutesSeconds.length > 2) { - // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] - hours = int.parse(hoursMinutesSeconds[0]); - minutes = int.parse(hoursMinutesSeconds[1]); - seconds = int.parse(hoursMinutesSeconds[2]); - } else if (int.parse(hoursMinutesSeconds[0]) > 59) { - // Timestamp takes the form of [hours]:[minutes].[milliseconds] - // First position is hours as it's over 59. - hours = int.parse(hoursMinutesSeconds[0]); - minutes = int.parse(hoursMinutesSeconds[1]); - } else { - // Timestamp takes the form of [minutes]:[seconds].[milliseconds] - minutes = int.parse(hoursMinutesSeconds[0]); - seconds = int.parse(hoursMinutesSeconds[1]); + if (timeComponents.length == 3) { + final String hourString = timeComponents.removeAt(0); + if (hourString.length < 2) { + // Invalid hour component, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid + // WebVTT timestamp format. + return null; + } + hours = int.parse(hourString); + } + final int minutes = int.parse(timeComponents.removeAt(0)); + if (minutes < 0 || minutes > 59) { + // Invalid minutes component, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid + // WebVTT timestamp format. + return null; + } + final int seconds = int.parse(timeComponents.removeAt(0)); + if (seconds < 0 || seconds > 59) { + // Invalid seconds component, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid + // WebVTT timestamp format. + return null; } List milisecondsStyles = dotSections[1].split(" "); // TODO(cyanglaz): Handle caption styles. // https://github.com/flutter/flutter/issues/90009. + // ```dart // if (milisecondsStyles.length > 1) { // List styles = milisecondsStyles.sublist(1); // } + // ``` int milliseconds = int.parse(milisecondsStyles[0]); return Duration( @@ -171,9 +181,9 @@ Duration? _parseWebVttTimestamp(String timestampString) { ); } -// Reads on Vtt file and splits it into Lists of strings where each list is one +// Reads on VTT file and splits it into Lists of strings where each list is one // caption. -List> _readWebVttFile(String file) { +List> _readWebVTTFile(String file) { final List lines = LineSplitter.split(file).toList(); final List> captionStrings = >[]; @@ -196,5 +206,5 @@ List> _readWebVttFile(String file) { return captionStrings; } -const String _webVttTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; -const String _webVttArrow = r' --> '; +const String _webVTTTimeStamp = r'(\d+):(\d{2})(:\d{2})?\.(\d{3})'; +const String _webVTTArrow = r' --> '; diff --git a/packages/video_player/video_player/test/web_vtt_test.dart b/packages/video_player/video_player/test/web_vtt_test.dart index 69925da0258d..551fc7ee9e79 100644 --- a/packages/video_player/video_player/test/web_vtt_test.dart +++ b/packages/video_player/video_player/test/web_vtt_test.dart @@ -8,10 +8,10 @@ import 'package:video_player/video_player.dart'; void main() { group('Parse VTT file', () { - WebVttCaptionFile parsedFile; + WebVTTCaptionFile parsedFile; test('with Metadata', () { - parsedFile = WebVttCaptionFile(_valid_vtt_with_metadata); + parsedFile = WebVTTCaptionFile(_valid_vtt_with_metadata); expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].start, Duration(seconds: 1)); @@ -21,7 +21,7 @@ void main() { }); test('with Multiline', () { - parsedFile = WebVttCaptionFile(_valid_vtt_with_multiline); + parsedFile = WebVTTCaptionFile(_valid_vtt_with_multiline); expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].start, @@ -33,7 +33,7 @@ void main() { }); test('with styles tags', () { - parsedFile = WebVttCaptionFile(_valid_vtt_with_styles); + parsedFile = WebVTTCaptionFile(_valid_vtt_with_styles); expect(parsedFile.captions.length, 3); expect(parsedFile.captions[0].start, @@ -45,7 +45,7 @@ void main() { }); test('with subtitling features', () { - parsedFile = WebVttCaptionFile(_valid_vtt_with_subtitling_features); + parsedFile = WebVTTCaptionFile(_valid_vtt_with_subtitling_features); expect(parsedFile.captions.length, 3); expect(parsedFile.captions[0].number, 1); @@ -54,8 +54,8 @@ void main() { expect(parsedFile.captions.last.text, "Transcrit par Célestes™"); }); - test('with [hours]:[minutes]:[seconds].[milliseconds]', () { - parsedFile = WebVttCaptionFile(_valid_vtt_with_hours); + test('with [hours]:[minutes]:[seconds].[milliseconds].', () { + parsedFile = WebVTTCaptionFile(_valid_vtt_with_hours); expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].number, 1); @@ -64,29 +64,42 @@ void main() { expect(parsedFile.captions.last.text, "This is a test."); }); - test('with [hours]:[minutes].[milliseconds]', () { - parsedFile = WebVttCaptionFile(_valid_vtt_without_seconds); + test('with [minutes]:[seconds].[milliseconds].', () { + parsedFile = WebVTTCaptionFile(_valid_vtt_without_hours); expect(parsedFile.captions.length, 1); expect(parsedFile.captions[0].number, 1); - expect(parsedFile.captions.last.start, Duration(hours: 60)); - expect(parsedFile.captions.last.end, Duration(hours: 60, minutes: 2)); + expect(parsedFile.captions.last.start, Duration(seconds: 3)); + expect(parsedFile.captions.last.end, Duration(seconds: 4)); expect(parsedFile.captions.last.text, "This is a test."); }); - test('with [minutes]:[seconds].[milliseconds]', () { - parsedFile = WebVttCaptionFile(_valid_vtt_without_hours); - expect(parsedFile.captions.length, 1); + test('with invalid seconds format returns empty captions.', () { + parsedFile = WebVTTCaptionFile(_invalid_seconds); + expect(parsedFile.captions, isEmpty); + }); - expect(parsedFile.captions[0].number, 1); - expect(parsedFile.captions.last.start, Duration(seconds: 3)); - expect(parsedFile.captions.last.end, Duration(seconds: 4)); - expect(parsedFile.captions.last.text, "This is a test."); + test('with invalid minutes format returns empty captions.', () { + parsedFile = WebVTTCaptionFile(_invalid_minutes); + expect(parsedFile.captions, isEmpty); + }); + + test('with invalid hours format returns empty captions.', () { + parsedFile = WebVTTCaptionFile(_invalid_hours); + expect(parsedFile.captions, isEmpty); + }); + + test('with invalid component length returns empty captions.', () { + parsedFile = WebVTTCaptionFile(_time_component_too_long); + expect(parsedFile.captions, isEmpty); + + parsedFile = WebVTTCaptionFile(_time_component_too_short); + expect(parsedFile.captions, isEmpty); }); }); - test('Parses VTT file with malformed input', () { - final ClosedCaptionFile parsedFile = WebVttCaptionFile(_malformedVTT); + test('Parses VTT file with malformed input.', () { + final ClosedCaptionFile parsedFile = WebVTTCaptionFile(_malformedVTT); expect(parsedFile.captions.length, 1); @@ -178,8 +191,44 @@ This is a test. '''; -/// With format [hours]:[minutes].[milliseconds] -const String _valid_vtt_without_seconds = ''' +/// Invalid seconds format. +const String _invalid_seconds = ''' +WEBVTT + +60:00:000.000 --> 60:02:000.000 +This is a test. + +'''; + +/// Invalid minutes format. +const String _invalid_minutes = ''' +WEBVTT + +60:60:00.000 --> 60:70:00.000 +This is a test. + +'''; + +/// Invalid hours format. +const String _invalid_hours = ''' +WEBVTT + +5:00:00.000 --> 5:02:00.000 +This is a test. + +'''; + +/// Invalid seconds format. +const String _time_component_too_long = ''' +WEBVTT + +60:00:00:00.000 --> 60:02:00:00.000 +This is a test. + +'''; + +/// Invalid seconds format. +const String _time_component_too_short = ''' WEBVTT 60:00.000 --> 60:02.000 From 5ffff31726e74f5d45165255c5739faaafc019a4 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 21 Sep 2021 15:10:00 -0700 Subject: [PATCH 68/69] review 2 --- .../lib/src/closed_caption_file.dart | 4 ---- .../video_player/lib/src/web_vtt.dart | 17 +++++++++-------- .../video_player/lib/video_player.dart | 7 +------ 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/video_player/video_player/lib/src/closed_caption_file.dart b/packages/video_player/video_player/lib/src/closed_caption_file.dart index 533acf136ddd..e410e2652ad3 100644 --- a/packages/video_player/video_player/lib/src/closed_caption_file.dart +++ b/packages/video_player/video_player/lib/src/closed_caption_file.dart @@ -46,13 +46,9 @@ class Caption { final int number; /// When in the given video should this [Caption] begin displaying. - /// - /// When the value is null, the caption object is invalid. final Duration start; /// When in the given video should this [Caption] be dismissed. - /// - /// When the value is null, the caption object is invalid. final Duration end; /// The actual text that should appear on screen to be read between [start] diff --git a/packages/video_player/video_player/lib/src/web_vtt.dart b/packages/video_player/video_player/lib/src/web_vtt.dart index 2316f1509e38..6c4527d34d67 100644 --- a/packages/video_player/video_player/lib/src/web_vtt.dart +++ b/packages/video_player/video_player/lib/src/web_vtt.dart @@ -122,6 +122,9 @@ String _extractTextFromHtml(String htmlString) { } // Parses a time stamp in an VTT file into a Duration. +// +// Returns `null` if `timestampString` is in an invalid format. +// // For example: // // _parseWebVTTTimestamp('00:01:08.430') @@ -134,31 +137,27 @@ Duration? _parseWebVTTTimestamp(String timestampString) { final List dotSections = timestampString.split('.'); final List timeComponents = dotSections[0].split(':'); + + // Validating and parsing the `timestampString`, invalid format will result this method + // to return `null`. See https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid + // WebVTT timestamp format. if (timeComponents.length > 3 || timeComponents.length < 2) { - // Invalid WebVTT timestamp format, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid - // WebVTT timestamp format. return null; } int hours = 0; if (timeComponents.length == 3) { final String hourString = timeComponents.removeAt(0); if (hourString.length < 2) { - // Invalid hour component, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid - // WebVTT timestamp format. return null; } hours = int.parse(hourString); } final int minutes = int.parse(timeComponents.removeAt(0)); if (minutes < 0 || minutes > 59) { - // Invalid minutes component, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid - // WebVTT timestamp format. return null; } final int seconds = int.parse(timeComponents.removeAt(0)); if (seconds < 0 || seconds > 59) { - // Invalid seconds component, see https://www.w3.org/TR/webvtt1/#webvtt-timestamp for valid - // WebVTT timestamp format. return null; } @@ -171,6 +170,8 @@ Duration? _parseWebVTTTimestamp(String timestampString) { // List styles = milisecondsStyles.sublist(1); // } // ``` + // For a better readable code style, style parsing should happen before + // calling this method. See: https://github.com/flutter/plugins/pull/2878/files#r713381134. int milliseconds = int.parse(milisecondsStyles[0]); return Duration( diff --git a/packages/video_player/video_player/lib/video_player.dart b/packages/video_player/video_player/lib/video_player.dart index d4de7dfce41c..fe3437593a81 100644 --- a/packages/video_player/video_player/lib/video_player.dart +++ b/packages/video_player/video_player/lib/video_player.dart @@ -581,12 +581,7 @@ class VideoPlayerController extends ValueNotifier { // TODO: This would be more efficient as a binary search. for (final caption in _closedCaptionFile!.captions) { - final Duration? start = caption.start; - final Duration? end = caption.end; - if (start == null || end == null) { - continue; - } - if (start <= position && end >= position) { + if (caption.start <= position && caption.end >= position) { return caption; } } From c8f7c2c9c3f6412b7ada9c0a3a7e8b37278aa162 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Tue, 21 Sep 2021 21:25:37 -0700 Subject: [PATCH 69/69] fix license header --- packages/video_player/video_player/test/web_vtt_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/video_player/video_player/test/web_vtt_test.dart b/packages/video_player/video_player/test/web_vtt_test.dart index 551fc7ee9e79..59fce98c5b71 100644 --- a/packages/video_player/video_player/test/web_vtt_test.dart +++ b/packages/video_player/video_player/test/web_vtt_test.dart @@ -1,4 +1,4 @@ -// Copyright 2020 The Chromium Authors. All rights reserved. +// Copyright 2013 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.