Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added support for android 13+ themed icons #497

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ Shown below is the full list of attributes which you can specify within your Flu
be used to fill out the background of the adaptive icon.
- `adaptive_icon_foreground`: The image asset which will be used for the icon foreground of the adaptive icon
*Note: Adaptive Icons will only be generated when both adaptive_icon_background and adaptive_icon_foreground are specified. (the image_path is not automatically taken as foreground)*
- `adaptive_icon_monochrome`: The image asset which will be used for the icon
foreground of the Android 13+ themed icon. For more information see [Android Adaptive Icons](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#user-theming)

### IOS

Expand Down
7 changes: 1 addition & 6 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ analyzer:
missing_return: warning
# allow having TODOs in the code
todo: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
sdk_version_async_exported_from_core: ignore
exclude:
- "bin/cache/**"
# the following two are relative to the stocks example and the flutter package respectively
Expand Down Expand Up @@ -55,10 +51,9 @@ linter:
- flutter_style_todos
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- collection_methods_unrelated_type
- library_names
- library_prefixes
- list_remove_unrelated_type
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names
Expand Down
1 change: 1 addition & 0 deletions bin/generate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ flutter_launcher_icons:
min_sdk_android: 21 # android min sdk min:16, default 21
# adaptive_icon_background: "assets/icon/background.png"
# adaptive_icon_foreground: "assets/icon/foreground.png"
# adaptive_icon_monochrome: "assets/icon/monochrome.png"

ios: true
# image_path_ios: "assets/icon/icon.png"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions example/default_example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ flutter_launcher_icons:
ios: true # can specify file name here e.g. "My-Launcher-Icon"
adaptive_icon_background: "assets/images/christmas-background.png" # only available for Android 8.0 devices and above
adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" # only available for Android 8.0 devices and above
adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" # only available for Android 13 devices and above
min_sdk_android: 21 # android min sdk min:16, default 21
remove_alpha_ios: true
background_color_ios: "#ffffff"
Expand Down
115 changes: 73 additions & 42 deletions lib/android.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,83 @@ void createAdaptiveIcons(
flavor,
);
} else {
createAdaptiveIconMipmapXmlFile(config, flavor);
updateColorsXmlFile(backgroundConfig, flavor);
}
}

void createAdaptiveMonochromeIcons(
Config config,
String? flavor,
) {
utils.printStatus('Creating adaptive monochrome icons Android');

// Retrieve the necessary Flutter Launcher Icons configuration from the pubspec.yaml file
final String? monochromeImagePath = config.adaptiveIconMonochrome;
if (monochromeImagePath == null) {
throw const InvalidConfigException(errorMissingImagePath);
}
final Image? monochromeImage = utils.decodeImageFile(monochromeImagePath);
if (monochromeImage == null) {
return;
}

// Create adaptive icon monochrome images
for (AndroidIconTemplate androidIcon in adaptiveForegroundIcons) {
overwriteExistingIcons(
androidIcon,
monochromeImage,
constants.androidAdaptiveMonochromeFileName,
flavor,
);
}
}

void createMipmapXmlFile(
Config config,
String? flavor,
) {
utils.printStatus('Creating mipmap xml file Android');

String xmlContent = '';

if (config.hasAndroidAdaptiveConfig) {
if (isAdaptiveIconConfigPngFile(config.adaptiveIconBackground!)) {
xmlContent +=
' <background android:drawable="@drawable/ic_launcher_background"/>\n';
} else {
xmlContent +=
' <background android:drawable="@color/ic_launcher_background"/>\n';
}

xmlContent +=
' <foreground android:drawable="@drawable/ic_launcher_foreground"/>\n';
}

if (config.hasAndroidAdaptiveMonochromeConfig) {
xmlContent +=
' <monochrome android:drawable="@drawable/ic_launcher_monochrome"/>\n';
}

late File mipmapXmlFile;
if (config.isCustomAndroidFile) {
mipmapXmlFile = File(
constants.androidAdaptiveXmlFolder(flavor) + config.android + '.xml',
);
} else {
mipmapXmlFile = File(
constants.androidAdaptiveXmlFolder(flavor) +
constants.androidDefaultIconName +
'.xml',
);
}

mipmapXmlFile.create(recursive: true).then((File adaptiveIconFile) {
adaptiveIconFile.writeAsString(
xml_template.mipmapXmlFile.replaceAll('{{CONTENT}}', xmlContent),
);
});
}

