Add support for asset variants and resolution-aware images
This commit is contained in:
parent
5ff2b1a19b
commit
9572b4969f
BIN
examples/widgets/assets/1.5x/starcircle.png
Normal file
BIN
examples/widgets/assets/1.5x/starcircle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
examples/widgets/assets/2.0x/starcircle.png
Normal file
BIN
examples/widgets/assets/2.0x/starcircle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
examples/widgets/assets/3.0x/starcircle.png
Normal file
BIN
examples/widgets/assets/3.0x/starcircle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
examples/widgets/assets/4.0x/starcircle.png
Normal file
BIN
examples/widgets/assets/4.0x/starcircle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
BIN
examples/widgets/assets/starcircle.png
Normal file
BIN
examples/widgets/assets/starcircle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -1,4 +1,6 @@
|
||||
name: widgets
|
||||
assets:
|
||||
- assets/starcircle.png
|
||||
material-design-icons:
|
||||
- name: action/account_circle
|
||||
- name: action/alarm
|
||||
|
86
examples/widgets/resolution_awareness.dart
Normal file
86
examples/widgets/resolution_awareness.dart
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright (c) 2016, the Flutter 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:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ExampleApp extends StatefulComponent {
|
||||
ExampleState createState() => new ExampleState();
|
||||
}
|
||||
|
||||
const List<double> _ratios = const <double>[ 1.0, 1.8, 1.3, 2.4, 2.5, 2.6, 3.9 ];
|
||||
|
||||
class ExampleState extends State<ExampleApp> {
|
||||
|
||||
int _index = 0;
|
||||
double _ratio = _ratios[0];
|
||||
|
||||
final EdgeDims padding = new EdgeDims.TRBL(
|
||||
ui.window.padding.top,
|
||||
ui.window.padding.right,
|
||||
ui.window.padding.bottom,
|
||||
ui.window.padding.left
|
||||
);
|
||||
|
||||
void _handlePressed() {
|
||||
setState(() {
|
||||
_index++;
|
||||
_index = _index % _ratios.length;
|
||||
_ratio = _ratios[_index];
|
||||
});
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
const double size = 200.0; // 200 logical pixels
|
||||
TextStyle style = new TextStyle(color: const Color(0xFF0000000));
|
||||
return new MediaQuery(
|
||||
data: new MediaQueryData(
|
||||
size: ui.window.size,
|
||||
devicePixelRatio: _ratio,
|
||||
padding: padding
|
||||
),
|
||||
child: new AssetVendor(
|
||||
bundle: rootBundle,
|
||||
devicePixelRatio: _ratio,
|
||||
child: new Material(
|
||||
child: new Padding(
|
||||
padding: const EdgeDims.symmetric(vertical: 48.0),
|
||||
child: new Column(
|
||||
children: <Widget>[
|
||||
new AssetImage(
|
||||
name: 'assets/2.0x/starcircle.png',
|
||||
height: size,
|
||||
width: size,
|
||||
fit: ImageFit.fill
|
||||
),
|
||||
new Text('Image designed for pixel ratio 2.0', style: style),
|
||||
new AssetImage(
|
||||
name: 'assets/starcircle.png',
|
||||
height: size,
|
||||
width: size,
|
||||
fit: ImageFit.fill
|
||||
),
|
||||
new Text(
|
||||
'Image variant for pixel ratio: ' + _ratio.toString(),
|
||||
style: style
|
||||
),
|
||||
new RaisedButton(
|
||||
child: new Text('Change pixel ratio', style: style),
|
||||
onPressed: _handlePressed
|
||||
)
|
||||
],
|
||||
justifyContent: FlexJustifyContent.spaceBetween
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
runApp(new ExampleApp());
|
||||
}
|
@ -65,13 +65,10 @@ class Icon extends StatelessComponent {
|
||||
category = parts[0];
|
||||
subtype = parts[1];
|
||||
}
|
||||
// TODO(eseidel): This clearly isn't correct. Not sure what would be.
|
||||
// Should we use the ios images on ios?
|
||||
String density = 'drawable-xxhdpi';
|
||||
String colorSuffix = _getColorSuffix(context);
|
||||
int iconSize = _kIconSize[size];
|
||||
return new AssetImage(
|
||||
name: '$category/$density/ic_${subtype}_${colorSuffix}_${iconSize}dp.png',
|
||||
name: '$category/ic_${subtype}_${colorSuffix}_${iconSize}dp.png',
|
||||
width: iconSize.toDouble(),
|
||||
height: iconSize.toDouble(),
|
||||
color: color
|
||||
|
@ -160,8 +160,9 @@ class _MaterialAppState extends State<MaterialApp> implements BindingObserver {
|
||||
duration: kThemeAnimationDuration,
|
||||
child: new DefaultTextStyle(
|
||||
style: _errorTextStyle,
|
||||
child: new DefaultAssetBundle(
|
||||
child: new AssetVendor(
|
||||
bundle: _defaultBundle,
|
||||
devicePixelRatio: ui.window.devicePixelRatio,
|
||||
child: new Title(
|
||||
title: config.title,
|
||||
color: theme.primaryColor,
|
||||
|
@ -42,24 +42,7 @@ class NetworkAssetBundle extends AssetBundle {
|
||||
}
|
||||
}
|
||||
|
||||
Future _fetchAndUnpackBundle(String relativeUrl, AssetBundleProxy bundle) async {
|
||||
core.MojoDataPipeConsumer bundleData = (await fetchUrl(relativeUrl)).body;
|
||||
AssetUnpackerProxy unpacker = new AssetUnpackerProxy.unbound();
|
||||
shell.connectToService("mojo:asset_bundle", unpacker);
|
||||
unpacker.ptr.unpackZipStream(bundleData, bundle);
|
||||
unpacker.close();
|
||||
}
|
||||
|
||||
class MojoAssetBundle extends AssetBundle {
|
||||
MojoAssetBundle(this._bundle);
|
||||
|
||||
factory MojoAssetBundle.fromNetwork(String relativeUrl) {
|
||||
AssetBundleProxy bundle = new AssetBundleProxy.unbound();
|
||||
_fetchAndUnpackBundle(relativeUrl, bundle);
|
||||
return new MojoAssetBundle(bundle);
|
||||
}
|
||||
|
||||
AssetBundleProxy _bundle;
|
||||
abstract class CachingAssetBundle extends AssetBundle {
|
||||
Map<String, ImageResource> _imageCache = new Map<String, ImageResource>();
|
||||
Map<String, Future<String>> _stringCache = new Map<String, Future<String>>();
|
||||
|
||||
@ -79,15 +62,35 @@ class MojoAssetBundle extends AssetBundle {
|
||||
return new String.fromCharCodes(new Uint8List.view(data.buffer));
|
||||
}
|
||||
|
||||
Future<core.MojoDataPipeConsumer> load(String key) async {
|
||||
return (await _bundle.ptr.getAsStream(key)).assetData;
|
||||
}
|
||||
|
||||
Future<String> loadString(String key) {
|
||||
return _stringCache.putIfAbsent(key, () => _fetchString(key));
|
||||
}
|
||||
}
|
||||
|
||||
class MojoAssetBundle extends CachingAssetBundle {
|
||||
MojoAssetBundle(this._bundle);
|
||||
|
||||
factory MojoAssetBundle.fromNetwork(String relativeUrl) {
|
||||
AssetBundleProxy bundle = new AssetBundleProxy.unbound();
|
||||
_fetchAndUnpackBundle(relativeUrl, bundle);
|
||||
return new MojoAssetBundle(bundle);
|
||||
}
|
||||
|
||||
static Future _fetchAndUnpackBundle(String relativeUrl, AssetBundleProxy bundle) async {
|
||||
core.MojoDataPipeConsumer bundleData = (await fetchUrl(relativeUrl)).body;
|
||||
AssetUnpackerProxy unpacker = new AssetUnpackerProxy.unbound();
|
||||
shell.connectToService("mojo:asset_bundle", unpacker);
|
||||
unpacker.ptr.unpackZipStream(bundleData, bundle);
|
||||
unpacker.close();
|
||||
}
|
||||
|
||||
AssetBundleProxy _bundle;
|
||||
|
||||
Future<core.MojoDataPipeConsumer> load(String key) async {
|
||||
return (await _bundle.ptr.getAsStream(key)).assetData;
|
||||
}
|
||||
}
|
||||
|
||||
AssetBundle _initRootBundle() {
|
||||
try {
|
||||
AssetBundleProxy bundle = new AssetBundleProxy.fromHandle(
|
||||
|
200
packages/flutter/lib/src/widgets/asset_vendor.dart
Normal file
200
packages/flutter/lib/src/widgets/asset_vendor.dart
Normal file
@ -0,0 +1,200 @@
|
||||
// Copyright 2016 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:collection';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:mojo/core.dart' as core;
|
||||
|
||||
import 'basic.dart';
|
||||
import 'framework.dart';
|
||||
|
||||
// Base class for asset resolvers.
|
||||
abstract class _AssetResolver {
|
||||
// Return a resolved asset key for the asset named [name].
|
||||
Future<String> resolve(String name);
|
||||
}
|
||||
|
||||
// Asset bundle capable of producing assets via the resolution logic of an
|
||||
// asset resolver.
|
||||
//
|
||||
// Wraps an underlying [AssetBundle] and forwards calls after resolving the
|
||||
// asset key.
|
||||
class _ResolvingAssetBundle extends CachingAssetBundle {
|
||||
|
||||
_ResolvingAssetBundle({ this.bundle, this.resolver });
|
||||
final AssetBundle bundle;
|
||||
final _AssetResolver resolver;
|
||||
|
||||
Map<String, String> _keyCache = <String, String>{};
|
||||
|
||||
Future<core.MojoDataPipeConsumer> load(String key) async {
|
||||
if (!_keyCache.containsKey(key))
|
||||
_keyCache[key] = await resolver.resolve(key);
|
||||
return await bundle.load(_keyCache[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Base class for resolvers that use the asset manifest to retrieve a list
|
||||
// of asset variants to choose from.
|
||||
abstract class _VariantAssetResolver extends _AssetResolver {
|
||||
_VariantAssetResolver({ this.bundle });
|
||||
final AssetBundle bundle;
|
||||
// TODO(kgiesing): Ideally, this cache would be on an object with the same
|
||||
// lifetime as the asset bundle it wraps. However, that won't matter until we
|
||||
// need to change AssetVendors frequently; as of this writing we only have
|
||||
// one.
|
||||
Map<String, List<String>> _assetManifest;
|
||||
Future _initializer;
|
||||
|
||||
Future _loadManifest() async {
|
||||
String json = await bundle.loadString("AssetManifest.json");
|
||||
_assetManifest = JSON.decode(json);
|
||||
}
|
||||
|
||||
Future<String> resolve(String name) async {
|
||||
_initializer ??= _loadManifest();
|
||||
await _initializer;
|
||||
// If there's no asset manifest, just return the main asset always
|
||||
if (_assetManifest == null)
|
||||
return name;
|
||||
// Allow references directly to variants: if the supplied name is not a
|
||||
// key, just return it
|
||||
List<String> variants = _assetManifest[name];
|
||||
if (variants == null)
|
||||
return name;
|
||||
else
|
||||
return chooseVariant(name, variants);
|
||||
}
|
||||
|
||||
String chooseVariant(String main, List<String> variants);
|
||||
}
|
||||
|
||||
// Asset resolver that understands how to determine the best match for the
|
||||
// current device pixel ratio
|
||||
class _ResolutionAwareAssetResolver extends _VariantAssetResolver {
|
||||
_ResolutionAwareAssetResolver({ AssetBundle bundle, this.devicePixelRatio })
|
||||
: super(bundle: bundle);
|
||||
|
||||
final double devicePixelRatio;
|
||||
|
||||
static final RegExp extractRatioRegExp = new RegExp(r"/?(\d+(\.\d*)?)x/");
|
||||
|
||||
SplayTreeMap<double, String> _buildMapping(List<String> candidates) {
|
||||
SplayTreeMap<double, String> result = new SplayTreeMap<double, String>();
|
||||
for (String candidate in candidates) {
|
||||
Match match = extractRatioRegExp.firstMatch(candidate);
|
||||
if (match != null && match.groupCount > 0) {
|
||||
double resolution = double.parse(match.group(1));
|
||||
result[resolution] = candidate;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Return the value for the key in a [SplayTreeMap] nearest the provided key.
|
||||
String _findNearest(SplayTreeMap<double, String> candidates, double value) {
|
||||
if (candidates.containsKey(value))
|
||||
return candidates[value];
|
||||
double lower = candidates.lastKeyBefore(value);
|
||||
double upper = candidates.firstKeyAfter(value);
|
||||
if (lower == null)
|
||||
return candidates[upper];
|
||||
if (upper == null)
|
||||
return candidates[lower];
|
||||
if (value > (lower + upper) / 2)
|
||||
return candidates[upper];
|
||||
else
|
||||
return candidates[lower];
|
||||
}
|
||||
|
||||
String chooseVariant(String main, List<String> candidates) {
|
||||
SplayTreeMap<double, String> mapping = _buildMapping(candidates);
|
||||
// We assume the main asset is designed for a device pixel ratio of 1.0
|
||||
mapping[1.0] = main;
|
||||
return _findNearest(mapping, devicePixelRatio);
|
||||
}
|
||||
}
|
||||
|
||||
/// Establishes an asset resolution strategy for its descendants.
|
||||
///
|
||||
/// Given a main asset and a set of variants, AssetVendor chooses the most
|
||||
/// appropriate asset for the current context. The current asset resolution
|
||||
/// strategy knows how to find the asset most closely matching the current
|
||||
/// device pixel ratio, as given by [MediaQueryData].
|
||||
///
|
||||
/// Main assets are presumed to match a nominal pixel ratio of 1.0. To specify
|
||||
/// assets targeting different pixel ratios, place the variant assets in
|
||||
/// the application bundle under subdirectories named in the form "Nx", where
|
||||
/// N is the nominal device pixel ratio for that asset.
|
||||
///
|
||||
/// For example, suppose an application wants to use an icon named
|
||||
/// "heart.png". This icon has representations at 1.0 (the main icon), as well
|
||||
/// as 1.5 and 2.0 pixel ratios (variants). The asset bundle should then contain
|
||||
/// the following assets:
|
||||
///
|
||||
/// heart.png
|
||||
/// 1.5x/heart.png
|
||||
/// 2.0x/heart.png
|
||||
///
|
||||
/// On a device with a 1.0 device pixel ratio, the image chosen would be
|
||||
/// heart.png; on a device with a 1.3 device pixel ratio, the image chosen
|
||||
/// would be 1.5x/heart.png.
|
||||
///
|
||||
/// The directory level of the asset does not matter as long as the variants are
|
||||
/// at the equivalent level; that is, the following is also a valid bundle
|
||||
/// structure:
|
||||
///
|
||||
/// icons/heart.png
|
||||
/// icons/1.5x/heart.png
|
||||
/// icons/2.0x/heart.png
|
||||
class AssetVendor extends StatefulComponent {
|
||||
AssetVendor({
|
||||
Key key,
|
||||
this.bundle,
|
||||
this.devicePixelRatio,
|
||||
this.child
|
||||
}) : super(key: key);
|
||||
|
||||
final AssetBundle bundle;
|
||||
final double devicePixelRatio;
|
||||
final Widget child;
|
||||
|
||||
_AssetVendorState createState() => new _AssetVendorState();
|
||||
}
|
||||
|
||||
class _AssetVendorState extends State<AssetVendor> {
|
||||
|
||||
_ResolvingAssetBundle _bundle;
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
_bundle = new _ResolvingAssetBundle(
|
||||
bundle: config.bundle,
|
||||
resolver: new _ResolutionAwareAssetResolver(
|
||||
bundle: config.bundle,
|
||||
devicePixelRatio: config.devicePixelRatio
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void didUpdateConfig(AssetVendor oldConfig) {
|
||||
if (config.bundle != oldConfig.bundle ||
|
||||
config.devicePixelRatio != oldConfig.devicePixelRatio) {
|
||||
_bundle = new _ResolvingAssetBundle(
|
||||
bundle: config.bundle,
|
||||
resolver: new _ResolutionAwareAssetResolver(
|
||||
bundle: config.bundle,
|
||||
devicePixelRatio: config.devicePixelRatio
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new DefaultAssetBundle(bundle: _bundle, child: config.child);
|
||||
}
|
||||
}
|
@ -1826,7 +1826,7 @@ class AsyncImage extends StatelessComponent {
|
||||
|
||||
/// Displays an image from an [AssetBundle].
|
||||
///
|
||||
/// By default, asset image will load the image from the cloest enclosing
|
||||
/// By default, asset image will load the image from the closest enclosing
|
||||
/// [DefaultAssetBundle].
|
||||
class AssetImage extends StatelessComponent {
|
||||
// Don't add asserts here unless absolutely necessary, since it will
|
||||
|
@ -5,6 +5,7 @@
|
||||
/// The Flutter widget framework.
|
||||
library widgets;
|
||||
|
||||
export 'src/widgets/asset_vendor.dart';
|
||||
export 'src/widgets/basic.dart';
|
||||
export 'src/widgets/binding.dart';
|
||||
export 'src/widgets/dismissable.dart';
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@ -24,42 +25,77 @@ const String defaultSnapshotPath = 'build/snapshot_blob.bin';
|
||||
const String defaultPrivateKeyPath = 'privatekey.der';
|
||||
|
||||
const String _kSnapshotKey = 'snapshot_blob.bin';
|
||||
const List<String> _kDensities = const ['drawable-xxhdpi'];
|
||||
Map<String, double> _kIconDensities = {
|
||||
'mdpi': 1.0,
|
||||
'hdpi' : 1.5,
|
||||
'xhdpi' : 2.0,
|
||||
'xxhdpi' : 3.0,
|
||||
'xxxhdpi' : 4.0
|
||||
};
|
||||
const List<String> _kThemes = const ['white', 'black'];
|
||||
const List<int> _kSizes = const [18, 24, 36, 48];
|
||||
|
||||
class _Asset {
|
||||
final String source;
|
||||
final String base;
|
||||
final String key;
|
||||
|
||||
_Asset({ this.base, this.key });
|
||||
_Asset({ this.source, this.base, this.key });
|
||||
}
|
||||
|
||||
Iterable<_Asset> _parseAssets(Map manifestDescriptor, String manifestPath) sync* {
|
||||
if (manifestDescriptor == null || !manifestDescriptor.containsKey('assets'))
|
||||
return;
|
||||
Map<_Asset, List<_Asset>> _parseAssets(Map manifestDescriptor, String manifestPath) {
|
||||
Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
||||
if (manifestDescriptor == null)
|
||||
return result;
|
||||
String basePath = path.dirname(path.absolute(manifestPath));
|
||||
for (String asset in manifestDescriptor['assets'])
|
||||
yield new _Asset(base: basePath, key: asset);
|
||||
if (manifestDescriptor.containsKey('assets')) {
|
||||
for (String asset in manifestDescriptor['assets']) {
|
||||
_Asset baseAsset = new _Asset(base: basePath, key: asset);
|
||||
List<_Asset> variants = <_Asset>[];
|
||||
result[baseAsset] = variants;
|
||||
// Find asset variants
|
||||
String assetPath = path.join(basePath, asset);
|
||||
String assetFilename = path.basename(assetPath);
|
||||
Directory assetDir = new Directory(path.dirname(assetPath));
|
||||
List<FileSystemEntity> files = assetDir.listSync(recursive: true);
|
||||
for (FileSystemEntity entity in files) {
|
||||
if (path.basename(entity.path) == assetFilename &&
|
||||
FileSystemEntity.isFileSync(entity.path) &&
|
||||
entity.path != assetPath) {
|
||||
String key = path.relative(entity.path, from: basePath);
|
||||
variants.add(new _Asset(base: basePath, key: key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class _MaterialAsset {
|
||||
class _MaterialAsset extends _Asset {
|
||||
final String name;
|
||||
final String density;
|
||||
final String theme;
|
||||
final int size;
|
||||
|
||||
_MaterialAsset(Map descriptor)
|
||||
: name = descriptor['name'],
|
||||
density = descriptor['density'],
|
||||
theme = descriptor['theme'],
|
||||
size = descriptor['size'];
|
||||
_MaterialAsset(this.name, this.density, this.theme, this.size, String assetBase)
|
||||
: super(base: assetBase);
|
||||
|
||||
String get source {
|
||||
List<String> parts = name.split('/');
|
||||
String category = parts[0];
|
||||
String subtype = parts[1];
|
||||
return '$category/drawable-$density/ic_${subtype}_${theme}_${size}dp.png';
|
||||
}
|
||||
|
||||
String get key {
|
||||
List<String> parts = name.split('/');
|
||||
String category = parts[0];
|
||||
String subtype = parts[1];
|
||||
return '$category/$density/ic_${subtype}_${theme}_${size}dp.png';
|
||||
double devicePixelRatio = _kIconDensities[density];
|
||||
if (devicePixelRatio == 1.0)
|
||||
return '$category/ic_${subtype}_${theme}_${size}dp.png';
|
||||
else
|
||||
return '$category/${devicePixelRatio}x/ic_${subtype}_${theme}_${size}dp.png';
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,28 +105,30 @@ List _generateValues(Map assetDescriptor, String key, List defaults) {
|
||||
return defaults;
|
||||
}
|
||||
|
||||
Iterable<_MaterialAsset> _generateMaterialAssets(Map assetDescriptor) sync* {
|
||||
Map currentAssetDescriptor = new Map.from(assetDescriptor);
|
||||
for (String density in _generateValues(assetDescriptor, 'density', _kDensities)) {
|
||||
currentAssetDescriptor['density'] = density;
|
||||
for (String theme in _generateValues(assetDescriptor, 'theme', _kThemes)) {
|
||||
currentAssetDescriptor['theme'] = theme;
|
||||
for (int size in _generateValues(assetDescriptor, 'size', _kSizes)) {
|
||||
currentAssetDescriptor['size'] = size;
|
||||
yield new _MaterialAsset(currentAssetDescriptor);
|
||||
void _accumulateMaterialAssets(Map<_Asset, List<_Asset>> result, Map assetDescriptor, String assetBase) {
|
||||
String name = assetDescriptor['name'];
|
||||
for (String theme in _generateValues(assetDescriptor, 'theme', _kThemes)) {
|
||||
for (int size in _generateValues(assetDescriptor, 'size', _kSizes)) {
|
||||
_MaterialAsset main = new _MaterialAsset(name, 'mdpi', theme, size, assetBase);
|
||||
List<_Asset> variants = <_Asset>[];
|
||||
result[main] = variants;
|
||||
for (String density in _generateValues(assetDescriptor, 'density', _kIconDensities.keys)) {
|
||||
if (density == 'mdpi')
|
||||
continue;
|
||||
variants.add(new _MaterialAsset(name, density, theme, size, assetBase));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<_MaterialAsset> _parseMaterialAssets(Map manifestDescriptor) sync* {
|
||||
Map<_Asset, List<_Asset>> _parseMaterialAssets(Map manifestDescriptor, String assetBase) {
|
||||
Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
||||
if (manifestDescriptor == null || !manifestDescriptor.containsKey('material-design-icons'))
|
||||
return;
|
||||
return result;
|
||||
for (Map assetDescriptor in manifestDescriptor['material-design-icons']) {
|
||||
for (_MaterialAsset asset in _generateMaterialAssets(assetDescriptor)) {
|
||||
yield asset;
|
||||
}
|
||||
_accumulateMaterialAssets(result, assetDescriptor, assetBase);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
dynamic _loadManifest(String manifestPath) {
|
||||
@ -100,11 +138,30 @@ dynamic _loadManifest(String manifestPath) {
|
||||
return loadYaml(manifestDescriptor);
|
||||
}
|
||||
|
||||
ArchiveFile _createFile(String key, String assetBase) {
|
||||
File file = new File('$assetBase/$key');
|
||||
if (!file.existsSync())
|
||||
return null;
|
||||
bool _addAssetFile(Archive archive, _Asset asset) {
|
||||
String source = asset.source ?? asset.key;
|
||||
File file = new File('${asset.base}/$source');
|
||||
if (!file.existsSync()) {
|
||||
printError('Cannot find asset "$source" in directory "${path.absolute(asset.base)}".');
|
||||
return false;
|
||||
}
|
||||
List<int> content = file.readAsBytesSync();
|
||||
archive.addFile(
|
||||
new ArchiveFile.noCompress(asset.key, content.length, content)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
ArchiveFile _createAssetManifest(Map<_Asset, List<_Asset>> assets) {
|
||||
String key = 'AssetManifest.json';
|
||||
Map<String, List<String>> json = <String, List<String>>{};
|
||||
for (_Asset main in assets.keys) {
|
||||
List<String> variants = <String>[];
|
||||
for (_Asset variant in assets[main])
|
||||
variants.add(variant.key);
|
||||
json[main.key] = variants;
|
||||
}
|
||||
List<int> content = UTF8.encode(JSON.encode(json));
|
||||
return new ArchiveFile.noCompress(key, content.length, content);
|
||||
}
|
||||
|
||||
@ -162,8 +219,8 @@ Future<int> build(
|
||||
|
||||
Map manifestDescriptor = _loadManifest(manifestPath);
|
||||
|
||||
Iterable<_Asset> assets = _parseAssets(manifestDescriptor, manifestPath);
|
||||
Iterable<_MaterialAsset> materialAssets = _parseMaterialAssets(manifestDescriptor);
|
||||
Map<_Asset, List<_Asset>> assets = _parseAssets(manifestDescriptor, manifestPath);
|
||||
assets.addAll(_parseMaterialAssets(manifestDescriptor, assetBase));
|
||||
|
||||
Archive archive = new Archive();
|
||||
|
||||
@ -181,20 +238,16 @@ Future<int> build(
|
||||
archive.addFile(_createSnapshotFile(snapshotPath));
|
||||
}
|
||||
|
||||
for (_Asset asset in assets) {
|
||||
ArchiveFile file = _createFile(asset.key, asset.base);
|
||||
if (file == null) {
|
||||
printError('Cannot find asset "${asset.key}" in directory "${path.absolute(asset.base)}".');
|
||||
for (_Asset asset in assets.keys) {
|
||||
if (!_addAssetFile(archive, asset))
|
||||
return 1;
|
||||
for (_Asset variant in assets[asset]) {
|
||||
if (!_addAssetFile(archive, variant))
|
||||
return 1;
|
||||
}
|
||||
archive.addFile(file);
|
||||
}
|
||||
|
||||
for (_MaterialAsset asset in materialAssets) {
|
||||
ArchiveFile file = _createFile(asset.key, assetBase);
|
||||
if (file != null)
|
||||
archive.addFile(file);
|
||||
}
|
||||
archive.addFile(_createAssetManifest(assets));
|
||||
|
||||
await CipherParameters.get().seedRandom();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user