From e6fe1ed73a75a72d63f07630a4ec1d331772b1fb Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 9 Jun 2021 16:09:04 -0700 Subject: [PATCH] Migrate android application_package to null safety (#84227) --- .../lib/src/android/application_package.dart | 151 +++++++++--------- 1 file changed, 77 insertions(+), 74 deletions(-) diff --git a/packages/flutter_tools/lib/src/android/application_package.dart b/packages/flutter_tools/lib/src/android/application_package.dart index 8aa941cd538..ba0d439c007 100644 --- a/packages/flutter_tools/lib/src/android/application_package.dart +++ b/packages/flutter_tools/lib/src/android/application_package.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 - import 'dart:collection'; import 'package:meta/meta.dart'; @@ -25,10 +23,10 @@ import 'gradle.dart'; /// An application package created from an already built Android APK. class AndroidApk extends ApplicationPackage { AndroidApk({ - String id, - @required this.file, - @required this.versionCode, - @required this.launchActivity, + required String id, + required this.file, + required this.versionCode, + required this.launchActivity, }) : assert(file != null), assert(launchActivity != null), super(id: id); @@ -36,14 +34,15 @@ class AndroidApk extends ApplicationPackage { /// Creates a new AndroidApk from an existing APK. /// /// Returns `null` if the APK was invalid or any required tooling was missing. - factory AndroidApk.fromApk(File apk, { - @required AndroidSdk androidSdk, - @required ProcessManager processManager, - @required UserMessages userMessages, - @required Logger logger, - @required ProcessUtils processUtils, + static AndroidApk? fromApk( + File apk, { + required AndroidSdk androidSdk, + required ProcessManager processManager, + required UserMessages userMessages, + required Logger logger, + required ProcessUtils processUtils, }) { - final String aaptPath = androidSdk?.latestVersion?.aaptPath; + final String? aaptPath = androidSdk.latestVersion?.aaptPath; if (aaptPath == null || !processManager.canRun(aaptPath)) { logger.printError(userMessages.aaptNotFound); return null; @@ -66,22 +65,23 @@ class AndroidApk extends ApplicationPackage { return null; } - final ApkManifestData data = ApkManifestData.parseFromXmlDump(apptStdout, logger); + final ApkManifestData? data = ApkManifestData.parseFromXmlDump(apptStdout, logger); if (data == null) { logger.printError('Unable to read manifest info from ${apk.path}.'); return null; } - if (data.packageName == null || data.launchableActivityName == null) { + final String? packageName = data.packageName; + if (packageName == null || data.launchableActivityName == null) { logger.printError('Unable to read manifest info from ${apk.path}.'); return null; } return AndroidApk( - id: data.packageName, + id: packageName, file: apk, - versionCode: int.tryParse(data.versionCode), + versionCode: data.versionCode == null ? null : int.tryParse(data.versionCode!), launchActivity: '${data.packageName}/${data.launchableActivityName}', ); } @@ -93,16 +93,17 @@ class AndroidApk extends ApplicationPackage { final String launchActivity; /// The version code of the APK. - final int versionCode; + final int? versionCode; /// Creates a new AndroidApk based on the information in the Android manifest. - static Future fromAndroidProject(AndroidProject androidProject, { - @required AndroidSdk androidSdk, - @required ProcessManager processManager, - @required UserMessages userMessages, - @required ProcessUtils processUtils, - @required Logger logger, - @required FileSystem fileSystem, + static Future fromAndroidProject( + AndroidProject androidProject, { + required AndroidSdk androidSdk, + required ProcessManager processManager, + required UserMessages userMessages, + required ProcessUtils processUtils, + required Logger logger, + required FileSystem fileSystem, }) async { File apkFile; @@ -157,32 +158,31 @@ class AndroidApk extends ApplicationPackage { logger.printError('Please check ${manifest.path} for errors.'); return null; } - final String packageId = manifests.first.getAttribute('package'); + final String? packageId = manifests.first.getAttribute('package'); - String launchActivity; + String? launchActivity; for (final XmlElement activity in document.findAllElements('activity')) { - final String enabled = activity.getAttribute('android:enabled'); + final String? enabled = activity.getAttribute('android:enabled'); if (enabled != null && enabled == 'false') { continue; } for (final XmlElement element in activity.findElements('intent-filter')) { - String actionName = ''; - String categoryName = ''; + String? actionName = ''; + String? categoryName = ''; for (final XmlNode node in element.children) { if (node is! XmlElement) { continue; } - final XmlElement xmlElement = node as XmlElement; - final String name = xmlElement.getAttribute('android:name'); + final String? name = node.getAttribute('android:name'); if (name == 'android.intent.action.MAIN') { actionName = name; } else if (name == 'android.intent.category.LAUNCHER') { categoryName = name; } } - if (actionName.isNotEmpty && categoryName.isNotEmpty) { - final String activityName = activity.getAttribute('android:name'); + if (actionName != null && categoryName != null && actionName.isNotEmpty && categoryName.isNotEmpty) { + final String? activityName = activity.getAttribute('android:name'); launchActivity = '$packageId/$activityName'; break; } @@ -213,47 +213,51 @@ class AndroidApk extends ApplicationPackage { abstract class _Entry { const _Entry(this.parent, this.level); - final _Element parent; + final _Element? parent; final int level; } class _Element extends _Entry { - _Element._(this.name, _Element parent, int level) : super(parent, level); + _Element._(this.name, _Element? parent, int level) : super(parent, level); - factory _Element.fromLine(String line, _Element parent) { + factory _Element.fromLine(String line, _Element? parent) { // E: application (line=29) final List parts = line.trimLeft().split(' '); return _Element._(parts[1], parent, line.length - line.trimLeft().length); } final List<_Entry> children = <_Entry>[]; - final String name; + final String? name; void addChild(_Entry child) { children.add(child); } - _Attribute firstAttribute(String name) { - return children.whereType<_Attribute>().firstWhere( - (_Attribute e) => e.key.startsWith(name), - orElse: () => null, - ); + _Attribute? firstAttribute(String name) { + for (final _Attribute child in children.whereType<_Attribute>()) { + if (child.key?.startsWith(name) == true) { + return child; + } + } + return null; } - _Element firstElement(String name) { - return children.whereType<_Element>().firstWhere( - (_Element e) => e.name.startsWith(name), - orElse: () => null, - ); + _Element? firstElement(String name) { + for (final _Element child in children.whereType<_Element>()) { + if (child.name?.startsWith(name) == true) { + return child; + } + } + return null; } Iterable<_Element> allElements(String name) { - return children.whereType<_Element>().where((_Element e) => e.name.startsWith(name)); + return children.whereType<_Element>().where((_Element e) => e.name?.startsWith(name) == true); } } class _Attribute extends _Entry { - const _Attribute._(this.key, this.value, _Element parent, int level) : super(parent, level); + const _Attribute._(this.key, this.value, _Element? parent, int level) : super(parent, level); factory _Attribute.fromLine(String line, _Element parent) { // A: android:label(0x01010001)="hello_world" (Raw: "hello_world") @@ -262,19 +266,19 @@ class _Attribute extends _Entry { return _Attribute._(keyVal[0], keyVal[1], parent, line.length - line.trimLeft().length); } - final String key; - final String value; + final String? key; + final String? value; } class ApkManifestData { ApkManifestData._(this._data); - static bool _isAttributeWithValuePresent(_Element baseElement, - String childElement, String attributeName, String attributeValue) { + static bool _isAttributeWithValuePresent( + _Element baseElement, String childElement, String attributeName, String attributeValue) { final Iterable<_Element> allElements = baseElement.allElements(childElement); for (final _Element oneElement in allElements) { - final String elementAttributeValue = oneElement - ?.firstAttribute(attributeName) + final String? elementAttributeValue = oneElement + .firstAttribute(attributeName) ?.value; if (elementAttributeValue != null && elementAttributeValue.startsWith(attributeValue)) { @@ -284,7 +288,7 @@ class ApkManifestData { return false; } - static ApkManifestData parseFromXmlDump(String data, Logger logger) { + static ApkManifestData? parseFromXmlDump(String data, Logger logger) { if (data == null || data.trim().isEmpty) { return null; } @@ -302,7 +306,7 @@ class ApkManifestData { // Handle level out while (currentElement.parent != null && level <= currentElement.level) { - currentElement = currentElement.parent; + currentElement = currentElement.parent!; } if (level > currentElement.level) { @@ -319,19 +323,19 @@ class ApkManifestData { } } - final _Element application = manifest.firstElement('application'); + final _Element? application = manifest.firstElement('application'); if (application == null) { return null; } final Iterable<_Element> activities = application.allElements('activity'); - _Element launchActivity; + _Element? launchActivity; for (final _Element activity in activities) { - final _Attribute enabled = activity.firstAttribute('android:enabled'); + final _Attribute? enabled = activity.firstAttribute('android:enabled'); final Iterable<_Element> intentFilters = activity.allElements('intent-filter'); final bool isEnabledByDefault = enabled == null; - final bool isExplicitlyEnabled = enabled != null && enabled.value.contains('0xffffffff'); + final bool isExplicitlyEnabled = enabled != null && enabled.value?.contains('0xffffffff') == true; if (!(isEnabledByDefault || isExplicitlyEnabled)) { continue; } @@ -356,31 +360,30 @@ class ApkManifestData { } } - final _Attribute package = manifest.firstAttribute('package'); + final _Attribute? package = manifest.firstAttribute('package'); // "io.flutter.examples.hello_world" (Raw: "io.flutter.examples.hello_world") - final String packageName = package.value.substring(1, package.value.indexOf('" ')); + final String? packageName = package?.value?.substring(1, package.value?.indexOf('" ')); if (launchActivity == null) { logger.printError('Error running $packageName. Default activity not found'); return null; } - final _Attribute nameAttribute = launchActivity.firstAttribute('android:name'); + final _Attribute? nameAttribute = launchActivity.firstAttribute('android:name'); // "io.flutter.examples.hello_world.MainActivity" (Raw: "io.flutter.examples.hello_world.MainActivity") - final String activityName = nameAttribute - .value.substring(1, nameAttribute.value.indexOf('" ')); + final String? activityName = nameAttribute?.value?.substring(1, nameAttribute.value?.indexOf('" ')); // Example format: (type 0x10)0x1 - final _Attribute versionCodeAttr = manifest.firstAttribute('android:versionCode'); + final _Attribute? versionCodeAttr = manifest.firstAttribute('android:versionCode'); if (versionCodeAttr == null) { logger.printError('Error running $packageName. Manifest versionCode not found'); return null; } - if (!versionCodeAttr.value.startsWith('(type 0x10)')) { + if (versionCodeAttr.value?.startsWith('(type 0x10)') != true) { logger.printError('Error running $packageName. Manifest versionCode invalid'); return null; } - final int versionCode = int.tryParse(versionCodeAttr.value.substring(11)); + final int? versionCode = versionCodeAttr.value == null ? null : int.tryParse(versionCodeAttr.value!.substring(11)); if (versionCode == null) { logger.printError('Error running $packageName. Manifest versionCode invalid'); return null; @@ -403,12 +406,12 @@ class ApkManifestData { Map> get data => UnmodifiableMapView>(_data); - String get packageName => _data['package'] == null ? null : _data['package']['name']; + String? get packageName => _data['package'] == null ? null : _data['package']?['name']; - String get versionCode => _data['version-code'] == null ? null : _data['version-code']['name']; + String? get versionCode => _data['version-code'] == null ? null : _data['version-code']?['name']; - String get launchableActivityName { - return _data['launchable-activity'] == null ? null : _data['launchable-activity']['name']; + String? get launchableActivityName { + return _data['launchable-activity'] == null ? null : _data['launchable-activity']?['name']; } @override