Commit 21057de9 authored by Chinmay Garde's avatar Chinmay Garde

Merge pull request #2118 from chinmaygarde/develop

Remove all inline templates and provide a way to specify templates as resources
parents 1acdfb79 038367ad
...@@ -12,13 +12,6 @@ import '../base/process.dart'; ...@@ -12,13 +12,6 @@ import '../base/process.dart';
import '../globals.dart'; import '../globals.dart';
import '../runner/flutter_command_runner.dart'; import '../runner/flutter_command_runner.dart';
/// A map from file path to file contents.
final Map<String, String> iosTemplateFiles = <String, String>{
'ios/Info.plist': _infoPlistInitialContents,
'ios/LaunchScreen.storyboard': _launchScreenInitialContents,
'ios/Assets.xcassets/AppIcon.appiconset/Contents.json': _iconAssetInitialContents
};
Uri _xcodeProjectUri(String revision) { Uri _xcodeProjectUri(String revision) {
String uriString = 'https://storage.googleapis.com/flutter_infra/flutter/$revision/ios/FlutterXcode.zip'; String uriString = 'https://storage.googleapis.com/flutter_infra/flutter/$revision/ios/FlutterXcode.zip';
return Uri.parse(uriString); return Uri.parse(uriString);
...@@ -162,159 +155,3 @@ Future<int> setupXcodeProjectHarness(String flutterProjectPath) async { ...@@ -162,159 +155,3 @@ Future<int> setupXcodeProjectHarness(String flutterProjectPath) async {
return 0; return 0;
} }
final String _infoPlistInitialContents = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>Runner</string>
<key>CFBundleIdentifier</key>
<string>{{projectIdentifier}}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>{{projectName}}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
''';
final String _launchScreenInitialContents = '''
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
''';
final String _iconAssetInitialContents = '''
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "83.5x83.5",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
''';
// 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:io';
import 'package:path/path.dart' as path;
import 'package:mustache4dart/mustache4dart.dart' as mustache;
import 'artifacts.dart';
import 'globals.dart';
const String _kTemplateExtension = '.tmpl';
const String _kCopyTemplateExtension = '.copy.tmpl';
/// Expands templates in a directory to a destination. All files that must
/// undergo template expansion should end with the '.tmpl' extension. All other
/// files are ignored. In case the contents of entire directories must be copied
/// as is, the directory itself can end with '.tmpl' extension. Files within
/// such a directory may also contain the '.tmpl' extension and will be
/// considered for expansion. In case certain files need to be copied but
/// without template expansion (images, data files, etc.), the '.copy.tmpl'
/// extension may be used.
///
/// Files in the destination will not contain either the '.tmpl' or '.copy.tmpl'
/// extensions.
class Template {
factory Template.fromName(String name) {
// All named templates are placed in the 'templates' directory
Directory templateDir = _templateDirectoryInPackage(name);
return new Template(templateDir, templateDir);
}
Template(Directory templateSource, Directory baseDir) {
_templateFilePaths = new Map<String, String>();
if (!templateSource.existsSync()) {
return;
}
List<FileSystemEntity> templateFiles =
templateSource.listSync(recursive: true);
for (FileSystemEntity entity in templateFiles) {
if (entity is! File) {
// We are only interesting in template *file* URIs.
continue;
}
String relativePath = path.relative(entity.path,
from: baseDir.absolute.path);
if (relativePath.contains(_kTemplateExtension)) {
// If '.tmpl' appears anywhere within the path of this entity, it is
// is a candidate for rendering. This catches cases where the folder
// itself is a template.
_templateFilePaths[relativePath] = path.absolute(entity.path);
}
}
}
Map<String /* relative */, String /* absolute source */> _templateFilePaths;
void render(Directory destination, Map<String, dynamic> context,
{ bool overwriteExisting: true }) {
destination.createSync(recursive: true);
String destinationDirPath = destination.absolute.path;
_templateFilePaths.forEach((String relativeDestPath,
String absoluteSrcPath) {
String finalDestinationPath = path
.join(destinationDirPath, relativeDestPath)
.replaceAll(_kCopyTemplateExtension, '')
.replaceAll(_kTemplateExtension, '');
File finalDestinationFile = new File(finalDestinationPath);
String relativePathForLogging = relativeDestPath
.replaceAll(_kCopyTemplateExtension, '')
.replaceAll(_kTemplateExtension, '');
// Step 1: Check if the file needs to be overwritten.
if (finalDestinationFile.existsSync()) {
if (overwriteExisting) {
finalDestinationFile.delete(recursive: true);
printStatus('$relativePathForLogging exists. Overwriting.');
} else {
// The file exists but we cannot overwrite it, move on.
printStatus('$relativePathForLogging exists. Skipping.');
return;
}
} else {
printStatus('$relativePathForLogging created.');
}
finalDestinationFile.createSync(recursive: true);
File sourceFile = new File(absoluteSrcPath);
// Step 2: If the absolute paths ends with a 'copy.tmpl', this file does
// not need mustache rendering but needs to be directly copied.
if (sourceFile.path.endsWith(_kCopyTemplateExtension)) {
finalDestinationFile.writeAsBytesSync(sourceFile.readAsBytesSync());
return;
}
// Step 3: If the absolute path ends with a '.tmpl', this file needs
// rendering via mustache.
if (sourceFile.path.endsWith(_kTemplateExtension)) {
String templateContents = sourceFile.readAsStringSync();
String renderedContents = mustache.render(templateContents, context);
finalDestinationFile.writeAsStringSync(renderedContents);
return;
}
// Step 4: This file does not end in .tmpl but is in a directory that
// does. Directly copy the file to the destination.
finalDestinationFile.writeAsBytesSync(sourceFile.readAsBytesSync());
});
}
}
Directory _templateDirectoryInPackage(String name) {
String templatesDir = path.join(ArtifactStore.flutterRoot,
'packages', 'flutter_tools', 'templates');
return new Directory(path.join(templatesDir, name));
}
.DS_Store
.atom/
.idea
.packages
.pub/
build/
ios/.generated/
packages
pubspec.lock
# {{projectName}}
{{description}}
## Getting Started
For help getting started with Flutter, view our online
[documentation](http://flutter.io/).
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="{{projectIdentifier}}"
android:versionCode="1"
android:versionName="0.0.1">
<uses-sdk android:minSdkVersion="${android.minApiLevel}" android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET"/>
<application android:name="org.domokit.sky.shell.SkyApplication" android:label="{{projectName}}">
<activity android:name="org.domokit.sky.shell.SkyActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Place Android resources here (http://developer.android.com/guide/topics/resources/overview.html).
name: {{projectName}}
material-design-icons:
- name: content/add
- name: navigation/arrow_back
- name: navigation/menu
- name: navigation/more_vert
{
"images" : [
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-Small-40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-Small-40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-Small-40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-Small-40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-83.5@2x.png",
"scale" : "2x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256@2x.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512@2x.png",
"scale" : "2x"
}
]
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>Runner</string>
<key>CFBundleIdentifier</key>
<string>{{projectIdentifier}}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>{{projectName}}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arm64</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
import 'package:flutter/material.dart';
void main() {
runApp(
new MaterialApp(
title: 'Flutter Demo',
routes: <String, RouteBuilder>{
'/': (RouteArguments args) => new FlutterDemo()
}
)
);
}
class FlutterDemo extends StatefulComponent {
State createState() => new _FlutterDemoState();
}
class _FlutterDemoState extends State<FlutterDemo> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
Widget build(BuildContext context) {
return new Scaffold(
toolBar: new ToolBar(
center: new Text('Flutter Demo')
),
body: new Center(
child: new Text(
'Button tapped $_counter times.',
key: const ValueKey('counter')
)
),
floatingActionButton: new FloatingActionButton(
key: const ValueKey('fab'),
child: new Icon(
icon: 'content/add'
),
onPressed: _incrementCounter
)
);
}
}
name: {{projectName}}
description: {{description}}
dependencies:
flutter:
path: {{flutterPackagesDirectory}}/flutter
{{#withDriverTest?}}
dev_dependencies:
flutter_driver:
path: {{flutterPackagesDirectory}}/flutter_driver
{{/withDriverTest?}}
// Starts the app with Flutter Driver extension enabled to allow Flutter Driver
// to test the app.
import 'package:{{projectName}}/main.dart' as app;
import 'package:flutter_driver/driver_extension.dart';
main() {
enableFlutterDriverExtension();
app.main();
}
// This is a basic Flutter Driver test for the application. A Flutter Driver
// test is an end-to-end test that "drives" your application from another
// process or even from another computer. If you are familiar with
// Selenium/WebDriver for web, Espresso for Android or UI Automation for iOS,
// this is simply Flutter's version of that.
//
// To start the test run the following command from the root of your application
// package:
//
// flutter drive --target=test_driver/e2e.dart
//
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
main() {
group('end-to-end test', () {
FlutterDriver driver;
setUpAll(() async {
// Connect to a running Flutter application instance.
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
if (driver != null) driver.close();
});
test('find the floating action button by value key', () async {
ObjectRef elem = await driver.findByValueKey('fab');
expect(elem, isNotNull);
expect(elem.objectReferenceKey, isNotNull);
});
test('tap on the floating action button; verify counter', () async {
ObjectRef fab = await driver.findByValueKey('fab');
expect(fab, isNotNull);
await driver.tap(fab);
ObjectRef counter = await driver.findByValueKey('counter');
expect(counter, isNotNull);
String text = await driver.getText(counter);
expect(text, contains("Button tapped 1 times."));
});
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment