├── .github
└── workflows
│ └── dart_build.yml
├── .gitignore
├── .gitmodules
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── runConfigurations
│ └── build_runner_build.xml
└── vcs.xml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── _docs
└── example.png
├── analysis_options.yaml
├── asset
├── fonts
│ ├── rubik-bold.ttf
│ └── rubik-regular.ttf
└── index.html
├── bin
└── frameit_chrome.dart
├── build.yaml
├── example
├── README.md
└── metadata
│ ├── android
│ ├── en-US
│ │ ├── keyword.strings
│ │ ├── samsung-galaxy-s10-plus-appbar_menu.png
│ │ └── title.strings
│ └── frameit.yaml
│ └── framed
│ └── en-US
│ ├── _preview.html
│ ├── samsung-galaxy-s10-plus-appbar_menu.png
│ └── samsungapps-samsung-galaxy-s10-plus-appbar_menu.png
├── lib
└── src
│ ├── config.dart
│ ├── config.g.dart
│ ├── frame_colors.dart
│ ├── frame_process.dart
│ ├── frameit_frame.dart
│ ├── scene.assetBundler.g.part
│ ├── scene.dart
│ └── scene.g.dart
├── pubspec.lock
├── pubspec.yaml
└── tool
├── builder.dart
└── src
└── asset_bundler.dart
/.github/workflows/dart_build.yml:
--------------------------------------------------------------------------------
1 | name: Dart Build Exe
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | strategy:
8 | matrix:
9 | # os: [macos-latest, ubuntu-latest, windows-latest]
10 | include:
11 | - os: macos-latest
12 | name: macos
13 | - os: ubuntu-latest
14 | name: linux
15 | - os: windows-latest
16 | name: windows
17 |
18 | runs-on: ${{ matrix.os }}
19 |
20 | # container:
21 | # image: google/dart:latest
22 |
23 | steps:
24 | - uses: actions/checkout@v2
25 | - uses: cedx/setup-dart@v2
26 | with:
27 | release-channel: dev
28 | - name: Install dependencies
29 | run: pub get
30 | - name: compile
31 | run: dart compile exe bin/frameit_chrome.dart -o frameit_chrome_${{ matrix.name }}.exe
32 | - name: Upload Artifact
33 | uses: actions/upload-artifact@v2.1.4
34 | with:
35 | # Artifact name
36 | name: frameit_chrome_${{ matrix.name }}.exe
37 | path: frameit_chrome_${{ matrix.name }}.exe
38 | # - name: Update release
39 | # if: startsWith(github.ref, 'refs/tags/v')
40 | # uses: johnwbyrd/update-release@v1
41 | # with:
42 | # token: ${{ secrets.GITHUB_TOKEN }}
43 | # draft: true
44 | # files: frameit_chrome_${{ matrix.name }}.exe
45 | #- uses: meeDamian/github-release@2.0
46 | # if: startsWith(github.ref, 'refs/tags/v')
47 | # with:
48 | # token: ${{ secrets.GITHUB_TOKEN }}
49 | # draft: true
50 | # files: frameit_chrome_${{ matrix.name }}.exe
51 | release:
52 | if: startsWith(github.ref, 'refs/tags/v')
53 | needs: build
54 | runs-on: ubuntu-latest
55 | steps:
56 | - name: Download all workflow run artifacts
57 | uses: actions/download-artifact@v2
58 | - run: find .
59 | - name: Create Release
60 | id: create_release
61 | uses: actions/create-release@v1
62 | env:
63 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
64 | with:
65 | tag_name: ${{ github.ref }}
66 | draft: true
67 | prerelease: false
68 | - name: Upload Release Asset
69 | uses: actions/upload-release-asset@v1
70 | env:
71 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72 | with:
73 | upload_url: ${{ steps.create_release.outputs.upload_url }}
74 | asset_path: ./frameit_chrome_linux.exe/frameit_chrome_linux.exe
75 | asset_name: ./frameit_chrome_linux.exe
76 | asset_content_type: application/octet-stream
77 | - name: Upload Release Asset
78 | uses: actions/upload-release-asset@v1
79 | env:
80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
81 | with:
82 | upload_url: ${{ steps.create_release.outputs.upload_url }}
83 | asset_path: ./frameit_chrome_windows.exe/frameit_chrome_windows.exe
84 | asset_name: ./frameit_chrome_windows.exe
85 | asset_content_type: application/octet-stream
86 | - name: Upload Release Asset
87 | uses: actions/upload-release-asset@v1
88 | env:
89 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
90 | with:
91 | upload_url: ${{ steps.create_release.outputs.upload_url }}
92 | asset_path: ./frameit_chrome_linux.exe/frameit_chrome_linux.exe
93 | asset_name: ./frameit_chrome_macos.exe
94 | asset_content_type: application/octet-stream
95 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # https://gist.github.com/hpoul/b78f7a1b3cde988f3ce4d12e954367eb
2 | #
3 | # IDEA: Allow some configuration, which is shared across users.
4 |
5 | /.idea/*
6 | !.idea/runConfigurations
7 | !.idea/runConfigurations/*
8 | !.idea/vcs.xml
9 | !.idea/dictionaries
10 | !.idea/dictionaries/*
11 | !.idea/inspectionProfiles/*
12 | !.idea/codeStyles
13 | !.idea/codeStyles/*
14 | *.iml
15 |
16 | # Java/Kotlin/Android
17 |
18 | /.gradle
19 | /build
20 | /out
21 |
22 | # JavaScript/Node.JS
23 |
24 | /node_modules
25 |
26 | # Dart
27 |
28 | /.dart_tool
29 | /.packages
30 |
31 | # VIM
32 |
33 | *.swp
34 |
35 | #### project specific ignores below
36 |
37 | /index_override.css
38 |
39 | /screenshot.png
40 |
41 | /example/metadata
42 | /example/metadata/*
43 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/frameit-frames"]
2 | path = deps/frameit-frames
3 | url = https://github.com/fastlane/frameit-frames.git
4 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | xmlns:android
41 |
42 | ^$
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | xmlns:.*
52 |
53 | ^$
54 |
55 |
56 | BY_NAME
57 |
58 |
59 |
60 |
61 |
62 |
63 | .*:id
64 |
65 | http://schemas.android.com/apk/res/android
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | .*:name
75 |
76 | http://schemas.android.com/apk/res/android
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | name
86 |
87 | ^$
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | style
97 |
98 | ^$
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | ^$
110 |
111 |
112 | BY_NAME
113 |
114 |
115 |
116 |
117 |
118 |
119 | .*
120 |
121 | http://schemas.android.com/apk/res/android
122 |
123 |
124 | ANDROID_ATTRIBUTE_ORDER
125 |
126 |
127 |
128 |
129 |
130 |
131 | .*
132 |
133 | .*
134 |
135 |
136 | BY_NAME
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/runConfigurations/build_runner_build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 1.0.0 - 2020-09-17
2 |
3 | * Initial Release 🥳
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Herbert Poul https://codeux.design/
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # frameit_chrome
2 |
3 | Embed app store and play store screenshots in device frames.
4 | Drop in replacement for fastlane frameit.
5 |
6 | > Also check out the blog article for [how to use frameit-chrome for flutter apps](https://codeux.design/articles/automatically-add-device-frames-and-text-to-app-screenshots/)
7 | > for more details: https://codeux.design/articles/automatically-add-device-frames-and-text-to-app-screenshots/
8 |
9 | It uses a simple dart script to locate localized screenshots and parses
10 | `title.strings` and `keyword.strings` and uses **chrome headless**
11 | to render the screenshot with some css and html magic.
12 |
13 | [](./_docs/example.png)
14 |
15 | * (Screenshots from [AuthPass Password Manager](https://authpass.app/))
16 |
17 | # Requirements
18 |
19 | * Dart 😅️ (for now)
20 | * Google Chrome executable. By default, will look into
21 | `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`
22 | (tested with Chrome 86.0.4240.30).
23 | * Screenshots and Device Frames.
24 |
25 | # Usage
26 |
27 | ## Create screenshots
28 |
29 | Use any tool to create non-framed screenshots, for flutter I've used
30 | [screenshots package](https://pub.dev/packages/screenshots).
31 |
32 | ## Download device frames
33 |
34 | Download device frames from https://github.com/fastlane/frameit-frames
35 | to `$HOME/frameit-frames`.
36 |
37 | ## Folder hierarchy
38 |
39 | Place your screenshots into file hierarchy as used by fastlane.
40 |
41 | ```bash
42 | metadata/
43 | android/ # <-- `--base-dir` argument
44 | en-US/
45 | -.png
46 | samsung-galaxy-s10-plus-password_generator.png # Example
47 | title.strings
48 | keyword.strings (optional)
49 | de-DE/
50 | -.png
51 | samsung-galaxy-s10-plus-password_generator.png # Example
52 | title.strings
53 | keyword.strings (optional)
54 | frameit.yaml (optional)
55 | framed/ # <-- output directory
56 | ```
57 |
58 | * In the above example: `` = `metadata/android`
59 | * Put Screenshots into `//images/`
60 | * a `title.strings` and `keyword.strings` into `//`
61 | * example `title.strings` (key must match part of the file name of the screenshot):
62 | ```
63 | "password_generator" = "Great password generator!";
64 | ```
65 |
66 | ## Install `frameit_chrome`
67 |
68 | ```shell script
69 | pub global activate frameit_chrome
70 | ```
71 |
72 | ## Run `frameit_chrome.dart`:
73 |
74 | (Assumes [frameit-frames](https://github.com/fastlane/frameit-frames) downloaded to `$HOME/frameit-frames`)
75 |
76 | ```shell script
77 | pub global run frameit_chrome --base-dir=/myproject/fastlane/metadata/android --frames-dir=$HOME/frameit-frames/latest
78 | ```
79 |
80 | On non-mac platforms or when you've installed Google Chrome in non-default location:
81 |
82 | ```shell script
83 | pub global run frameit_chrome --base-dir=/myproject/fastlane/metadata/android --frames-dir=$HOME/frameit-frames/latest --chrome-binary="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
84 | ```
85 |
86 | # Example
87 |
88 | See the [Example Directory](./example/README.md) as well as usage of AuthPass:
89 |
90 | * Android: https://github.com/authpass/authpass/tree/master/authpass/android/fastlane/metadata/android
91 | * iOS: https://github.com/authpass/authpass/tree/master/authpass/ios/fastlane/screenshots
92 |
93 | # TODO
94 |
95 | * Allow more customizations
96 | * Frame screenshot overrides.
97 | * CSS customizations.
98 |
--------------------------------------------------------------------------------
/_docs/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/authpass/frameit-chrome/1350fcac7d53831a60ebf212c74f75647a85f9cb/_docs/example.png
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # Defines a default set of lint rules enforced for
2 | # projects at Google. For details and rationale,
3 | # see https://github.com/dart-lang/pedantic#enabled-lints.
4 | include: package:pedantic/analysis_options.yaml
5 |
6 | analyzer:
7 | strong-mode:
8 | implicit-casts: false
9 | implicit-dynamic: false
10 | errors:
11 | # treat missing required parameters as a warning (not a hint)
12 | missing_required_param: warning
13 | # treat missing returns as a warning (not a hint)
14 | missing_return: warning
15 | # allow having TODOs in the code
16 | todo: ignore
17 | exclude:
18 | - lib/**/*.g.dart
19 |
20 | linter:
21 | rules:
22 | # these rules are documented on and in the same order as
23 | # the Dart Lint rules page to make maintenance easier
24 | # http://dart-lang.github.io/linter/lints/
25 |
26 | # HP mostly in sync with https://github.com/flutter/flutter/blob/master/analysis_options.yaml
27 |
28 | - always_declare_return_types
29 | - always_put_control_body_on_new_line
30 | # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
31 | - always_require_non_null_named_parameters
32 | # - always_specify_types
33 | - annotate_overrides
34 | # - avoid_annotating_with_dynamic # not yet tested
35 | # - avoid_as
36 | - avoid_bool_literals_in_conditional_expressions
37 | # - avoid_catches_without_on_clauses # not yet tested
38 | # - avoid_catching_errors # not yet tested
39 | # - avoid_classes_with_only_static_members # not yet tested
40 | # - avoid_double_and_int_checks # only useful when targeting JS runtime
41 | - avoid_empty_else
42 | - avoid_field_initializers_in_const_classes
43 | - avoid_function_literals_in_foreach_calls
44 | # - avoid_implementing_value_types # not yet tested
45 | - avoid_init_to_null
46 | # - avoid_js_rounded_ints # only useful when targeting JS runtime
47 | - avoid_null_checks_in_equality_operators
48 | # - avoid_positional_boolean_parameters # not yet tested
49 | # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
50 | - avoid_relative_lib_imports
51 | - avoid_renaming_method_parameters
52 | - avoid_return_types_on_setters
53 | # - avoid_returning_null # not yet tested
54 | # - avoid_returning_null_for_future # not yet tested
55 | - avoid_returning_null_for_void
56 | # - avoid_returning_this # not yet tested
57 | # - avoid_setters_without_getters # not yet tested
58 | # - avoid_shadowing_type_parameters # not yet tested
59 | # - avoid_single_cascade_in_expression_statements # not yet tested
60 | - avoid_slow_async_io
61 | - avoid_types_as_parameter_names
62 | # - avoid_types_on_closure_parameters # not yet tested
63 | - avoid_unused_constructor_parameters
64 | - avoid_void_async
65 | - await_only_futures
66 | - camel_case_extensions
67 | - camel_case_types
68 | - cancel_subscriptions
69 | # - cascade_invocations # not yet tested
70 | # - close_sinks # not reliable enough
71 | # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765
72 | # - constant_identifier_names # https://github.com/dart-lang/linter/issues/204
73 | - control_flow_in_finally
74 | - curly_braces_in_flow_control_structures
75 | # - diagnostic_describe_all_properties # not yet tested
76 | - directives_ordering
77 | - empty_catches
78 | - empty_constructor_bodies
79 | - empty_statements
80 | # - file_names # not yet tested
81 | # - flutter_style_todos TODO(HP)
82 | - hash_and_equals
83 | - implementation_imports
84 | # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
85 | - iterable_contains_unrelated_type
86 | # - join_return_with_assignment # not yet tested
87 | - library_names
88 | - library_prefixes
89 | # - lines_longer_than_80_chars # not yet tested
90 | - list_remove_unrelated_type
91 | # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
92 | - no_adjacent_strings_in_list
93 | - no_duplicate_case_values
94 | - non_constant_identifier_names
95 | # - null_closures # not yet tested
96 | # - omit_local_variable_types # opposite of always_specify_types
97 | # - one_member_abstracts # too many false positives
98 | # - only_throw_errors # https://github.com/flutter/flutter/issues/5792
99 | - overridden_fields
100 | - package_api_docs
101 | - package_names
102 | - package_prefixed_library_names
103 | # - parameter_assignments # we do this commonly
104 | - prefer_adjacent_string_concatenation
105 | - prefer_asserts_in_initializer_lists
106 | # - prefer_asserts_with_message # not yet tested
107 | - prefer_collection_literals
108 | - prefer_conditional_assignment
109 | - prefer_const_constructors
110 | - prefer_const_constructors_in_immutables
111 | - prefer_const_declarations
112 | - prefer_const_literals_to_create_immutables
113 | # - prefer_constructors_over_static_methods # not yet tested
114 | - prefer_contains
115 | # - prefer_double_quotes # opposite of prefer_single_quotes
116 | - prefer_equal_for_default_values
117 | # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods
118 | - prefer_final_fields
119 | - prefer_final_in_for_each
120 | - prefer_final_locals
121 | - prefer_for_elements_to_map_fromIterable
122 | - prefer_foreach
123 | # - prefer_function_declarations_over_variables # not yet tested
124 | - prefer_generic_function_type_aliases
125 | # - prefer_if_elements_to_conditional_expressions # not yet tested
126 | - prefer_if_null_operators
127 | - prefer_initializing_formals
128 | - prefer_inlined_adds
129 | # - prefer_int_literals # not yet tested
130 | # - prefer_interpolation_to_compose_strings # not yet tested
131 | - prefer_is_empty
132 | - prefer_is_not_empty
133 | - prefer_iterable_whereType
134 | # - prefer_mixin # https://github.com/dart-lang/language/issues/32
135 | # - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932
136 | - prefer_single_quotes
137 | - prefer_spread_collections
138 | - prefer_typing_uninitialized_variables
139 | - prefer_void_to_null
140 | # - provide_deprecation_message # not yet tested
141 | # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
142 | - recursive_getters
143 | - slash_for_doc_comments
144 | # - sort_child_properties_last # not yet tested
145 | - sort_constructors_first
146 | - sort_pub_dependencies
147 | - sort_unnamed_constructors_first
148 | - test_types_in_equals
149 | - throw_in_finally
150 | # - type_annotate_public_apis # subset of always_specify_types
151 | - type_init_formals
152 | # - unawaited_futures # https://github.com/flutter/flutter/issues/5793
153 | # - unnecessary_await_in_return # not yet tested
154 | - unnecessary_brace_in_string_interps
155 | - unnecessary_const
156 | - unnecessary_getters_setters
157 | # - unnecessary_lambdas # https://github.com/dart-lang/linter/issues/498
158 | - unnecessary_new
159 | - unnecessary_null_aware_assignments
160 | - unnecessary_null_in_if_null_operators
161 | - unnecessary_overrides
162 | #- unnecessary_parenthesis HP: I like parenthesis :-)
163 | - unnecessary_statements
164 | - unnecessary_this
165 | - unrelated_type_equality_checks
166 | # - unsafe_html # not yet tested
167 | - use_full_hex_values_for_flutter_colors
168 | # - use_function_type_syntax_for_parameters # not yet tested
169 | - use_rethrow_when_possible
170 | # - use_setters_to_change_properties # not yet tested
171 | # - use_string_buffers # https://github.com/dart-lang/linter/pull/664
172 | # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
173 | - valid_regexps
174 | - void_checks
175 |
176 |
--------------------------------------------------------------------------------
/asset/fonts/rubik-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/authpass/frameit-chrome/1350fcac7d53831a60ebf212c74f75647a85f9cb/asset/fonts/rubik-bold.ttf
--------------------------------------------------------------------------------
/asset/fonts/rubik-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/authpass/frameit-chrome/1350fcac7d53831a60ebf212c74f75647a85f9cb/asset/fonts/rubik-regular.ttf
--------------------------------------------------------------------------------
/asset/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
176 |
177 |
178 |
179 |
180 |
181 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/bin/frameit_chrome.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 | import 'dart:io';
3 |
4 | import 'package:args/args.dart';
5 | import 'package:frameit_chrome/src/config.dart';
6 | import 'package:frameit_chrome/src/frame_process.dart';
7 | import 'package:frameit_chrome/src/frameit_frame.dart';
8 | import 'package:frameit_chrome/src/scene.dart';
9 | import 'package:logging/logging.dart';
10 | import 'package:logging_appenders/logging_appenders.dart';
11 | import 'package:path/path.dart' as path;
12 | import 'package:quiver/check.dart';
13 | import 'package:yaml/yaml.dart';
14 |
15 | final _logger = Logger('frame');
16 |
17 | const chromeBinaryMac =
18 | '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
19 |
20 | const ARG_BASE_DIR = 'base-dir';
21 | const ARG_FRAMES_DIR = 'frames-dir';
22 | const ARG_CHROME_BINARY = 'chrome-binary';
23 | const ARG_PIXEL_RATIO = 'pixel-ratio';
24 |
25 | const FRAMES_REPO = 'https://github.com/fastlane/frameit-frames';
26 |
27 | Future main(List args) async {
28 | PrintAppender.setupLogging(stderrLevel: Level.WARNING);
29 |
30 | final parser = ArgParser();
31 | parser.addOption(ARG_BASE_DIR,
32 | help: 'base dir of screenshots. (android/fastlane/metadata/android)');
33 | parser.addOption(ARG_FRAMES_DIR,
34 | help:
35 | 'dir with frames from $FRAMES_REPO (e.g. checkout/frameit-frames/latest)');
36 | parser.addOption(ARG_CHROME_BINARY,
37 | help: 'Path to chrome binary.', defaultsTo: chromeBinaryMac);
38 | parser.addOption(ARG_PIXEL_RATIO,
39 | valueHelp: '2',
40 | help: 'Device pixel to real pixel ratio.',
41 | defaultsTo: '2');
42 | final result = parser.parse(args);
43 |
44 | final baseDir = result[ARG_BASE_DIR] as String;
45 | final framesDir = result[ARG_FRAMES_DIR] as String;
46 | final chromeBinary = result[ARG_CHROME_BINARY] as String;
47 | final pixelRatio = double.tryParse(result[ARG_PIXEL_RATIO].toString());
48 | if (baseDir == null ||
49 | framesDir == null ||
50 | chromeBinary == null ||
51 | pixelRatio == null) {
52 | print(parser.usage);
53 | exit(1);
54 | }
55 | if (!File(chromeBinary).existsSync()) {
56 | _logger.severe('Unable to find chrome at $chromeBinary');
57 | print(parser.usage);
58 | exit(1);
59 | }
60 | try {
61 | await runFrame(baseDir, framesDir, chromeBinary, pixelRatio);
62 | } catch (e, stackTrace) {
63 | _logger.severe('Error while creating frames.', e, stackTrace);
64 | }
65 | }
66 |
67 | final localePattern = RegExp('^[a-z]{2}-[A-Z]{2}');
68 |
69 | Future runFrame(String baseDir, String framesDirPath, String chromeBinary,
70 | double pixelRatio) async {
71 | // validate folder.
72 | // find strings files (title.strings and keywords.strings)
73 | final dir = Directory(baseDir);
74 | checkArgument(dir.existsSync(), message: 'directory $dir does not exist.');
75 | final outDir = Directory(path.join(dir.parent.path, 'framed'));
76 | if (outDir.existsSync()) {
77 | _logger.info('Deleting output directory $outDir');
78 | await outDir.delete(recursive: true);
79 | }
80 | final config = await FrameConfig.load(baseDir);
81 | await outDir.create(recursive: true);
82 | final framesDir = Directory(framesDirPath);
83 | checkArgument(framesDir.existsSync(),
84 | message: '$framesDir does not exist (download $FRAMES_REPO).');
85 | final framesProvider = await FramesProvider.create(framesDir);
86 |
87 | final tempDir = await Directory.systemTemp.createTemp('frameit_chrome');
88 | _logger.fine('Using ${tempDir.path}');
89 | await Assets.extractTo(tempDir);
90 |
91 | final frameProcess = FrameProcess(
92 | config: config,
93 | chromeBinary: chromeBinary,
94 | framesProvider: framesProvider,
95 | pixelRatio: pixelRatio,
96 | workingDir: Directory(path.join(tempDir.path, 'asset')),
97 | );
98 |
99 | await for (final localeDir in dir.list()) {
100 | if (localeDir is! Directory) {
101 | // _logger.info('not a director ${localeDir}');
102 | continue;
103 | }
104 | if (!localePattern.hasMatch(path.basename(localeDir.path))) {
105 | _logger.finer('dir is not a locale: ${path.basename(localeDir.path)}');
106 | continue;
107 | }
108 |
109 | final titleStrings =
110 | await _parseStrings(File(path.join(localeDir.path, 'title.strings')));
111 | final keywordStrings = await _parseStrings(
112 | File(path.join(localeDir.path, 'keyword.strings'))) ??
113 | {};
114 |
115 | if (titleStrings == null) {
116 | _logger.warning('Locale without titles: $localeDir');
117 | continue;
118 | }
119 | _logger.finer('for ${path.basename(localeDir.path)} Found titles: '
120 | '${const JsonEncoder.withIndent(' ').convert(titleStrings)}');
121 |
122 | final imagesDir = path.join(localeDir.path);
123 | final imagesOutDir =
124 | path.join(outDir.path, path.relative(imagesDir, from: dir.path));
125 | await frameProcess.processScreenshots(
126 | Directory(imagesDir),
127 | Directory(imagesOutDir),
128 | titleStrings,
129 | keywordStrings,
130 | );
131 | }
132 |
133 | _logger.fine('Deleting temp directory.');
134 | await tempDir.delete(recursive: true);
135 | }
136 |
137 | Future