// Copyright 2014 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. import 'package:xml/xml.dart'; import '../base/file_system.dart'; // These utility methods are used to generate the code for multidex support as // well as verifying the project is properly set up. File _getMultiDexApplicationFile(Directory projectDir) { return projectDir.childDirectory('android') .childDirectory('app') .childDirectory('src') .childDirectory('main') .childDirectory('java') .childDirectory('io') .childDirectory('flutter') .childDirectory('app') .childFile('FlutterMultiDexApplication.java'); } /// Creates the FlutterMultiDexApplication.java if it does not exist. void ensureMultiDexApplicationExists(final Directory projectDir) { final File applicationFile = _getMultiDexApplicationFile(projectDir); if (applicationFile.existsSync()) { // This checks for instances of legacy versions of this file. Legacy versions maintained // compatibility with v1 embedding by extending FlutterApplication. If we detect this, // we replace the file with the modern v2 embedding version. if (applicationFile.readAsStringSync().contains('android.app.Application;')) { return; } } applicationFile.createSync(recursive: true); final StringBuffer buffer = StringBuffer(); buffer.write(''' // Generated file. // // If you wish to remove Flutter's multidex support, delete this entire file. // // Modifications to this file should be done in a copy under a different name // as this file may be regenerated. package io.flutter.app; import android.app.Application; import android.content.Context; import androidx.annotation.CallSuper; import androidx.multidex.MultiDex; /** * Extension of {@link android.app.Application}, adding multidex support. */ public class FlutterMultiDexApplication extends Application { @Override @CallSuper protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } } '''); applicationFile.writeAsStringSync(buffer.toString(), flush: true); } /// Returns true if FlutterMultiDexApplication.java exists. /// /// This function does not verify the contents of the file. bool multiDexApplicationExists(final Directory projectDir) { if (_getMultiDexApplicationFile(projectDir).existsSync()) { return true; } return false; } File _getManifestFile(Directory projectDir) { return projectDir.childDirectory('android') .childDirectory('app') .childDirectory('src') .childDirectory('main') .childFile('AndroidManifest.xml'); } /// Returns true if the `app` module AndroidManifest.xml includes the /// <application android:name="${applicationName}"> attribute. bool androidManifestHasNameVariable(final Directory projectDir) { final File manifestFile = _getManifestFile(projectDir); if (!manifestFile.existsSync()) { return false; } XmlDocument document; try { document = XmlDocument.parse(manifestFile.readAsStringSync()); } on XmlException { return false; } on FileSystemException { return false; } // Check for the ${androidName} application attribute. for (final XmlElement application in document.findAllElements('application')) { final String? applicationName = application.getAttribute('android:name'); if (applicationName == r'${applicationName}') { return true; } } return false; }