Add org option to flutter create (#10290)

diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index c02eaf5..34b3915 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -39,12 +39,18 @@
       'plugin',
       negatable: true,
       defaultsTo: false,
-      help: 'Generate a new Flutter Plugin project.'
+      help: 'Generate a Flutter plugin project.'
     );
     argParser.addOption(
       'description',
-      defaultsTo: 'A new flutter project.',
-      help: 'The description to use for your new flutter project. This string ends up in the pubspec.yaml file.'
+      defaultsTo: 'A new Flutter project.',
+      help: 'The description to use for your new Flutter project. This string ends up in the pubspec.yaml file.'
+    );
+    argParser.addOption(
+      'org',
+      defaultsTo: 'com.yourcompany',
+      help: 'The organization responsible for your new Flutter project, in reverse domain name notation.\n'
+            'This string is used in Java package names and as prefix in the iOS bundle identifier.'
     );
     argParser.addOption(
       'ios-language',
@@ -110,6 +116,7 @@
     // TODO(goderbauer): Work-around for: https://github.com/dart-lang/path/issues/24
     if (fs.path.basename(dirPath) == '.')
       dirPath = fs.path.dirname(dirPath);
+    final String organization = argResults['org'];
     final String projectName = fs.path.basename(dirPath);
 
     String error =_validateProjectDir(dirPath, flutterRoot: flutterRoot);
@@ -121,6 +128,7 @@
       throwToolExit(error);
 
     final Map<String, dynamic> templateContext = _templateContext(
+      organization: organization,
       projectName: projectName,
       projectDescription: argResults['description'],
       dirPath: dirPath,
@@ -151,8 +159,8 @@
       final String androidPluginIdentifier = templateContext['androidIdentifier'];
       final String exampleProjectName = projectName + '_example';
       templateContext['projectName'] = exampleProjectName;
-      templateContext['androidIdentifier'] = _createAndroidIdentifier(exampleProjectName);
-      templateContext['iosIdentifier'] = _createUTIIdentifier(exampleProjectName);
+      templateContext['androidIdentifier'] = _createAndroidIdentifier(organization, exampleProjectName);
+      templateContext['iosIdentifier'] = _createUTIIdentifier(organization, exampleProjectName);
       templateContext['description'] = 'Demonstrates how to use the $projectName plugin.';
       templateContext['pluginProjectName'] = projectName;
       templateContext['androidPluginIdentifier'] = androidPluginIdentifier;
@@ -224,6 +232,7 @@
   }
 
   Map<String, dynamic> _templateContext({
+    String organization,
     String projectName,
     String projectDescription,
     String androidLanguage,
@@ -241,9 +250,10 @@
         : pluginDartClass + 'Plugin';
 
     return <String, dynamic>{
+      'organization': organization,
       'projectName': projectName,
-      'androidIdentifier': _createAndroidIdentifier(projectName),
-      'iosIdentifier': _createUTIIdentifier(projectName),
+      'androidIdentifier': _createAndroidIdentifier(organization, projectName),
+      'iosIdentifier': _createUTIIdentifier(organization, projectName),
       'description': projectDescription,
       'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
       'androidMinApiLevel': android.minApiLevel,
@@ -264,8 +274,8 @@
   }
 }
 
-String _createAndroidIdentifier(String name) {
-  return 'com.yourcompany.$name';
+String _createAndroidIdentifier(String organization, String name) {
+  return '$organization.$name';
 }
 
 String _createPluginClassName(String name) {
@@ -273,12 +283,12 @@
   return camelizedName[0].toUpperCase() + camelizedName.substring(1);
 }
 
-String _createUTIIdentifier(String name) {
+String _createUTIIdentifier(String organization, String name) {
   // Create a UTI (https://en.wikipedia.org/wiki/Uniform_Type_Identifier) from a base name
   final RegExp disallowed = new RegExp(r"[^a-zA-Z0-9\-\.\u0080-\uffff]+");
   name = camelCase(name).replaceAll(disallowed, '');
   name = name.isEmpty ? 'untitled' : name;
-  return 'com.yourcompany.$name';
+  return '$organization.$name';
 }
 
 final Set<String> _packageDependencies = new Set<String>.from(<String>[
diff --git a/packages/flutter_tools/lib/src/template.dart b/packages/flutter_tools/lib/src/template.dart
index ec3d94d..580b3af 100644
--- a/packages/flutter_tools/lib/src/template.dart
+++ b/packages/flutter_tools/lib/src/template.dart
@@ -84,13 +84,20 @@
           return null;
         relativeDestinationPath = relativeDestinationPath.replaceAll('$platform-$language.tmpl', platform);
       }
+      final String organization = context['organization'];
       final String projectName = context['projectName'];
       final String pluginClass = context['pluginClass'];
       final String destinationDirPath = destination.absolute.path;
+      final String pathSeparator = fs.path.separator;
       String finalDestinationPath = fs.path
         .join(destinationDirPath, relativeDestinationPath)
         .replaceAll(_kCopyTemplateExtension, '')
-        .replaceAll(_kTemplateExtension, '');
+        .replaceAll(_kTemplateExtension, '')
+        .replaceAll(
+          '${pathSeparator}organization$pathSeparator',
+          '$pathSeparator${organization.replaceAll('.', pathSeparator)}$pathSeparator',
+        );
+      
       if (projectName != null)
         finalDestinationPath = finalDestinationPath.replaceAll('projectName', projectName);
       if (pluginClass != null)
diff --git a/packages/flutter_tools/templates/create/android-java.tmpl/app/src/main/java/com/yourcompany/projectName/MainActivity.java.tmpl b/packages/flutter_tools/templates/create/android-java.tmpl/app/src/main/java/organization/projectName/MainActivity.java.tmpl
similarity index 100%
rename from packages/flutter_tools/templates/create/android-java.tmpl/app/src/main/java/com/yourcompany/projectName/MainActivity.java.tmpl
rename to packages/flutter_tools/templates/create/android-java.tmpl/app/src/main/java/organization/projectName/MainActivity.java.tmpl
diff --git a/packages/flutter_tools/templates/create/android-kotlin.tmpl/app/src/main/kotlin/com/yourcompany/projectName/MainActivity.kt.tmpl b/packages/flutter_tools/templates/create/android-kotlin.tmpl/app/src/main/kotlin/organization/projectName/MainActivity.kt.tmpl
similarity index 100%
rename from packages/flutter_tools/templates/create/android-kotlin.tmpl/app/src/main/kotlin/com/yourcompany/projectName/MainActivity.kt.tmpl
rename to packages/flutter_tools/templates/create/android-kotlin.tmpl/app/src/main/kotlin/organization/projectName/MainActivity.kt.tmpl
diff --git a/packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/com/yourcompany/projectName/pluginClass.java.tmpl b/packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/organization/projectName/pluginClass.java.tmpl
similarity index 100%
rename from packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/com/yourcompany/projectName/pluginClass.java.tmpl
rename to packages/flutter_tools/templates/plugin/android-java.tmpl/src/main/java/organization/projectName/pluginClass.java.tmpl
diff --git a/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/com/yourcompany/projectName/pluginClass.kt.tmpl b/packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/organization/projectName/pluginClass.kt.tmpl
similarity index 100%
rename from packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/com/yourcompany/projectName/pluginClass.kt.tmpl
rename to packages/flutter_tools/templates/plugin/android-kotlin.tmpl/src/main/kotlin/organization/projectName/pluginClass.kt.tmpl
diff --git a/packages/flutter_tools/test/commands/create_test.dart b/packages/flutter_tools/test/commands/create_test.dart
index db6a522..744a2b6 100644
--- a/packages/flutter_tools/test/commands/create_test.dart
+++ b/packages/flutter_tools/test/commands/create_test.dart
@@ -52,7 +52,7 @@
     testUsingContext('kotlin/swift project', () async {
       return _createAndAnalyzeProject(
         projectDir,
-        <String>['--android-language', 'kotlin', '--ios-language', 'swift'],
+        <String>['--android-language', 'kotlin', '-i', 'swift'],
         <String>[
           'android/app/src/main/kotlin/com/yourcompany/flutter_project/MainActivity.kt',
           'ios/Runner/AppDelegate.swift',
@@ -89,7 +89,7 @@
     testUsingContext('kotlin/swift plugin project', () async {
       return _createAndAnalyzeProject(
         projectDir,
-        <String>['--plugin', '--android-language', 'kotlin', '--ios-language', 'swift'],
+        <String>['--plugin', '-a', 'kotlin', '--ios-language', 'swift'],
         <String>[
           'android/src/main/kotlin/com/yourcompany/flutter_project/FlutterProjectPlugin.kt',
           'ios/Classes/FlutterProjectPlugin.h',
@@ -111,6 +111,21 @@
       );
     });
 
+    testUsingContext('plugin project with custom org', () async {
+      return _createAndAnalyzeProject(
+          projectDir,
+          <String>['--plugin', '--org', 'com.bar.foo'],
+          <String>[
+            'android/src/main/java/com/bar/foo/flutter_project/FlutterProjectPlugin.java',
+            'example/android/app/src/main/java/com/bar/foo/flutter_project_example/MainActivity.java',
+          ],
+          <String>[
+            'android/src/main/java/com/yourcompany/flutter_project/FlutterProjectPlugin.java',
+            'example/android/app/src/main/java/com/yourcompany/flutter_project_example/MainActivity.java',
+          ],
+      );
+    });
+
     testUsingContext('project with-driver-test', () async {
       return _createAndAnalyzeProject(
         projectDir,
@@ -126,7 +141,7 @@
       final CreateCommand command = new CreateCommand();
       final CommandRunner<Null> runner = createTestCommandRunner(command);
 
-      await runner.run(<String>['create', '--no-pub', projectDir.path]);
+      await runner.run(<String>['create', '--no-pub', '--org', 'com.foo.bar', projectDir.path]);
 
       void expectExists(String relPath) {
         expect(fs.isFileSync('${projectDir.path}/$relPath'), true);
@@ -156,6 +171,12 @@
       expect(xcodeConfig, contains('FLUTTER_ROOT='));
       expect(xcodeConfig, contains('FLUTTER_APPLICATION_PATH='));
       expect(xcodeConfig, contains('FLUTTER_FRAMEWORK_DIR='));
+      // App identification
+      final String xcodeProjectPath = fs.path.join('ios', 'Runner.xcodeproj', 'project.pbxproj');
+      expectExists(xcodeProjectPath);
+      final File xcodeProjectFile = fs.file(fs.path.join(projectDir.path, xcodeProjectPath));
+      final String xcodeProject = xcodeProjectFile.readAsStringSync();
+      expect(xcodeProject, contains('PRODUCT_BUNDLE_IDENTIFIER = com.foo.bar.flutterProject'));
     });
 
     // Verify that we can regenerate over an existing project.