/// Retrieves the colors.xml file for the project.
///
/// If the colors.xml file is found, it is updated with a new color item for the
Expand All @@ -151,29 +223,6 @@ void updateColorsXmlFile(String backgroundConfig, String? flavor) {
}
}

/// Creates the xml file required for the adaptive launcher icon
/// FILE LOCATED HERE: res/mipmap-anydpi/{icon-name-from-yaml-config}.xml
void createAdaptiveIconMipmapXmlFile(
Config config,
String? flavor,
) {
if (config.isCustomAndroidFile) {
File(
constants.androidAdaptiveXmlFolder(flavor) + config.android + '.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherXml);
});
} else {
File(
constants.androidAdaptiveXmlFolder(flavor) +
constants.androidDefaultIconName +
'.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherXml);
});
}
}

/// creates adaptive background using png image
void _createAdaptiveBackgrounds(
Config config,
Expand All @@ -196,24 +245,6 @@ void _createAdaptiveBackgrounds(
flavor,
);
}

// Creates the xml file required for the adaptive launcher icon
// FILE LOCATED HERE: res/mipmap-anydpi/{icon-name-from-yaml-config}.xml
if (config.isCustomAndroidFile) {
File(
constants.androidAdaptiveXmlFolder(flavor) + config.android + '.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherDrawableBackgroundXml);
});
} else {
File(
constants.androidAdaptiveXmlFolder(flavor) +
constants.androidDefaultIconName +
'.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherDrawableBackgroundXml);
});
}
}

/// Creates a colors.xml file if it was missing from android/app/src/main/res/values/colors.xml
Expand Down
12 changes: 11 additions & 1 deletion lib/config/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Config {
this.imagePathIOS,
this.adaptiveIconForeground,
this.adaptiveIconBackground,
this.adaptiveIconMonochrome,
this.minSdkAndroid = constants.androidDefaultAndroidMinSDK,
this.removeAlphaIOS = false,
this.backgroundColorIOS = '#ffffff',
Expand Down Expand Up @@ -116,14 +117,18 @@ class Config {
@JsonKey(name: 'image_path_ios')
final String? imagePathIOS;

/// android adaptive icon foreground image
/// android adaptive_icon_foreground image
@JsonKey(name: 'adaptive_icon_foreground')
final String? adaptiveIconForeground;

/// android adaptive_icon_background image
@JsonKey(name: 'adaptive_icon_background')
final String? adaptiveIconBackground;

/// android adaptive_icon_background image
@JsonKey(name: 'adaptive_icon_monochrome')
final String? adaptiveIconMonochrome;

/// Android min_sdk_android
@JsonKey(name: 'min_sdk_android')
final int minSdkAndroid;
Expand Down Expand Up @@ -157,6 +162,11 @@ class Config {
adaptiveIconForeground != null &&
adaptiveIconBackground != null;

/// whether or not there is configuration for monochrome icons for android
bool get hasAndroidAdaptiveMonochromeConfig {
return isNeedingNewAndroidIcon && adaptiveIconMonochrome != null;
}

/// Checks if contains any platform config
bool get hasPlatformConfig {
return ios != false ||
Expand Down
4 changes: 4 additions & 0 deletions lib/config/config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const int androidDefaultAndroidMinSDK = 21;
const String androidFileName = 'ic_launcher.png';
const String androidAdaptiveForegroundFileName = 'ic_launcher_foreground.png';
const String androidAdaptiveBackgroundFileName = 'ic_launcher_background.png';
const String androidAdaptiveMonochromeFileName = 'ic_launcher_monochrome.png';
String androidAdaptiveXmlFolder(String? flavor) =>
androidResFolder(flavor) + 'mipmap-anydpi-v26/';
const String androidDefaultIconName = 'ic_launcher';
Expand Down
6 changes: 3 additions & 3 deletions lib/ios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void modifyContentsFile(String newIconName) {
String generateContentsFileAsString(String newIconName) {
final Map<String, dynamic> contentJson = <String, dynamic>{
'images': createImageList(newIconName),
'info': ContentsInfoObject(version: 1, author: 'xcode').toJson()
'info': ContentsInfoObject(version: 1, author: 'xcode').toJson(),
};
return json.encode(contentJson);
}
Expand All @@ -220,7 +220,7 @@ class ContentsImageObject {
'size': size,
'idiom': idiom,
'filename': filename,
'scale': scale
'scale': scale,
};
}
}
Expand Down Expand Up @@ -390,7 +390,7 @@ List<Map<String, String>> createImageList(String fileNamePrefix) {
idiom: 'ios-marketing',
filename: '[email protected]',
scale: '1x',
).toJson()
).toJson(),
];
return imageList;
}
Expand Down
12 changes: 12 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ Future<void> createIconsFromConfig(
if (flutterConfigs.hasAndroidAdaptiveConfig) {
android_launcher_icons.createAdaptiveIcons(flutterConfigs, flavor);
}
if (flutterConfigs.hasAndroidAdaptiveMonochromeConfig) {
android_launcher_icons.createAdaptiveMonochromeIcons(
flutterConfigs,
flavor,
);
}
if (flutterConfigs.isNeedingNewAndroidIcon) {
android_launcher_icons.createMipmapXmlFile(
flutterConfigs,
flavor,
);
}
if (flutterConfigs.isNeedingNewIOSIcon) {
ios_launcher_icons.createIcons(flutterConfigs, flavor);
}
Expand Down
14 changes: 2 additions & 12 deletions lib/xml_templates.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
// ignore_for_file: public_member_api_docs

const String icLauncherXml = '''
const String mipmapXmlFile = '''
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
''';

const String icLauncherDrawableBackgroundXml = '''
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>
{{CONTENT}}</adaptive-icon>
''';

const String colorsXml = '''
Expand Down
7 changes: 6 additions & 1 deletion test/abs/icon_generator_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.3.2 from annotations
// Mocks generated by Mockito 5.4.2 from annotations
// in flutter_launcher_icons/test/abs/icon_generator_test.dart.
// Do not manually edit this file.

Expand Down Expand Up @@ -58,6 +58,11 @@ class MockConfig extends _i1.Mock implements _i3.Config {
returnValue: false,
) as bool);
@override
bool get hasAndroidAdaptiveMonochromeConfig => (super.noSuchMethod(
Invocation.getter(#hasAndroidAdaptiveMonochromeConfig),
returnValue: false,
) as bool);
@override
bool get hasPlatformConfig => (super.noSuchMethod(
Invocation.getter(#hasPlatformConfig),
returnValue: false,
Expand Down
6 changes: 3 additions & 3 deletions test/android_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ void main() {
final Map<String, dynamic> flutterIconsConfig = <String, dynamic>{
'image_path': 'assets/images/icon-710x599.png',
'android': true,
'ios': true
'ios': true,
};
expect(
Config.fromJson(flutterIconsConfig).isCustomAndroidFile,
Expand All @@ -42,7 +42,7 @@ void main() {
final Map<String, dynamic> flutterIconsNewIconConfig = <String, dynamic>{
'image_path': 'assets/images/icon-710x599.png',
'android': 'New Icon',
'ios': true
'ios': true,
};
expect(
Config.fromJson(flutterIconsNewIconConfig).isCustomAndroidFile,
Expand All @@ -55,7 +55,7 @@ void main() {
'image_path': 'assets/images/icon-710x599.png',
'image_path_android': 'assets/images/icon-android.png',
'android': 'New Icon',
'ios': true
'ios': true,
};
expect(
Config.fromJson(flutterIconsNewIconConfig).getImagePathAndroid(),
Expand Down
8 changes: 7 additions & 1 deletion test/macos/macos_icon_generator_test.mocks.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Mocks generated by Mockito 5.3.2 from annotations
// Mocks generated by Mockito 5.4.2 from annotations
// in flutter_launcher_icons/test/macos/macos_icon_generator_test.dart.
// Do not manually edit this file.

Expand Down Expand Up @@ -69,6 +69,12 @@ class MockConfig extends _i1.Mock implements _i3.Config {
returnValueForMissingStub: false,
) as bool);
@override
bool get hasAndroidAdaptiveMonochromeConfig => (super.noSuchMethod(
Invocation.getter(#hasAndroidAdaptiveMonochromeConfig),
returnValue: false,
returnValueForMissingStub: false,
) as bool);
@override
bool get hasPlatformConfig => (super.noSuchMethod(
Invocation.getter(#hasPlatformConfig),
returnValue: false,
Expand Down
Loading
Loading