Repository: zhanghai/AppIconLoader
Branch: master
Commit: f7e2b2cbb7c2
Files: 96
Total size: 136.1 KB
Directory structure:
gitextract_gr1qz0j7/
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── android.yml
├── .gitignore
├── .gitmodules
├── LICENSE
├── PRIVACY.md
├── README.md
├── appiconloader/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── me/
│ └── zhanghai/
│ └── android/
│ └── appiconloader/
│ ├── AppIconLoader.java
│ ├── PackageInfoCompat.java
│ ├── PackageItemInfoCompat.java
│ ├── UserHandleCompat.java
│ └── UserSerialNumberCache.java
├── appiconloader-coil/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── me/
│ └── zhanghai/
│ └── android/
│ └── appiconloader/
│ └── coil/
│ ├── AppIconFetcher.java
│ └── AppIconKeyer.java
├── appiconloader-glide/
│ ├── .gitignore
│ ├── build.gradle
│ ├── gradle.properties
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── me/
│ └── zhanghai/
│ └── android/
│ └── appiconloader/
│ └── glide/
│ └── AppIconModelLoader.java
├── appiconloader-iconloaderlib/
│ ├── .gitignore
│ ├── build.gradle
│ ├── generate-iconloaderlib-src.gradle
│ ├── generate-iconloaderlib-src.sh
│ ├── gradle.properties
│ ├── iconloaderlib-classes.patch
│ ├── iconloaderlib-resources.patch
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ └── AndroidManifest.xml
├── build.gradle
├── docs/
│ └── contributing.md
├── fastlane/
│ └── metadata/
│ └── android/
│ └── en-US/
│ ├── changelogs/
│ │ ├── 1.txt
│ │ ├── 2.txt
│ │ ├── 3.txt
│ │ ├── 4.txt
│ │ ├── 5.txt
│ │ ├── 6.txt
│ │ └── 7.txt
│ ├── full_description.txt
│ ├── short_description.txt
│ └── title.txt
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── sample/
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── me/
│ │ └── zhanghai/
│ │ └── android/
│ │ └── appiconloader/
│ │ └── sample/
│ │ ├── AppGlideModule.java
│ │ ├── AppListAdapter.java
│ │ ├── AppListFragment.java
│ │ ├── AppListLiveData.java
│ │ ├── AppListLoader.java
│ │ ├── AppListViewModel.java
│ │ ├── CoilAppListFragment.java
│ │ ├── CoilInitializer.java
│ │ ├── CoordinatorScrollingFrameLayout.java
│ │ ├── FastScrollerLiftOnScrollHack.java
│ │ ├── GlideAppListFragment.java
│ │ ├── LauncherAppsCompat.java
│ │ ├── MainActivity.java
│ │ ├── MainFragment.java
│ │ ├── NewDispatchApplyWindowInsetsFrameLayout.java
│ │ ├── PackageManagerCompat.java
│ │ ├── ParcelableCloner.java
│ │ ├── SynchronousAppListFragment.java
│ │ └── UserHandleCompat.java
│ └── res/
│ ├── drawable/
│ │ ├── info_icon_control_normal_24dp.xml
│ │ └── menu_icon_control_normal_24dp.xml
│ ├── layout/
│ │ ├── app_item.xml
│ │ ├── app_list_fragment.xml
│ │ └── main_fragment.xml
│ ├── menu/
│ │ ├── main.xml
│ │ └── navigation.xml
│ ├── values/
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ ├── values-night/
│ │ └── colors.xml
│ ├── values-v23/
│ │ └── themes.xml
│ ├── values-v27/
│ │ └── themes.xml
│ └── values-v29/
│ └── themes.xml
└── settings.gradle
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
## Expected Behavior
## Actual Behavior
## Steps to Reproduce the Problem
1.
1.
1.
## Specifications
- Version:
- Platform:
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Fixes #<issue_number_goes_here>
> It's a good idea to open an issue first for discussion.
- [ ] Tests pass
- [ ] Appropriate changes to README are included in PR
================================================
FILE: .github/workflows/android.yml
================================================
name: Android CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
with:
submodules: true
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'
- name: Build with Gradle
run: ./gradlew assembleDebug lintVitalRelease
================================================
FILE: .gitignore
================================================
/.gradle/
/.idea/
/build/
/captures/
/local.properties
.DS_Store
*.iml
================================================
FILE: .gitmodules
================================================
[submodule "appiconloader-iconloaderlib/systemui"]
path = appiconloader-iconloaderlib/systemui
url = https://android.googlesource.com/platform/frameworks/libs/systemui
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: PRIVACY.md
================================================
# Privacy Policy
Hai Zhang built the AppIconLoader Sample app as an Open Source app. This SERVICE is provided by Hai Zhang at no cost and is intended for use as is.
This Service does not collect, use, or share your Personal Information.
If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at https://github.com/zhanghai/AppIconLoader.
================================================
FILE: README.md
================================================
# AppIconLoader
[](https://github.com/zhanghai/AppIconLoader/actions) [](https://github.com/zhanghai/AppIconLoader/releases) [](LICENSE)
Android app icon loader from AOSP [iconloaderlib](https://android.googlesource.com/platform/frameworks/libs/systemui/+/refs/heads/master/iconloaderlib/), with optional Glide and Coil integration.
This is not an officially supported Google product.
## Why AppIconLoader?
Because [`PackageManager.getApplicationIcon()`](https://developer.android.com/reference/android/content/pm/PackageManager#getApplicationIcon(android.content.pm.ApplicationInfo)) (or [`PackageItemInfo.loadIcon()`](https://developer.android.com/reference/android/content/pm/PackageItemInfo#loadIcon(android.content.pm.PackageManager))) just doesn't work well with [adaptive icons](https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive). Non-adaptive icons usually have some shadow baked in (it's the recommended behavior), however adaptive icons never contain a shadow themselves, so we'll need to manually add the shadow or icons with a white background will just blend into our app's own background.
This library packaged the AOSP iconloaderlib for loading app icons, which has proper shadow and badging logic, and added easy integration with Glide and Coil.
Meanwhile, by passing `true` for the `shrinkNonAdaptiveIcons` parameter, this library can also synthesize adaptive icons for apps that don't have it.
## Preview
<a href="https://play.google.com/store/apps/details?id=me.zhanghai.android.appiconloader.sample" target="_blank"><img alt="Google Play" height="90" src="https://play.google.com/intl/en_US/badges/images/generic/en_badge_web_generic.png"/></a>
[Sample APK](https://github.com/zhanghai/AppIconLoader/releases/latest/download/sample-release.apk)
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2.png" width="49%" />
## Integration
Gradle:
```gradle
// For using with Glide.
implementation 'me.zhanghai.android.appiconloader:appiconloader-glide:1.5.0'
// For using with Coil.
implementation 'me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0'
// For using AppIconLoader directly.
implementation 'me.zhanghai.android.appiconloader:appiconloader:1.5.0'
// For using iconloaderlib directly.
implementation 'me.zhanghai.android.appiconloader:appiconloader-iconloaderlib:1.5.0'
```
## Usage
### Glide integration
See [Glide's documentation on registering a `ModuleLoader`](https://bumptech.github.io/glide/tut/custom-modelloader.html#registering-our-modelloader-with-glide).
Inside your implementation of `AppGlideModule.registerComponents()`, you can have something like the following code fragment:
```java
int iconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
registry.prepend(PackageInfo.class, Bitmap.class, new AppIconModelLoader.Factory(iconSize,
false, context));
```
See also the sample app's [`AppGlideModule` implementation](sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppGlideModule.java).
After the setup above, Glide will support loading app icons with the app's `PackageInfo`.
```java
GlideApp.with(imageView)
.load(packageInfo)
.into(imageView);
```
### Coil integration
```kotlin
val iconSize = context.resources.getDimensionPixelSize(R.dimen.app_icon_size)
Coil.setImageLoader(
ImageLoader.Builder(context)
.components {
add(AppIconKeyer())
add(AppIconFetcher.Factory(iconSize, false, context))
}
.build()
)
```
After the setup above, Coil will support loading app icons with the app's `PackageInfo`.
```kotlin
imageView.loadAny(packageInfo)
```
### AppIconLoader
[`AppIconLoader`](appiconloader/src/main/java/me/zhanghai/android/appiconloader/AppIconLoader.java) is the API exposed by this library, and you can simply call `AppIconLoader.loadIcon()` to load an app icon. You can also use `AppIconLoader.getIconKey()` to generate a cache key for your loaded icon.
### iconloaderlib
Please refer to [its source code](https://android.googlesource.com/platform/frameworks/libs/systemui/+/refs/heads/master/iconloaderlib/).
## License
Copyright 2020 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: appiconloader/.gitignore
================================================
/.externalNativeBuild/
/build/
/out/
================================================
FILE: appiconloader/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
versionCode Integer.parseInt(VERSION_CODE)
versionName VERSION_NAME
consumerProguardFiles 'proguard-rules.pro'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.4.0'
implementation project(':appiconloader-iconloaderlib')
}
apply plugin: 'com.vanniktech.maven.publish'
================================================
FILE: appiconloader/gradle.properties
================================================
POM_NAME=AppIconLoader
================================================
FILE: appiconloader/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: appiconloader/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest package="me.zhanghai.android.appiconloader" />
================================================
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/AppIconLoader.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import java.util.concurrent.ConcurrentLinkedQueue;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import me.zhanghai.android.appiconloader.iconloaderlib.BaseIconFactory;
import me.zhanghai.android.appiconloader.iconloaderlib.BitmapInfo;
public class AppIconLoader {
@Px
private final int mIconSize;
private final boolean mShrinkNonAdaptiveIcons;
@NonNull
private final Context mContext;
@NonNull
private final ConcurrentLinkedQueue<IconFactory> mIconFactoryPool =
new ConcurrentLinkedQueue<>();
public AppIconLoader(@Px int iconSize, boolean shrinkNonAdaptiveIcons,
@NonNull Context context) {
mIconSize = iconSize;
mShrinkNonAdaptiveIcons = shrinkNonAdaptiveIcons;
mContext = context;
}
@NonNull
public static String getIconKey(@NonNull ApplicationInfo applicationInfo, long versionCode,
@NonNull Context context) {
UserHandle user = UserHandleCompat.getUserHandleForUid(applicationInfo.uid);
return applicationInfo.packageName + ":" + versionCode + ":"
+ UserSerialNumberCache.getSerialNumber(user, context);
}
@NonNull
public static String getIconKey(@NonNull PackageInfo packageInfo, @NonNull Context context) {
return getIconKey(packageInfo.applicationInfo, PackageInfoCompat.getLongVersionCode(
packageInfo), context);
}
@NonNull
public Bitmap loadIcon(@NonNull ApplicationInfo applicationInfo, boolean isInstantApp) {
Drawable unbadgedIcon = PackageItemInfoCompat.loadUnbadgedIcon(applicationInfo,
mContext.getPackageManager());
UserHandle user = UserHandleCompat.getUserHandleForUid(applicationInfo.uid);
IconFactory iconFactory = mIconFactoryPool.poll();
if (iconFactory == null) {
iconFactory = new IconFactory(mIconSize, mContext);
}
try {
return iconFactory.createBadgedIconBitmap(unbadgedIcon, user, mShrinkNonAdaptiveIcons,
isInstantApp).icon;
} finally {
mIconFactoryPool.offer(iconFactory);
}
}
@NonNull
public Bitmap loadIcon(@NonNull ApplicationInfo applicationInfo) {
return loadIcon(applicationInfo, false);
}
private static class IconFactory extends BaseIconFactory {
private final float[] mTempScale = new float[1];
public IconFactory(@Px int iconBitmapSize, @NonNull Context context) {
super(context, context.getResources().getConfiguration().densityDpi, iconBitmapSize,
true);
disableColorExtraction();
}
@NonNull
public BitmapInfo createBadgedIconBitmap(@NonNull Drawable icon, @Nullable UserHandle user,
boolean shrinkNonAdaptiveIcons,
boolean isInstantApp) {
return super.createBadgedIconBitmap(icon, user, shrinkNonAdaptiveIcons, isInstantApp,
mTempScale);
}
}
}
================================================
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/PackageInfoCompat.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader;
import android.content.pm.PackageInfo;
import android.os.Build;
import androidx.annotation.NonNull;
class PackageInfoCompat {
private PackageInfoCompat() {}
public static long getLongVersionCode(@NonNull PackageInfo info) {
if (Build.VERSION.SDK_INT >= 28) {
return info.getLongVersionCode();
} else {
//noinspection deprecation
return info.versionCode;
}
}
}
================================================
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/PackageItemInfoCompat.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
class PackageItemInfoCompat {
private PackageItemInfoCompat() {}
public static Drawable loadUnbadgedIcon(@NonNull PackageItemInfo packageItemInfo,
@NonNull PackageManager packageManager) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
return packageItemInfo.loadUnbadgedIcon(packageManager);
} else {
ApplicationInfo applicationInfo = getApplicationInfo(packageItemInfo);
Drawable drawable = null;
if (packageItemInfo.packageName != null) {
drawable = packageManager.getDrawable(packageItemInfo.packageName,
packageItemInfo.icon, applicationInfo);
}
if (drawable == null && packageItemInfo != applicationInfo && applicationInfo != null) {
drawable = loadUnbadgedIcon(applicationInfo, packageManager);
}
if (drawable == null) {
drawable = loadDefaultIcon(packageItemInfo, packageManager);
}
return drawable;
}
}
@Nullable
private static ApplicationInfo getApplicationInfo(@NonNull PackageItemInfo packageItemInfo) {
if (packageItemInfo instanceof ApplicationInfo) {
return (ApplicationInfo) packageItemInfo;
} else if (packageItemInfo instanceof ComponentInfo) {
return ((ComponentInfo) packageItemInfo).applicationInfo;
} else {
return null;
}
}
@NonNull
private static Drawable loadDefaultIcon(@NonNull PackageItemInfo packageItemInfo,
@NonNull PackageManager packageManager) {
return packageManager.getDefaultActivityIcon();
}
}
================================================
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/UserHandleCompat.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader;
import android.os.Build;
import android.os.UserHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
class UserHandleCompat {
private static final int PER_USER_RANGE = 100000;
private static final int USER_SYSTEM = 0;
private static final boolean MU_ENABLED = true;
@Nullable
private static Constructor<UserHandle> sConstructor;
@NonNull
private static final Object sConstructorLock = new Object();
private UserHandleCompat() {}
@NonNull
public static UserHandle getUserHandleForUid(int uid) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return UserHandle.getUserHandleForUid(uid);
} else {
int userId = getUserId(uid);
Constructor<UserHandle> constructor = getConstructor();
try {
return constructor.newInstance(userId);
} catch (IllegalAccessException | InstantiationException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
private static int getUserId(int uid) {
if (MU_ENABLED) {
return uid / PER_USER_RANGE;
} else {
return USER_SYSTEM;
}
}
@NonNull
private static Constructor<UserHandle> getConstructor() {
synchronized (sConstructorLock) {
if (sConstructor == null) {
try {
sConstructor = UserHandle.class.getDeclaredConstructor(int.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
return sConstructor;
}
}
}
================================================
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/UserSerialNumberCache.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
import androidx.annotation.NonNull;
class UserSerialNumberCache {
private static final long CACHE_MILLIS = 1000;
@NonNull
private static final ArrayMap<UserHandle, long[]> sCache = new ArrayMap<>();
UserSerialNumberCache() {}
public static long getSerialNumber(@NonNull UserHandle user, @NonNull Context context) {
synchronized (sCache) {
long[] serialNumberAndTime = sCache.get(user);
if (serialNumberAndTime == null) {
serialNumberAndTime = new long[2];
sCache.put(user, serialNumberAndTime);
}
long time = System.currentTimeMillis();
if (serialNumberAndTime[1] + CACHE_MILLIS <= time) {
UserManager userManager = (UserManager) context.getSystemService(
Context.USER_SERVICE);
serialNumberAndTime[0] = userManager.getSerialNumberForUser(user);
serialNumberAndTime[1] = time;
}
return serialNumberAndTime[0];
}
}
}
================================================
FILE: appiconloader-coil/.gitignore
================================================
/.externalNativeBuild/
/build/
/out/
================================================
FILE: appiconloader-coil/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
versionCode Integer.parseInt(VERSION_CODE)
versionName VERSION_NAME
consumerProguardFiles 'proguard-rules.pro'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.4.0'
implementation 'io.coil-kt:coil-base:2.2.0'
implementation project(':appiconloader')
}
apply plugin: 'com.vanniktech.maven.publish'
================================================
FILE: appiconloader-coil/gradle.properties
================================================
POM_NAME=AppIconLoader Coil
================================================
FILE: appiconloader-coil/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: appiconloader-coil/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest package="me.zhanghai.android.appiconloader.coil" />
================================================
FILE: appiconloader-coil/src/main/java/me/zhanghai/android/appiconloader/coil/AppIconFetcher.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.coil;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import coil.ImageLoader;
import coil.decode.DataSource;
import coil.fetch.DrawableResult;
import coil.fetch.FetchResult;
import coil.fetch.Fetcher;
import coil.request.Options;
import kotlin.coroutines.Continuation;
import me.zhanghai.android.appiconloader.AppIconLoader;
public class AppIconFetcher implements Fetcher {
@NonNull
private final Options mOptions;
@NonNull
private final AppIconLoader mLoader;
@NonNull
private final ApplicationInfo mApplicationInfo;
public AppIconFetcher(@NonNull Options options, @NonNull AppIconLoader loader,
@NonNull ApplicationInfo applicationInfo) {
mOptions = options;
mLoader = loader;
mApplicationInfo = applicationInfo;
}
@Nullable
@Override
public FetchResult fetch(@NonNull Continuation<? super FetchResult> continuation) {
Bitmap icon = mLoader.loadIcon(mApplicationInfo);
return new DrawableResult(new BitmapDrawable(mOptions.getContext().getResources(), icon),
true, DataSource.DISK);
}
public static class Factory implements Fetcher.Factory<PackageInfo> {
@NonNull
private final AppIconLoader mLoader;
public Factory(@Px int iconSize, boolean shrinkNonAdaptiveIcons, @NonNull Context context) {
context = context.getApplicationContext();
mLoader = new AppIconLoader(iconSize, shrinkNonAdaptiveIcons, context);
}
@Nullable
@Override
public Fetcher create(@NonNull PackageInfo packageInfo, @NonNull Options options,
@NonNull ImageLoader imageLoader) {
return new AppIconFetcher(options, mLoader, packageInfo.applicationInfo);
}
}
}
================================================
FILE: appiconloader-coil/src/main/java/me/zhanghai/android/appiconloader/coil/AppIconKeyer.java
================================================
package me.zhanghai.android.appiconloader.coil;
import android.content.pm.PackageInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import coil.key.Keyer;
import coil.request.Options;
import me.zhanghai.android.appiconloader.AppIconLoader;
public class AppIconKeyer implements Keyer<PackageInfo> {
@Nullable
@Override
public String key(@NonNull PackageInfo packageInfo, @NonNull Options options) {
return AppIconLoader.getIconKey(packageInfo, options.getContext());
}
}
================================================
FILE: appiconloader-glide/.gitignore
================================================
/.externalNativeBuild/
/build/
/out/
================================================
FILE: appiconloader-glide/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
versionCode Integer.parseInt(VERSION_CODE)
versionName VERSION_NAME
consumerProguardFiles 'proguard-rules.pro'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.4.0'
implementation 'com.github.bumptech.glide:glide:4.13.2'
implementation project(':appiconloader')
}
apply plugin: 'com.vanniktech.maven.publish'
================================================
FILE: appiconloader-glide/gradle.properties
================================================
POM_NAME=AppIconLoader Glide
================================================
FILE: appiconloader-glide/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: appiconloader-glide/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest package="me.zhanghai.android.appiconloader.glide" />
================================================
FILE: appiconloader-glide/src/main/java/me/zhanghai/android/appiconloader/glide/AppIconModelLoader.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.glide;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import com.bumptech.glide.signature.ObjectKey;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import me.zhanghai.android.appiconloader.AppIconLoader;
public class AppIconModelLoader implements ModelLoader<PackageInfo, Bitmap> {
@NonNull
private final AppIconLoader mLoader;
@NonNull
private final Context mContext;
private AppIconModelLoader(@Px int iconSize, boolean shrinkNonAdaptiveIcons,
@NonNull Context context) {
mLoader = new AppIconLoader(iconSize, shrinkNonAdaptiveIcons, context);
mContext = context;
}
@Override
public boolean handles(@NonNull PackageInfo model) {
return true;
}
@Nullable
@Override
public LoadData<Bitmap> buildLoadData(@NonNull PackageInfo model, int width, int height,
@NonNull Options options) {
return new LoadData<>(new ObjectKey(AppIconLoader.getIconKey(model, mContext)), new Fetcher(
mLoader, model.applicationInfo));
}
private static class Fetcher implements DataFetcher<Bitmap> {
@NonNull
private final AppIconLoader mLoader;
@NonNull
private final ApplicationInfo mApplicationInfo;
public Fetcher(@NonNull AppIconLoader loader, @NonNull ApplicationInfo applicationInfo) {
mLoader = loader;
mApplicationInfo = applicationInfo;
}
@Override
public void loadData(@NonNull Priority priority,
@NonNull DataCallback<? super Bitmap> callback) {
try {
Bitmap icon = mLoader.loadIcon(mApplicationInfo);
callback.onDataReady(icon);
} catch (Exception e) {
callback.onLoadFailed(e);
}
}
@Override
public void cleanup() {}
@Override
public void cancel() {}
@NonNull
@Override
public Class<Bitmap> getDataClass() {
return Bitmap.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.LOCAL;
}
}
public static class Factory implements ModelLoaderFactory<PackageInfo, Bitmap> {
@Px
private final int mIconSize;
private final boolean mShrinkNonAdaptiveIcons;
@NonNull
private final Context mContext;
public Factory(@Px int iconSize, boolean shrinkNonAdaptiveIcons, @NonNull Context context) {
mIconSize = iconSize;
mShrinkNonAdaptiveIcons = shrinkNonAdaptiveIcons;
mContext = context.getApplicationContext();
}
@NonNull
@Override
public ModelLoader<PackageInfo, Bitmap> build(
@NonNull MultiModelLoaderFactory multiFactory) {
return new AppIconModelLoader(mIconSize, mShrinkNonAdaptiveIcons, mContext);
}
@Override
public void teardown() {}
}
}
================================================
FILE: appiconloader-iconloaderlib/.gitignore
================================================
/.externalNativeBuild/
/build/
/out/
================================================
FILE: appiconloader-iconloaderlib/build.gradle
================================================
apply plugin: 'com.android.library'
apply from: 'generate-iconloaderlib-src.gradle'
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
minSdkVersion 21
targetSdkVersion 33
versionCode Integer.parseInt(VERSION_CODE)
versionName VERSION_NAME
consumerProguardFiles 'proguard-rules.pro'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
release {
minifyEnabled false
}
}
}
dependencies {
implementation 'androidx.annotation:annotation:1.4.0'
}
apply plugin: 'com.vanniktech.maven.publish'
================================================
FILE: appiconloader-iconloaderlib/generate-iconloaderlib-src.gradle
================================================
def aospIconloaderlibSrcDir = file('systemui/iconloaderlib')
def generatedIconloaderlibSrcDir = file("$buildDir/generated/iconloaderlib")
task generateIconloaderlibSrc {
inputs.dir aospIconloaderlibSrcDir
outputs.dir generatedIconloaderlibSrcDir
doLast {
exec {
commandLine './generate-iconloaderlib-src.sh', aospIconloaderlibSrcDir,
generatedIconloaderlibSrcDir
}
}
}
preBuild.dependsOn generateIconloaderlibSrc
android {
sourceSets {
main {
java {
srcDirs += file("$generatedIconloaderlibSrcDir/java")
}
res {
srcDirs += file("$generatedIconloaderlibSrcDir/res")
}
}
}
}
================================================
FILE: appiconloader-iconloaderlib/generate-iconloaderlib-src.sh
================================================
#!/bin/bash
# Copyright 2020 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -eu
LAUNCHER3_CLASSES_ROOT="$1/src/com/android/launcher3/icons"
LAUNCHER3_RESOURCES_ROOT="$1/res"
LIBRARY_CLASSES_ROOT="$2/java/me/zhanghai/android/appiconloader/iconloaderlib"
LIBRARY_RESOURCES_ROOT="$2/res"
LIBRARY_CLASSES_FILES=(
"BaseIconFactory.java"
"BitmapInfo.java"
"BitmapRenderer.java"
"ColorExtractor.java"
"FixedScaleDrawable.java"
"GraphicsUtils.java"
"IconNormalizer.java"
"ShadowGenerator.java"
)
LIBRARY_RESOURCES_FILES=(
"drawable/ic_instant_app_badge.xml"
"drawable-v26/adaptive_icon_drawable_wrapper.xml"
"values/colors.xml"
"values/dimens.xml"
)
rm -rf "${LIBRARY_CLASSES_ROOT}"
for file in "${LIBRARY_CLASSES_FILES[@]}"; do
mkdir -p "$(dirname "${LIBRARY_CLASSES_ROOT}/${file}")"
cp "${LAUNCHER3_CLASSES_ROOT}/${file}" "${LIBRARY_CLASSES_ROOT}/${file}"
done
find "${LIBRARY_CLASSES_ROOT}" -iname '*.java' -type f -print0 | xargs -0 sed -Ei \
-e 's/\bcom\.android\.launcher3\.icons\b/me.zhanghai.android.appiconloader.iconloaderlib/g'
patch -d "${LIBRARY_CLASSES_ROOT}" -p0 <iconloaderlib-classes.patch
rm -rf "${LIBRARY_RESOURCES_ROOT}"
for file in "${LIBRARY_RESOURCES_FILES[@]}"; do
mkdir -p "$(dirname "${LIBRARY_RESOURCES_ROOT}/${file}")"
cp "${LAUNCHER3_RESOURCES_ROOT}/${file}" "${LIBRARY_RESOURCES_ROOT}/${file}"
done
find "${LIBRARY_RESOURCES_ROOT}" -iname '*.xml' -type f -print0 | xargs -0 sed -Ei \
-e 's/\bcom\.android\.launcher3\.icons\b/me.zhanghai.android.appiconloader.iconloaderlib/g'
patch -d "${LIBRARY_RESOURCES_ROOT}" -p0 <iconloaderlib-resources.patch
================================================
FILE: appiconloader-iconloaderlib/gradle.properties
================================================
POM_NAME=AppIconLoader iconloaderlib
================================================
FILE: appiconloader-iconloaderlib/iconloaderlib-classes.patch
================================================
--- BaseIconFactory.java
+++ BaseIconFactory.java
@@ -25,6 +25,7 @@ import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.ChecksSdkIntAtLeast;
import androidx.annotation.NonNull;
import me.zhanghai.android.appiconloader.iconloaderlib.BitmapInfo.Extender;
@@ -37,7 +38,9 @@ public class BaseIconFactory implements AutoCloseable {
private static final String TAG = "BaseIconFactory";
private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
static final boolean ATLEAST_OREO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P)
static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
private static final float ICON_BADGE_SCALE = 0.444f;
@@ -361,12 +364,16 @@ public class BaseIconFactory implements AutoCloseable {
if (ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
Math.round(size * (1 - scale) / 2 ));
- icon.setBounds(offset, offset, size - offset, size - offset);
+ // Work around bug in MIUI AdaptiveIconDrawableInjector.
+ //icon.setBounds(offset, offset, size - offset, size - offset);
+ icon.setBounds(0, 0, size - 2 * offset, size - 2 * offset);
+ mCanvas.translate(offset, offset);
if (icon instanceof BitmapInfo.Extender) {
((Extender) icon).drawForPersistence(mCanvas);
} else {
icon.draw(mCanvas);
}
+ mCanvas.translate(-offset, -offset);
} else {
if (icon instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
--- BitmapInfo.java
+++ BitmapInfo.java
@@ -15,37 +15,16 @@
*/
package me.zhanghai.android.appiconloader.iconloaderlib;
-import static me.zhanghai.android.appiconloader.iconloaderlib.GraphicsUtils.getExpectedBitmapSize;
-
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.UserHandle;
-import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import me.zhanghai.android.appiconloader.iconloaderlib.ThemedIconDrawable.ThemedBitmapInfo;
-import me.zhanghai.android.appiconloader.iconloaderlib.cache.BaseIconCache;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
public class BitmapInfo {
- public static final Bitmap LOW_RES_ICON = Bitmap.createBitmap(1, 1, Config.ALPHA_8);
- public static final BitmapInfo LOW_RES_INFO = fromBitmap(LOW_RES_ICON);
-
- public static final String TAG = "BitmapInfo";
-
- protected static final byte TYPE_DEFAULT = 1;
- protected static final byte TYPE_THEMED = 2;
-
public final Bitmap icon;
public final int color;
@@ -54,83 +33,6 @@ public class BitmapInfo {
this.color = color;
}
- /**
- * Ideally icon should not be null, except in cases when generating hardware bitmap failed
- */
- public final boolean isNullOrLowRes() {
- return icon == null || icon == LOW_RES_ICON;
- }
-
- public final boolean isLowRes() {
- return LOW_RES_ICON == icon;
- }
-
- /**
- * Returns a serialized version of BitmapInfo
- */
- @Nullable
- public byte[] toByteArray() {
- if (isNullOrLowRes()) {
- return null;
- }
- ByteArrayOutputStream out = new ByteArrayOutputStream(getExpectedBitmapSize(icon) + 1);
- try {
- out.write(TYPE_DEFAULT);
- icon.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- out.close();
- return out.toByteArray();
- } catch (IOException e) {
- Log.w(TAG, "Could not write bitmap");
- return null;
- }
- }
-
- /**
- * Returns a new icon based on the theme of the context
- */
- public FastBitmapDrawable newThemedIcon(Context context) {
- return newIcon(context);
- }
-
- /**
- * Creates a drawable for the provided BitmapInfo
- */
- public FastBitmapDrawable newIcon(Context context) {
- FastBitmapDrawable drawable = isLowRes()
- ? new PlaceHolderIconDrawable(this, context)
- : new FastBitmapDrawable(this);
- drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f);
- return drawable;
- }
-
- /**
- * Returns a BitmapInfo previously serialized using {@link #toByteArray()};
- */
- @NonNull
- public static BitmapInfo fromByteArray(byte[] data, int color, UserHandle user,
- BaseIconCache iconCache, Context context) {
- if (data == null) {
- return null;
- }
- BitmapFactory.Options decodeOptions;
- if (BitmapRenderer.USE_HARDWARE_BITMAP && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- decodeOptions = new BitmapFactory.Options();
- decodeOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
- } else {
- decodeOptions = null;
- }
- if (data[0] == TYPE_DEFAULT) {
- return BitmapInfo.of(
- BitmapFactory.decodeByteArray(data, 1, data.length - 1, decodeOptions),
- color);
- } else if (data[0] == TYPE_THEMED) {
- return ThemedBitmapInfo.decode(data, color, decodeOptions, user, iconCache, context);
- } else {
- return null;
- }
- }
-
public static BitmapInfo fromBitmap(@NonNull Bitmap bitmap) {
return of(bitmap, 0);
}
--- BitmapRenderer.java
+++ BitmapRenderer.java
@@ -25,11 +25,14 @@ import android.graphics.RectF;
import android.os.Build;
import android.os.Build.VERSION_CODES;
+import androidx.annotation.ChecksSdkIntAtLeast;
+
/**
* Interface representing a bitmap draw operation.
*/
public interface BitmapRenderer {
+ @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P)
boolean USE_HARDWARE_BITMAP = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
static Bitmap createSoftwareBitmap(int width, int height, BitmapRenderer renderer) {
--- FixedScaleDrawable.java
+++ FixedScaleDrawable.java
@@ -5,13 +5,17 @@ import android.content.res.Resources.Theme;
import android.graphics.Canvas;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.DrawableWrapper;
+import android.os.Build;
import android.util.AttributeSet;
+import androidx.annotation.RequiresApi;
+
import org.xmlpull.v1.XmlPullParser;
/**
* Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
*/
+@RequiresApi(Build.VERSION_CODES.O)
public class FixedScaleDrawable extends DrawableWrapper {
// TODO b/33553066 use the constant defined in MaskableIconDrawable
--- GraphicsUtils.java
+++ GraphicsUtils.java
@@ -15,23 +15,12 @@
*/
package me.zhanghai.android.appiconloader.iconloaderlib;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.RegionIterator;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.util.Log;
import androidx.annotation.ColorInt;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
public class GraphicsUtils {
private static final String TAG = "GraphicsUtils";
@@ -53,30 +42,6 @@ public class GraphicsUtils {
return (color & 0x00ffffff) | (alpha << 24);
}
- /**
- * Compresses the bitmap to a byte array for serialization.
- */
- public static byte[] flattenBitmap(Bitmap bitmap) {
- ByteArrayOutputStream out = new ByteArrayOutputStream(getExpectedBitmapSize(bitmap));
- try {
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
- out.flush();
- out.close();
- return out.toByteArray();
- } catch (IOException e) {
- Log.w(TAG, "Could not write bitmap");
- return null;
- }
- }
-
- /**
- * Try go guesstimate how much space the icon will take when serialized to avoid unnecessary
- * allocations/copies during the write (4 bytes per pixel).
- */
- static int getExpectedBitmapSize(Bitmap bitmap) {
- return bitmap.getWidth() * bitmap.getHeight() * 4;
- }
-
public static int getArea(Region r) {
RegionIterator itr = new RegionIterator(r);
int area = 0;
@@ -93,35 +58,4 @@ public class GraphicsUtils {
public static void noteNewBitmapCreated() {
sOnNewBitmapRunnable.run();
}
-
-
- /**
- * Returns the default path to be used by an icon
- */
- public static Path getShapePath(int size) {
- AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
- new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
- drawable.setBounds(0, 0, size, size);
- return new Path(drawable.getIconMask());
- }
-
- /**
- * Returns the color associated with the attribute
- */
- public static int getAttrColor(Context context, int attr) {
- TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
- int colorAccent = ta.getColor(0, 0);
- ta.recycle();
- return colorAccent;
- }
-
- /**
- * Returns the alpha corresponding to the theme attribute {@param attr}
- */
- public static float getFloat(Context context, int attr, float defValue) {
- TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
- float value = ta.getFloat(0, defValue);
- ta.recycle();
- return value;
- }
}
--- ShadowGenerator.java
+++ ShadowGenerator.java
@@ -35,14 +35,17 @@ public class ShadowGenerator {
public static final boolean ENABLE_SHADOWS = true;
- public static final float BLUR_FACTOR = 1.68f/48;
+ //public static final float BLUR_FACTOR = 1.68f/48;
+ public static final float BLUR_FACTOR = 0.5f/48;
// Percent of actual icon size
public static final float KEY_SHADOW_DISTANCE = 1f/48;
- private static final int KEY_SHADOW_ALPHA = 7;
+ //private static final int KEY_SHADOW_ALPHA = 7;
+ private static final int KEY_SHADOW_ALPHA = 61;
// Percent of actual icon size
private static final float HALF_DISTANCE = 0.5f;
- private static final int AMBIENT_SHADOW_ALPHA = 25;
+ //private static final int AMBIENT_SHADOW_ALPHA = 25;
+ private static final int AMBIENT_SHADOW_ALPHA = 30;
private final int mIconSize;
================================================
FILE: appiconloader-iconloaderlib/iconloaderlib-resources.patch
================================================
--- values/colors.xml
+++ values/colors.xml
@@ -18,7 +18,4 @@
-->
<resources>
<color name="legacy_icon_background">#FFFFFF</color>
-
- <!-- Yellow 600, used for highlighting "important" conversations in settings & notifications -->
- <color name="important_conversation">#f9ab00</color>
</resources>
================================================
FILE: appiconloader-iconloaderlib/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: appiconloader-iconloaderlib/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest package="me.zhanghai.android.appiconloader.iconloaderlib" />
================================================
FILE: build.gradle
================================================
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.2'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.15.1'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: docs/contributing.md
================================================
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
## Community Guidelines
This project follows [Google's Open Source Community
Guidelines](https://opensource.google/conduct/).
================================================
FILE: fastlane/metadata/android/en-US/changelogs/1.txt
================================================
- Initial release.
================================================
FILE: fastlane/metadata/android/en-US/changelogs/2.txt
================================================
- Fixed crash when using shrinkNonAdaptiveIcons due to package name mismatch.
- Added shrinkNonAdaptiveIcons as a parameter in APIs.
- AppIconLoader.loadIcon() now returns the result Bitmap directly.
================================================
FILE: fastlane/metadata/android/en-US/changelogs/3.txt
================================================
- Enabled shape detection by default.
- Moved the shrinkNonAdaptiveIcons parameter to AppIconLoader constructor.
================================================
FILE: fastlane/metadata/android/en-US/changelogs/4.txt
================================================
- Updated iconloaderlib to Android 11 source code.
================================================
FILE: fastlane/metadata/android/en-US/changelogs/5.txt
================================================
- Fixed MIUI "optimization" that draws an extra shadow.
- Fixed the sample app to show all apps on Android 11.
- Published library to Maven Central.
================================================
FILE: fastlane/metadata/android/en-US/changelogs/6.txt
================================================
- Synchronized iconloaderlib code from Android 12L.
- Patched iconloaderlib to fix MIUI "optimization" reliably.
================================================
FILE: fastlane/metadata/android/en-US/changelogs/7.txt
================================================
- Migrated appiconloader-coil to Coil 2.
================================================
FILE: fastlane/metadata/android/en-US/full_description.txt
================================================
https://github.com/zhanghai/AppIconLoader
================================================
FILE: fastlane/metadata/android/en-US/short_description.txt
================================================
App icon loader from AOSP Launcher3
================================================
FILE: fastlane/metadata/android/en-US/title.txt
================================================
AppIconLoader Sample
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Thu Sep 23 01:27:10 PDT 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.enableJetifier=true
android.useAndroidX=true
GROUP=me.zhanghai.android.appiconloader
VERSION_NAME=1.5.0
VERSION_CODE=7
POM_DESCRIPTION=Android app icon loader from AOSP iconloaderlib
POM_INCEPTION_YEAR=2020
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=zhanghai
POM_DEVELOPER_NAME=Hai Zhang
POM_DEVELOPER_URL=https://github.com/zhanghai
POM_SCM_CONNECTION=scm:git@github.com:zhanghai/AppIconLoader.git
POM_SCM_DEV_CONNECTION=scm:git@github.com:zhanghai/AppIconLoader.git
POM_SCM_URL=https://github.com/zhanghai/AppIconLoader
POM_URL=https://github.com/zhanghai/AppIconLoader
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: sample/.gitignore
================================================
/.externalNativeBuild/
/build/
/out/
================================================
FILE: sample/build.gradle
================================================
apply plugin: 'com.android.application'
android {
compileSdkVersion 33
buildToolsVersion '33.0.0'
defaultConfig {
applicationId 'me.zhanghai.android.appiconloader.sample'
minSdkVersion 21
targetSdkVersion 33
versionCode Integer.parseInt(VERSION_CODE)
versionName VERSION_NAME
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
viewBinding {
enabled = true
}
signingConfigs {
release {
storeFile file(System.getenv('SAMPLE_STORE_FILE') ?: '/dev/null')
storePassword System.getenv('SAMPLE_STORE_PASSWORD')
keyAlias System.getenv('SAMPLE_KEY_ALIAS')
keyPassword System.getenv('SAMPLE_KEY_PASSWORD')
}
}
buildTypes {
release {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.5.0'
def androidx_lifecycle_version = '2.5.1'
implementation "androidx.lifecycle:lifecycle-viewmodel:$androidx_lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata:$androidx_lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$androidx_lifecycle_version"
implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'com.google.android.material:material:1.6.1'
def glide_version = '4.13.2'
implementation "com.github.bumptech.glide:glide:$glide_version"
annotationProcessor "com.github.bumptech.glide:compiler:$glide_version"
implementation 'io.coil-kt:coil:2.2.0'
implementation 'me.zhanghai.android.fastscroll:library:1.1.8'
implementation project(':appiconloader')
implementation project(':appiconloader-coil')
implementation project(':appiconloader-glide')
}
================================================
FILE: sample/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="me.zhanghai.android.appiconloader.sample">
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<application
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@mipmap/launcher_icon"
android:label="@string/app_name"
android:roundIcon="@mipmap/launcher_icon"
android:supportsRtl="true"
android:theme="@style/Theme.Sample">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppGlideModule.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import androidx.annotation.NonNull;
import me.zhanghai.android.appiconloader.glide.AppIconModelLoader;
@GlideModule
public class AppGlideModule extends com.bumptech.glide.module.AppGlideModule {
@Override
public boolean isManifestParsingEnabled() {
return false;
}
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
@NonNull Registry registry) {
int iconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
registry.prepend(PackageInfo.class, Bitmap.class, new AppIconModelLoader.Factory(iconSize,
false, context));
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListAdapter.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.pm.PackageInfo;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.ImageView;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.core.content.pm.PackageInfoCompat;
import androidx.core.util.Pair;
import androidx.recyclerview.widget.RecyclerView;
import me.zhanghai.android.appiconloader.sample.databinding.AppItemBinding;
import me.zhanghai.android.fastscroll.PopupTextProvider;
public class AppListAdapter extends RecyclerView.Adapter<AppListAdapter.ViewHolder>
implements PopupTextProvider {
@NonNull
private final IconLoader mIconLoader;
@NonNull
private final List<Pair<PackageInfo, String>> mApps = new ArrayList<>();
public AppListAdapter(@NonNull IconLoader iconLoader) {
mIconLoader = iconLoader;
setHasStableIds(true);
}
public void replace(@NonNull List<Pair<PackageInfo, String>> apps) {
mApps.clear();
mApps.addAll(apps);
notifyDataSetChanged();
}
@Override
public int getItemCount() {
return mApps.size();
}
@NonNull
private Pair<PackageInfo, String> getItem(int position) {
return mApps.get(position);
}
@Override
public long getItemId(int position) {
PackageInfo packageInfo = getItem(position).first;
return Objects.hash(packageInfo.packageName, PackageInfoCompat.getLongVersionCode(
packageInfo), packageInfo.applicationInfo.uid);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(AppItemBinding.inflate(LayoutInflater.from(parent.getContext()),
parent, false));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Pair<PackageInfo, String> app = getItem(position);
PackageInfo packageInfo = app.first;
String label = app.second;
mIconLoader.loadIcon(holder.binding.iconImage, packageInfo);
holder.binding.labelText.setText(label);
holder.binding.descriptionText.setText(
holder.binding.descriptionText.getContext().getString(
R.string.app_description_format, packageInfo.packageName,
packageInfo.applicationInfo.uid));
holder.binding.getRoot().setOnClickListener(view -> AppListLoader.startAppDetailsActivity(
packageInfo, view.getContext()));
}
@NonNull
@Override
public String getPopupText(int position) {
String label = getItem(position).second;
return label.substring(0, label.offsetByCodePoints(0, 1)).toUpperCase(Locale.getDefault());
}
public interface IconLoader {
void loadIcon(@NonNull ImageView imageView, @NonNull PackageInfo packageInfo);
}
static class ViewHolder extends RecyclerView.ViewHolder {
public final AppItemBinding binding;
public ViewHolder(@NonNull AppItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListFragment.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import me.zhanghai.android.appiconloader.sample.databinding.AppListFragmentBinding;
import me.zhanghai.android.fastscroll.FastScrollerBuilder;
public abstract class AppListFragment extends Fragment {
private AppListFragmentBinding mBinding;
private AppListAdapter mAdapter;
private AppListViewModel mViewModel;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mBinding = AppListFragmentBinding.inflate(inflater, container, false);
return mBinding.getRoot();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Activity activity = requireActivity();
mBinding.recycler.setLayoutManager(new LinearLayoutManager(activity));
mAdapter = new AppListAdapter(onCreateIconLoader());
mBinding.recycler.setAdapter(mAdapter);
new FastScrollerBuilder(mBinding.recycler).useMd2Style().build();
mViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(
activity.getApplication())).get(AppListViewModel.class);
mViewModel.getAppListLiveData().observe(getViewLifecycleOwner(), apps -> {
mBinding.progress.setVisibility(View.GONE);
mAdapter.replace(apps);
});
}
@NonNull
protected abstract AppListAdapter.IconLoader onCreateIconLoader();
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListLiveData.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.os.AsyncTask;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.lifecycle.LiveData;
public class AppListLiveData extends LiveData<List<Pair<PackageInfo, String>>> {
private final Context mContext;
public AppListLiveData(@NonNull Context context) {
mContext = context.getApplicationContext();
loadValue();
}
private void loadValue() {
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
List<Pair<PackageInfo, String>> value = AppListLoader.loadAppList(mContext);
postValue(value);
});
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListLoader.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.util.Pair;
public class AppListLoader {
private AppListLoader() {}
@NonNull
public static List<Pair<PackageInfo, String>> loadAppList(@NonNull Context context) {
PackageManager packageManager = context.getPackageManager();
List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
Iterator<PackageInfo> iterator = packageInfos.iterator();
while (iterator.hasNext()) {
PackageInfo packageInfo = iterator.next();
if (packageInfo.applicationInfo == null) {
iterator.remove();
}
}
LauncherApps launcherApps = ContextCompat.getSystemService(context, LauncherApps.class);
List<UserHandle> profiles = LauncherAppsCompat.getProfiles(launcherApps, context);
profiles.remove(Process.myUserHandle());
if (!profiles.isEmpty()) {
ArrayMap<String, PackageInfo> packageInfoMap = new ArrayMap<>();
for (PackageInfo packageInfo : packageInfos) {
packageInfoMap.put(packageInfo.packageName, packageInfo);
}
for (UserHandle profile : profiles) {
List<LauncherActivityInfo> activityList = launcherApps.getActivityList(null,
profile);
ArrayMap<String, ApplicationInfo> applicationInfoMap = new ArrayMap<>();
for (LauncherActivityInfo launcherActivityInfo : activityList) {
ApplicationInfo applicationInfo = launcherActivityInfo.getApplicationInfo();
if (!applicationInfoMap.containsKey(applicationInfo.packageName)) {
applicationInfoMap.put(applicationInfo.packageName, applicationInfo);
}
}
for (ApplicationInfo applicationInfo : applicationInfoMap.values()) {
PackageInfo packageInfo;
String packageName = applicationInfo.packageName;
if (packageInfoMap.containsKey(packageName)) {
packageInfo = ParcelableCloner.cloneParcelable(packageInfoMap.get(
packageName), PackageInfo.class.getClassLoader());
} else {
try {
packageInfo = packageManager.getPackageInfo(packageName,
PackageManagerCompat.MATCH_UNINSTALLED_PACKAGES);
} catch (PackageManager.NameNotFoundException e) {
continue;
}
}
packageInfo.applicationInfo = applicationInfo;
packageInfos.add(packageInfo);
}
}
}
List<Pair<PackageInfo, String>> apps = new ArrayList<>();
for (PackageInfo packageInfo : packageInfos) {
String label = packageInfo.applicationInfo.loadLabel(packageManager).toString();
apps.add(new Pair<>(packageInfo, label));
}
Collator collator = Collator.getInstance();
Collections.sort(apps, (first, second) -> {
int result = collator.compare(first.second, second.second);
if (result == 0) {
result = first.first.packageName.compareTo(second.first.packageName);
}
if (result == 0) {
result = Integer.compare(first.first.applicationInfo.uid,
second.first.applicationInfo.uid);
}
return result;
});
return apps;
}
public static void startAppDetailsActivity(@NonNull PackageInfo packageInfo,
@NonNull Context context) {
LauncherApps launcherApps = ContextCompat.getSystemService(context, LauncherApps.class);
ComponentName componentName = new ComponentName(packageInfo.packageName, "");
UserHandle user = UserHandleCompat.getUserHandleForUid(packageInfo.applicationInfo.uid);
launcherApps.startAppDetailsActivity(componentName, user, null, null);
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListViewModel.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.app.Application;
import android.content.pm.PackageInfo;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
public class AppListViewModel extends AndroidViewModel {
@NonNull
private final AppListLiveData mAppListLiveData;
public AppListViewModel(@NonNull Application application) {
super(application);
mAppListLiveData = new AppListLiveData(application);
}
@NonNull
public LiveData<List<Pair<PackageInfo, String>>> getAppListLiveData() {
return mAppListLiveData;
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoilAppListFragment.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import coil.Coil;
import coil.ImageLoader;
import coil.request.ImageRequest;
public class CoilAppListFragment extends AppListFragment {
@NonNull
public static CoilAppListFragment newInstance() {
return new CoilAppListFragment();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
CoilInitializer.initializeCoil(requireContext().getApplicationContext());
}
@NonNull
@Override
protected AppListAdapter.IconLoader onCreateIconLoader() {
return (imageView, packageInfo) -> {
Context context = imageView.getContext();
ImageLoader loader = Coil.imageLoader(context);
loader.enqueue(new ImageRequest.Builder(context)
.data(packageInfo)
.target(imageView)
.build());
};
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoilInitializer.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.content.pm.PackageInfo;
import androidx.annotation.NonNull;
import coil.Coil;
import coil.ComponentRegistry;
import coil.ImageLoader;
import me.zhanghai.android.appiconloader.coil.AppIconFetcher;
import me.zhanghai.android.appiconloader.coil.AppIconKeyer;
public class CoilInitializer {
private static boolean sInitialized = false;
@NonNull
private static final Object sInitializedLock = new Object();
private CoilInitializer() {}
public static void initializeCoil(@NonNull Context context) {
synchronized (sInitializedLock) {
if (sInitialized) {
return;
}
context = context.getApplicationContext();
int iconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
Coil.setImageLoader(new ImageLoader.Builder(context)
.components(new ComponentRegistry.Builder()
.add(new AppIconKeyer(), PackageInfo.class)
.add(new AppIconFetcher.Factory(iconSize, false, context),
PackageInfo.class)
.build())
.build());
sInitialized = true;
}
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoordinatorScrollingFrameLayout.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import com.google.android.material.appbar.AppBarLayout;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.graphics.Insets;
import androidx.core.view.WindowInsetsCompat;
public class CoordinatorScrollingFrameLayout extends FrameLayout
implements CoordinatorLayout.AttachedBehavior {
public CoordinatorScrollingFrameLayout(@NonNull Context context) {
super(context);
}
public CoordinatorScrollingFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CoordinatorScrollingFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CoordinatorScrollingFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@NonNull
@Override
public WindowInsets dispatchApplyWindowInsets(@NonNull WindowInsets insets) {
insets = new WindowInsetsCompat.Builder(WindowInsetsCompat.toWindowInsetsCompat(insets))
.setSystemWindowInsets(Insets.of(insets.getSystemWindowInsetLeft(), 0,
insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()))
.build()
.toWindowInsets();
super.dispatchApplyWindowInsets(insets);
return insets;
}
@NonNull
@Override
public WindowInsets onApplyWindowInsets(@NonNull WindowInsets insets) {
return insets;
}
@NonNull
@Override
public CoordinatorLayout.Behavior getBehavior() {
return new Behavior();
}
private static class Behavior extends AppBarLayout.ScrollingViewBehavior {
@Override
public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
@SuppressLint("RestrictedApi")
WindowInsetsCompat parentInsets = parent.getLastWindowInsets();
if (parentInsets != null) {
int parentHeightSize = View.MeasureSpec.getSize(parentHeightMeasureSpec);
parentHeightSize -= parentInsets.getSystemWindowInsetTop()
+ parentInsets.getSystemWindowInsetBottom();
int parentHeightMode = View.MeasureSpec.getMode(parentHeightMeasureSpec);
parentHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(parentHeightSize,
parentHeightMode);
}
return super.onMeasureChild(parent, child, parentWidthMeasureSpec, widthUsed,
parentHeightMeasureSpec, heightUsed);
}
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/FastScrollerLiftOnScrollHack.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import com.google.android.material.appbar.AppBarLayout;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
public class FastScrollerLiftOnScrollHack {
private FastScrollerLiftOnScrollHack() {}
public static void hack(@NonNull AppBarLayout appBarLayout, int liftOnScrollTargetViewId) {
appBarLayout.getViewTreeObserver().addOnPreDrawListener(() -> {
// Invalidate the cached view reference so that this works after replacing fragment.
appBarLayout.setLiftOnScrollTargetViewId(liftOnScrollTargetViewId);
// Call AppBarLayout.Behavior.onNestedPreScroll() with dy == 0 to update lifted state.
CoordinatorLayout.Behavior behavior =
((CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams()).getBehavior();
CoordinatorLayout coordinatorLayout = (CoordinatorLayout) appBarLayout.getParent();
behavior.onNestedPreScroll(coordinatorLayout, appBarLayout, coordinatorLayout, 0, 0,
null, 0);
return true;
});
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/GlideAppListFragment.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import androidx.annotation.NonNull;
public class GlideAppListFragment extends AppListFragment {
@NonNull
public static GlideAppListFragment newInstance() {
return new GlideAppListFragment();
}
@NonNull
@Override
protected AppListAdapter.IconLoader onCreateIconLoader() {
return (imageView, packageInfo) -> GlideApp.with(imageView)
.load(packageInfo)
.into(imageView);
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/LauncherAppsCompat.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
public class LauncherAppsCompat {
private LauncherAppsCompat() {}
@NonNull
public static List<UserHandle> getProfiles(@NonNull LauncherApps launcherApps,
@NonNull Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return launcherApps.getProfiles();
} else {
UserManager userManager = ContextCompat.getSystemService(context, UserManager.class);
return userManager.getUserProfiles();
}
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/MainActivity.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@NonNull
private MainFragment mMainFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Calls ensureSubDecor().
findViewById(android.R.id.content);
if (savedInstanceState == null) {
mMainFragment = MainFragment.newInstance();
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, mMainFragment)
.commit();
} else {
mMainFragment = (MainFragment) getSupportFragmentManager().findFragmentById(
android.R.id.content);
}
}
@Override
public void onBackPressed() {
if (mMainFragment.onBackPressed()) {
return;
}
super.onBackPressed();
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/MainFragment.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.view.GravityCompat;
import androidx.fragment.app.Fragment;
import me.zhanghai.android.appiconloader.sample.databinding.MainFragmentBinding;
public class MainFragment extends Fragment {
private static final Uri GITHUB_URI = Uri.parse("https://github.com/zhanghai/AppIconLoader");
private MainFragmentBinding mBinding;
@NonNull
public static MainFragment newInstance() {
return new MainFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
mBinding = MainFragmentBinding.inflate(inflater, container, false);
return mBinding.getRoot();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
AppCompatActivity activity = (AppCompatActivity) requireActivity();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
View decorView = activity.getWindow().getDecorView();
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
}
activity.setSupportActionBar(mBinding.toolbar);
if (savedInstanceState == null) {
setNavigationCheckedItem(R.id.glide);
}
mBinding.navigationView.setNavigationItemSelectedListener(this::onNavigationItemSelected);
FastScrollerLiftOnScrollHack.hack(mBinding.appBarLayout, R.id.recycler);
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
inflater.inflate(R.menu.main, menu);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mBinding.drawerLayout.openDrawer(GravityCompat.START);
return true;
case R.id.action_view_on_github:
viewOnGitHub();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void viewOnGitHub() {
try {
startActivity(new Intent(Intent.ACTION_VIEW, GITHUB_URI));
} catch (ActivityNotFoundException e) {
e.printStackTrace();
Toast.makeText(requireContext(), e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
}
private boolean onNavigationItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
switch (item.getItemId()) {
case R.id.glide:
case R.id.coil:
case R.id.synchronous:
setNavigationCheckedItem(itemId);
mBinding.drawerLayout.closeDrawer(GravityCompat.START);
return true;
default:
return false;
}
}
private void setNavigationCheckedItem(@IdRes int itemId) {
MenuItem item = mBinding.navigationView.getCheckedItem();
if (item != null && item.getItemId() == itemId) {
return;
}
Fragment fragment;
switch (itemId) {
case R.id.glide:
fragment = GlideAppListFragment.newInstance();
break;
case R.id.coil:
fragment = CoilAppListFragment.newInstance();
break;
case R.id.synchronous:
fragment = SynchronousAppListFragment.newInstance();
break;
default:
throw new AssertionError(itemId);
}
getChildFragmentManager().beginTransaction()
.replace(R.id.containerLayout, fragment)
.commit();
mBinding.navigationView.setCheckedItem(itemId);
}
public boolean onBackPressed() {
if (mBinding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
mBinding.drawerLayout.closeDrawer(GravityCompat.START);
return true;
}
return false;
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/NewDispatchApplyWindowInsetsFrameLayout.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.util.AttributeSet;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
public class NewDispatchApplyWindowInsetsFrameLayout extends FrameLayout {
public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context) {
super(context);
}
public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
}
public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context,
@Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context,
@Nullable AttributeSet attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@NonNull
@Override
public WindowInsets dispatchApplyWindowInsets(@NonNull WindowInsets insets) {
for (int i = 0, count = getChildCount(); i < count; ++i) {
getChildAt(i).dispatchApplyWindowInsets(insets);
}
return insets;
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/PackageManagerCompat.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.pm.PackageManager;
import android.os.Build;
public class PackageManagerCompat {
private PackageManagerCompat() {}
public static final int MATCH_UNINSTALLED_PACKAGES =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
PackageManager.MATCH_UNINSTALLED_PACKAGES
: PackageManager.GET_UNINSTALLED_PACKAGES;
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/ParcelableCloner.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.Nullable;
public class ParcelableCloner {
private ParcelableCloner() {}
public static <T extends Parcelable> T cloneParcelable(T parcelable,
@Nullable ClassLoader classLoader) {
Parcel parcel = Parcel.obtain();
try {
parcel.writeParcelable(parcelable, 0);
parcel.setDataPosition(0);
return parcel.readParcelable(classLoader);
} finally {
parcel.recycle();
}
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/SynchronousAppListFragment.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import me.zhanghai.android.appiconloader.AppIconLoader;
public class SynchronousAppListFragment extends AppListFragment {
private AppIconLoader mAppIconLoader;
@NonNull
public static SynchronousAppListFragment newInstance() {
return new SynchronousAppListFragment();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Context context = requireContext();
mAppIconLoader = new AppIconLoader(context.getResources().getDimensionPixelSize(
R.dimen.app_icon_size), false, context.getApplicationContext());
}
@NonNull
@Override
protected AppListAdapter.IconLoader onCreateIconLoader() {
return (imageView, packageInfo) -> imageView.setImageBitmap(mAppIconLoader.loadIcon(
packageInfo.applicationInfo));
}
}
================================================
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/UserHandleCompat.java
================================================
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package me.zhanghai.android.appiconloader.sample;
import android.os.Build;
import android.os.UserHandle;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class UserHandleCompat {
private static final int PER_USER_RANGE = 100000;
private static final int USER_SYSTEM = 0;
private static final boolean MU_ENABLED = true;
@Nullable
private static Constructor<UserHandle> sConstructor;
@NonNull
private static final Object sConstructorLock = new Object();
private UserHandleCompat() {}
@NonNull
public static UserHandle getUserHandleForUid(int uid) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return UserHandle.getUserHandleForUid(uid);
} else {
int userId = getUserId(uid);
Constructor<UserHandle> constructor = getConstructor();
try {
return constructor.newInstance(userId);
} catch (IllegalAccessException | InstantiationException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
private static int getUserId(int uid) {
if (MU_ENABLED) {
return uid / PER_USER_RANGE;
} else {
return USER_SYSTEM;
}
}
@NonNull
private static Constructor<UserHandle> getConstructor() {
synchronized (sConstructorLock) {
if (sConstructor == null) {
try {
sConstructor = UserHandle.class.getDeclaredConstructor(int.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
return sConstructor;
}
}
}
================================================
FILE: sample/src/main/res/drawable/info_icon_control_normal_24dp.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M11 7h2v2h-2zm0 4h2v6h-2zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z" />
</vector>
================================================
FILE: sample/src/main/res/drawable/menu_icon_control_normal_24dp.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?colorControlNormal">
<path
android:fillColor="#FF000000"
android:pathData="M3,18h18v-2H3v2zm0,-5h18v-2H3v2zm0,-7v2h18V6H3z" />
</vector>
================================================
FILE: sample/src/main/res/layout/app_item.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?listPreferredItemHeight"
android:paddingStart="?listPreferredItemPaddingStart"
android:paddingEnd="?listPreferredItemPaddingEnd"
android:background="?selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/iconImage"
android:layout_width="@dimen/app_icon_size"
android:layout_height="@dimen/app_icon_size" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginStart="?listPreferredItemPaddingStart"
android:orientation="vertical">
<TextView
android:id="@+id/labelText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1" />
<TextView
android:id="@+id/descriptionText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="?android:textColorSecondary" />
</LinearLayout>
</LinearLayout>
================================================
FILE: sample/src/main/res/layout/app_list_fragment.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<me.zhanghai.android.appiconloader.sample.NewDispatchApplyWindowInsetsFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:fitsSystemWindows="true" />
</me.zhanghai.android.appiconloader.sample.NewDispatchApplyWindowInsetsFrameLayout>
================================================
FILE: sample/src/main/res/layout/main_fragment.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorSurface"
android:fitsSystemWindows="true"
android:theme="?actionBarTheme"
app:liftOnScroll="true"
app:liftOnScrollTargetViewId="@id/recycler">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
app:navigationIcon="@drawable/menu_icon_control_normal_24dp"
app:popupTheme="?actionBarPopupTheme" />
</com.google.android.material.appbar.AppBarLayout>
<me.zhanghai.android.appiconloader.sample.CoordinatorScrollingFrameLayout
android:id="@+id/containerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.google.android.material.navigation.NavigationView
android:id="@+id/navigationView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:insetForeground="@null"
app:menu="@menu/navigation" />
</androidx.drawerlayout.widget.DrawerLayout>
================================================
FILE: sample/src/main/res/menu/main.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_view_on_github"
android:icon="@drawable/info_icon_control_normal_24dp"
android:orderInCategory="100"
android:title="@string/view_on_github"
app:showAsAction="always" />
</menu>
================================================
FILE: sample/src/main/res/menu/navigation.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/glide"
android:title="@string/glide" />
<item
android:id="@+id/coil"
android:title="@string/coil" />
<item
android:id="@+id/synchronous"
android:title="@string/synchronous" />
</group>
</menu>
================================================
FILE: sample/src/main/res/values/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<color name="color_secondary_light">#009688</color>
<color name="color_secondary_dark">#80cbc4</color>
<color name="color_secondary">@color/color_secondary_light</color>
<color name="system_window_scrim_light">#99FFFFFF</color>
<color name="system_window_scrim_dark">#52000000</color>
<color name="system_window_scrim">@color/system_window_scrim_light</color>
<color name="system_window_scrim_compat_light">#BBBBBB</color>
<color name="system_window_scrim_compat">@color/system_window_scrim_compat_light</color>
</resources>
================================================
FILE: sample/src/main/res/values/dimens.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<dimen name="app_icon_size">40dp</dimen>
</resources>
================================================
FILE: sample/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<string name="app_name">AppIconLoader Sample</string>
<string name="glide">Glide</string>
<string name="coil">Coil</string>
<string name="synchronous">Synchronous (laggy)</string>
<string name="view_on_github">View on GitHub</string>
<string name="app_description_format">%1$s (%2$d)</string>
</resources>
================================================
FILE: sample/src/main/res/values/themes.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Platform.V21.Theme.Sample" parent="Theme.MaterialComponents.DayNight">
<item name="colorPrimaryDark">@color/system_window_scrim_compat</item>
</style>
<style name="Platform.Theme.Sample" parent="Platform.V21.Theme.Sample" />
<style name="Base.Theme.Sample" parent="Platform.Theme.Sample">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">?colorSecondary</item>
<item name="colorOnPrimary">?colorOnSecondary</item>
<item name="colorSecondary">@color/color_secondary</item>
</style>
<style name="Theme.Sample" parent="Base.Theme.Sample" />
</resources>
================================================
FILE: sample/src/main/res/values-night/colors.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<color name="color_secondary">@color/color_secondary_dark</color>
<color name="system_window_scrim">@color/system_window_scrim_dark</color>
<color name="system_window_scrim_compat">@color/system_window_scrim_dark</color>
</resources>
================================================
FILE: sample/src/main/res/values-v23/themes.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Platform.V23.Theme.Sample" parent="Platform.V21.Theme.Sample">
<item name="colorPrimaryDark">@color/system_window_scrim</item>
<item name="android:windowLightStatusBar">?isLightTheme</item>
</style>
<style name="Platform.Theme.Sample" parent="Platform.V23.Theme.Sample" />
</resources>
================================================
FILE: sample/src/main/res/values-v27/themes.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Platform.V27.Theme.Sample" parent="Platform.V23.Theme.Sample">
<item name="android:navigationBarColor">?colorPrimaryDark</item>
<item name="android:windowLightNavigationBar">?isLightTheme</item>
</style>
<style name="Platform.Theme.Sample" parent="Platform.V27.Theme.Sample" />
</resources>
================================================
FILE: sample/src/main/res/values-v29/themes.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2020 Google LLC
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<resources>
<style name="Platform.V29.Theme.Sample" parent="Platform.V27.Theme.Sample">
<item name="colorPrimaryDark">@android:color/transparent</item>
</style>
<style name="Platform.Theme.Sample" parent="Platform.V29.Theme.Sample" />
</resources>
================================================
FILE: settings.gradle
================================================
include ':appiconloader', ':appiconloader-coil', ':appiconloader-glide', ':appiconloader-iconloaderlib', ':sample'
gitextract_gr1qz0j7/ ├── .github/ │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ └── android.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── PRIVACY.md ├── README.md ├── appiconloader/ │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── me/ │ └── zhanghai/ │ └── android/ │ └── appiconloader/ │ ├── AppIconLoader.java │ ├── PackageInfoCompat.java │ ├── PackageItemInfoCompat.java │ ├── UserHandleCompat.java │ └── UserSerialNumberCache.java ├── appiconloader-coil/ │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── me/ │ └── zhanghai/ │ └── android/ │ └── appiconloader/ │ └── coil/ │ ├── AppIconFetcher.java │ └── AppIconKeyer.java ├── appiconloader-glide/ │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── me/ │ └── zhanghai/ │ └── android/ │ └── appiconloader/ │ └── glide/ │ └── AppIconModelLoader.java ├── appiconloader-iconloaderlib/ │ ├── .gitignore │ ├── build.gradle │ ├── generate-iconloaderlib-src.gradle │ ├── generate-iconloaderlib-src.sh │ ├── gradle.properties │ ├── iconloaderlib-classes.patch │ ├── iconloaderlib-resources.patch │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ └── AndroidManifest.xml ├── build.gradle ├── docs/ │ └── contributing.md ├── fastlane/ │ └── metadata/ │ └── android/ │ └── en-US/ │ ├── changelogs/ │ │ ├── 1.txt │ │ ├── 2.txt │ │ ├── 3.txt │ │ ├── 4.txt │ │ ├── 5.txt │ │ ├── 6.txt │ │ └── 7.txt │ ├── full_description.txt │ ├── short_description.txt │ └── title.txt ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── sample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── me/ │ │ └── zhanghai/ │ │ └── android/ │ │ └── appiconloader/ │ │ └── sample/ │ │ ├── AppGlideModule.java │ │ ├── AppListAdapter.java │ │ ├── AppListFragment.java │ │ ├── AppListLiveData.java │ │ ├── AppListLoader.java │ │ ├── AppListViewModel.java │ │ ├── CoilAppListFragment.java │ │ ├── CoilInitializer.java │ │ ├── CoordinatorScrollingFrameLayout.java │ │ ├── FastScrollerLiftOnScrollHack.java │ │ ├── GlideAppListFragment.java │ │ ├── LauncherAppsCompat.java │ │ ├── MainActivity.java │ │ ├── MainFragment.java │ │ ├── NewDispatchApplyWindowInsetsFrameLayout.java │ │ ├── PackageManagerCompat.java │ │ ├── ParcelableCloner.java │ │ ├── SynchronousAppListFragment.java │ │ └── UserHandleCompat.java │ └── res/ │ ├── drawable/ │ │ ├── info_icon_control_normal_24dp.xml │ │ └── menu_icon_control_normal_24dp.xml │ ├── layout/ │ │ ├── app_item.xml │ │ ├── app_list_fragment.xml │ │ └── main_fragment.xml │ ├── menu/ │ │ ├── main.xml │ │ └── navigation.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ ├── values-night/ │ │ └── colors.xml │ ├── values-v23/ │ │ └── themes.xml │ ├── values-v27/ │ │ └── themes.xml │ └── values-v29/ │ └── themes.xml └── settings.gradle
SYMBOL INDEX (138 symbols across 27 files)
FILE: appiconloader-coil/src/main/java/me/zhanghai/android/appiconloader/coil/AppIconFetcher.java
class AppIconFetcher (line 37) | public class AppIconFetcher implements Fetcher {
method AppIconFetcher (line 45) | public AppIconFetcher(@NonNull Options options, @NonNull AppIconLoader...
method fetch (line 52) | @Nullable
class Factory (line 60) | public static class Factory implements Fetcher.Factory<PackageInfo> {
method Factory (line 64) | public Factory(@Px int iconSize, boolean shrinkNonAdaptiveIcons, @No...
method create (line 69) | @Nullable
FILE: appiconloader-coil/src/main/java/me/zhanghai/android/appiconloader/coil/AppIconKeyer.java
class AppIconKeyer (line 11) | public class AppIconKeyer implements Keyer<PackageInfo> {
method key (line 12) | @Nullable
FILE: appiconloader-glide/src/main/java/me/zhanghai/android/appiconloader/glide/AppIconModelLoader.java
class AppIconModelLoader (line 38) | public class AppIconModelLoader implements ModelLoader<PackageInfo, Bitm...
method AppIconModelLoader (line 44) | private AppIconModelLoader(@Px int iconSize, boolean shrinkNonAdaptive...
method handles (line 50) | @Override
method buildLoadData (line 55) | @Nullable
class Fetcher (line 63) | private static class Fetcher implements DataFetcher<Bitmap> {
method Fetcher (line 69) | public Fetcher(@NonNull AppIconLoader loader, @NonNull ApplicationIn...
method loadData (line 74) | @Override
method cleanup (line 85) | @Override
method cancel (line 88) | @Override
method getDataClass (line 91) | @NonNull
method getDataSource (line 97) | @NonNull
class Factory (line 104) | public static class Factory implements ModelLoaderFactory<PackageInfo,...
method Factory (line 111) | public Factory(@Px int iconSize, boolean shrinkNonAdaptiveIcons, @No...
method build (line 117) | @NonNull
method teardown (line 124) | @Override
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/AppIconLoader.java
class AppIconLoader (line 34) | public class AppIconLoader {
method AppIconLoader (line 45) | public AppIconLoader(@Px int iconSize, boolean shrinkNonAdaptiveIcons,
method getIconKey (line 52) | @NonNull
method getIconKey (line 60) | @NonNull
method loadIcon (line 66) | @NonNull
method loadIcon (line 83) | @NonNull
class IconFactory (line 88) | private static class IconFactory extends BaseIconFactory {
method IconFactory (line 91) | public IconFactory(@Px int iconBitmapSize, @NonNull Context context) {
method createBadgedIconBitmap (line 98) | @NonNull
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/PackageInfoCompat.java
class PackageInfoCompat (line 24) | class PackageInfoCompat {
method PackageInfoCompat (line 25) | private PackageInfoCompat() {}
method getLongVersionCode (line 27) | public static long getLongVersionCode(@NonNull PackageInfo info) {
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/PackageItemInfoCompat.java
class PackageItemInfoCompat (line 29) | class PackageItemInfoCompat {
method PackageItemInfoCompat (line 30) | private PackageItemInfoCompat() {}
method loadUnbadgedIcon (line 32) | public static Drawable loadUnbadgedIcon(@NonNull PackageItemInfo packa...
method getApplicationInfo (line 53) | @Nullable
method loadDefaultIcon (line 64) | @NonNull
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/UserHandleCompat.java
class UserHandleCompat (line 28) | class UserHandleCompat {
method UserHandleCompat (line 38) | private UserHandleCompat() {}
method getUserHandleForUid (line 40) | @NonNull
method getUserId (line 56) | private static int getUserId(int uid) {
method getConstructor (line 64) | @NonNull
FILE: appiconloader/src/main/java/me/zhanghai/android/appiconloader/UserSerialNumberCache.java
class UserSerialNumberCache (line 26) | class UserSerialNumberCache {
method UserSerialNumberCache (line 32) | UserSerialNumberCache() {}
method getSerialNumber (line 34) | public static long getSerialNumber(@NonNull UserHandle user, @NonNull ...
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppGlideModule.java
class AppGlideModule (line 30) | @GlideModule
method isManifestParsingEnabled (line 32) | @Override
method registerComponents (line 37) | @Override
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListAdapter.java
class AppListAdapter (line 36) | public class AppListAdapter extends RecyclerView.Adapter<AppListAdapter....
method AppListAdapter (line 44) | public AppListAdapter(@NonNull IconLoader iconLoader) {
method replace (line 50) | public void replace(@NonNull List<Pair<PackageInfo, String>> apps) {
method getItemCount (line 56) | @Override
method getItem (line 61) | @NonNull
method getItemId (line 66) | @Override
method onCreateViewHolder (line 73) | @NonNull
method onBindViewHolder (line 80) | @Override
method getPopupText (line 95) | @NonNull
type IconLoader (line 102) | public interface IconLoader {
method loadIcon (line 103) | void loadIcon(@NonNull ImageView imageView, @NonNull PackageInfo pac...
class ViewHolder (line 106) | static class ViewHolder extends RecyclerView.ViewHolder {
method ViewHolder (line 109) | public ViewHolder(@NonNull AppItemBinding binding) {
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListFragment.java
class AppListFragment (line 33) | public abstract class AppListFragment extends Fragment {
method onCreateView (line 40) | @Nullable
method onActivityCreated (line 48) | @Override
method onCreateIconLoader (line 66) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListLiveData.java
class AppListLiveData (line 29) | public class AppListLiveData extends LiveData<List<Pair<PackageInfo, Str...
method AppListLiveData (line 32) | public AppListLiveData(@NonNull Context context) {
method loadValue (line 38) | private void loadValue() {
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListLoader.java
class AppListLoader (line 40) | public class AppListLoader {
method AppListLoader (line 41) | private AppListLoader() {}
method loadAppList (line 43) | @NonNull
method startAppDetailsActivity (line 113) | public static void startAppDetailsActivity(@NonNull PackageInfo packag...
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListViewModel.java
class AppListViewModel (line 29) | public class AppListViewModel extends AndroidViewModel {
method AppListViewModel (line 33) | public AppListViewModel(@NonNull Application application) {
method getAppListLiveData (line 39) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoilAppListFragment.java
class CoilAppListFragment (line 28) | public class CoilAppListFragment extends AppListFragment {
method newInstance (line 29) | @NonNull
method onActivityCreated (line 34) | @Override
method onCreateIconLoader (line 41) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoilInitializer.java
class CoilInitializer (line 29) | public class CoilInitializer {
method CoilInitializer (line 34) | private CoilInitializer() {}
method initializeCoil (line 36) | public static void initializeCoil(@NonNull Context context) {
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoordinatorScrollingFrameLayout.java
class CoordinatorScrollingFrameLayout (line 36) | public class CoordinatorScrollingFrameLayout extends FrameLayout
method CoordinatorScrollingFrameLayout (line 38) | public CoordinatorScrollingFrameLayout(@NonNull Context context) {
method CoordinatorScrollingFrameLayout (line 42) | public CoordinatorScrollingFrameLayout(@NonNull Context context, @Null...
method CoordinatorScrollingFrameLayout (line 46) | public CoordinatorScrollingFrameLayout(@NonNull Context context, @Null...
method CoordinatorScrollingFrameLayout (line 51) | public CoordinatorScrollingFrameLayout(@NonNull Context context, @Null...
method dispatchApplyWindowInsets (line 56) | @NonNull
method onApplyWindowInsets (line 68) | @NonNull
method getBehavior (line 74) | @NonNull
class Behavior (line 80) | private static class Behavior extends AppBarLayout.ScrollingViewBehavi...
method onMeasureChild (line 81) | @Override
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/FastScrollerLiftOnScrollHack.java
class FastScrollerLiftOnScrollHack (line 24) | public class FastScrollerLiftOnScrollHack {
method FastScrollerLiftOnScrollHack (line 25) | private FastScrollerLiftOnScrollHack() {}
method hack (line 27) | public static void hack(@NonNull AppBarLayout appBarLayout, int liftOn...
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/GlideAppListFragment.java
class GlideAppListFragment (line 21) | public class GlideAppListFragment extends AppListFragment {
method newInstance (line 22) | @NonNull
method onCreateIconLoader (line 27) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/LauncherAppsCompat.java
class LauncherAppsCompat (line 30) | public class LauncherAppsCompat {
method LauncherAppsCompat (line 31) | private LauncherAppsCompat() {}
method getProfiles (line 33) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/MainActivity.java
class MainActivity (line 24) | public class MainActivity extends AppCompatActivity {
method onCreate (line 28) | @Override
method onBackPressed (line 46) | @Override
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/MainFragment.java
class MainFragment (line 40) | public class MainFragment extends Fragment {
method newInstance (line 45) | @NonNull
method onCreate (line 50) | @Override
method onCreateView (line 57) | @Nullable
method onActivityCreated (line 65) | @Override
method onCreateOptionsMenu (line 87) | @Override
method onOptionsItemSelected (line 92) | @Override
method viewOnGitHub (line 106) | private void viewOnGitHub() {
method onNavigationItemSelected (line 115) | private boolean onNavigationItemSelected(@NonNull MenuItem item) {
method setNavigationCheckedItem (line 129) | private void setNavigationCheckedItem(@IdRes int itemId) {
method onBackPressed (line 154) | public boolean onBackPressed() {
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/NewDispatchApplyWindowInsetsFrameLayout.java
class NewDispatchApplyWindowInsetsFrameLayout (line 29) | public class NewDispatchApplyWindowInsetsFrameLayout extends FrameLayout {
method NewDispatchApplyWindowInsetsFrameLayout (line 30) | public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context contex...
method NewDispatchApplyWindowInsetsFrameLayout (line 34) | public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context,
method NewDispatchApplyWindowInsetsFrameLayout (line 39) | public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context,
method NewDispatchApplyWindowInsetsFrameLayout (line 45) | public NewDispatchApplyWindowInsetsFrameLayout(@NonNull Context context,
method dispatchApplyWindowInsets (line 52) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/PackageManagerCompat.java
class PackageManagerCompat (line 22) | public class PackageManagerCompat {
method PackageManagerCompat (line 23) | private PackageManagerCompat() {}
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/ParcelableCloner.java
class ParcelableCloner (line 24) | public class ParcelableCloner {
method ParcelableCloner (line 25) | private ParcelableCloner() {}
method cloneParcelable (line 27) | public static <T extends Parcelable> T cloneParcelable(T parcelable,
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/SynchronousAppListFragment.java
class SynchronousAppListFragment (line 26) | public class SynchronousAppListFragment extends AppListFragment {
method newInstance (line 29) | @NonNull
method onActivityCreated (line 34) | @Override
method onCreateIconLoader (line 43) | @NonNull
FILE: sample/src/main/java/me/zhanghai/android/appiconloader/sample/UserHandleCompat.java
class UserHandleCompat (line 28) | public class UserHandleCompat {
method UserHandleCompat (line 38) | private UserHandleCompat() {}
method getUserHandleForUid (line 40) | @NonNull
method getUserId (line 56) | private static int getUserId(int uid) {
method getConstructor (line 64) | @NonNull
Condensed preview — 96 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (152K chars).
[
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 130,
"preview": "## Expected Behavior\n\n\n## Actual Behavior\n\n\n## Steps to Reproduce the Problem\n\n1.\n1.\n1.\n\n## Specifications\n\n- Version:\n-"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 163,
"preview": "Fixes #<issue_number_goes_here>\n\n> It's a good idea to open an issue first for discussion.\n\n- [ ] Tests pass\n- [ ] Appro"
},
{
"path": ".github/workflows/android.yml",
"chars": 398,
"preview": "name: Android CI\n\non: [push]\n\njobs:\n build:\n\n runs-on: ubuntu-latest\n\n steps:\n - name: Check out repository\n "
},
{
"path": ".gitignore",
"chars": 71,
"preview": "/.gradle/\n/.idea/\n/build/\n/captures/\n/local.properties\n.DS_Store\n*.iml\n"
},
{
"path": ".gitmodules",
"chars": 170,
"preview": "[submodule \"appiconloader-iconloaderlib/systemui\"]\n\tpath = appiconloader-iconloaderlib/systemui\n\turl = https://android.g"
},
{
"path": "LICENSE",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "PRIVACY.md",
"chars": 382,
"preview": "# Privacy Policy\n\nHai Zhang built the AppIconLoader Sample app as an Open Source app. This SERVICE is provided by Hai Zh"
},
{
"path": "README.md",
"chars": 5027,
"preview": "# AppIconLoader\n\n[](http"
},
{
"path": "appiconloader/.gitignore",
"chars": 37,
"preview": "/.externalNativeBuild/\n/build/\n/out/\n"
},
{
"path": "appiconloader/build.gradle",
"chars": 714,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 33\n buildToolsVersion '33.0.0'\n defaultConfig"
},
{
"path": "appiconloader/gradle.properties",
"chars": 23,
"preview": "POM_NAME=AppIconLoader\n"
},
{
"path": "appiconloader/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "appiconloader/src/main/AndroidManifest.xml",
"chars": 710,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "appiconloader/src/main/java/me/zhanghai/android/appiconloader/AppIconLoader.java",
"chars": 4031,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader/src/main/java/me/zhanghai/android/appiconloader/PackageInfoCompat.java",
"chars": 1075,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader/src/main/java/me/zhanghai/android/appiconloader/PackageItemInfoCompat.java",
"chars": 2721,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader/src/main/java/me/zhanghai/android/appiconloader/UserHandleCompat.java",
"chars": 2444,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader/src/main/java/me/zhanghai/android/appiconloader/UserSerialNumberCache.java",
"chars": 1812,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader-coil/.gitignore",
"chars": 37,
"preview": "/.externalNativeBuild/\n/build/\n/out/\n"
},
{
"path": "appiconloader-coil/build.gradle",
"chars": 748,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 33\n buildToolsVersion '33.0.0'\n defaultConfig"
},
{
"path": "appiconloader-coil/gradle.properties",
"chars": 28,
"preview": "POM_NAME=AppIconLoader Coil\n"
},
{
"path": "appiconloader-coil/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "appiconloader-coil/src/main/AndroidManifest.xml",
"chars": 715,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "appiconloader-coil/src/main/java/me/zhanghai/android/appiconloader/coil/AppIconFetcher.java",
"chars": 2697,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader-coil/src/main/java/me/zhanghai/android/appiconloader/coil/AppIconKeyer.java",
"chars": 525,
"preview": "package me.zhanghai.android.appiconloader.coil;\n\nimport android.content.pm.PackageInfo;\n\nimport androidx.annotation.NonN"
},
{
"path": "appiconloader-glide/.gitignore",
"chars": 37,
"preview": "/.externalNativeBuild/\n/build/\n/out/\n"
},
{
"path": "appiconloader-glide/build.gradle",
"chars": 760,
"preview": "apply plugin: 'com.android.library'\n\nandroid {\n compileSdkVersion 33\n buildToolsVersion '33.0.0'\n defaultConfig"
},
{
"path": "appiconloader-glide/gradle.properties",
"chars": 29,
"preview": "POM_NAME=AppIconLoader Glide\n"
},
{
"path": "appiconloader-glide/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "appiconloader-glide/src/main/AndroidManifest.xml",
"chars": 716,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "appiconloader-glide/src/main/java/me/zhanghai/android/appiconloader/glide/AppIconModelLoader.java",
"chars": 4185,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "appiconloader-iconloaderlib/.gitignore",
"chars": 37,
"preview": "/.externalNativeBuild/\n/build/\n/out/\n"
},
{
"path": "appiconloader-iconloaderlib/build.gradle",
"chars": 704,
"preview": "apply plugin: 'com.android.library'\n\napply from: 'generate-iconloaderlib-src.gradle'\n\nandroid {\n compileSdkVersion 33"
},
{
"path": "appiconloader-iconloaderlib/generate-iconloaderlib-src.gradle",
"chars": 744,
"preview": "def aospIconloaderlibSrcDir = file('systemui/iconloaderlib')\ndef generatedIconloaderlibSrcDir = file(\"$buildDir/generate"
},
{
"path": "appiconloader-iconloaderlib/generate-iconloaderlib-src.sh",
"chars": 2165,
"preview": "#!/bin/bash\n\n# Copyright 2020 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may n"
},
{
"path": "appiconloader-iconloaderlib/gradle.properties",
"chars": 37,
"preview": "POM_NAME=AppIconLoader iconloaderlib\n"
},
{
"path": "appiconloader-iconloaderlib/iconloaderlib-classes.patch",
"chars": 11046,
"preview": "--- BaseIconFactory.java\n+++ BaseIconFactory.java\n@@ -25,6 +25,7 @@ import android.os.Build;\n import android.os.Process;"
},
{
"path": "appiconloader-iconloaderlib/iconloaderlib-resources.patch",
"chars": 314,
"preview": "--- values/colors.xml\n+++ values/colors.xml\n@@ -18,7 +18,4 @@\n -->\n <resources>\n <color name=\"legacy_icon_background"
},
{
"path": "appiconloader-iconloaderlib/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "appiconloader-iconloaderlib/src/main/AndroidManifest.xml",
"chars": 724,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "build.gradle",
"chars": 377,
"preview": "buildscript {\n repositories {\n google()\n mavenCentral()\n }\n dependencies {\n classpath 'com"
},
{
"path": "docs/contributing.md",
"chars": 1097,
"preview": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guid"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/1.txt",
"chars": 19,
"preview": "- Initial release.\n"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/2.txt",
"chars": 200,
"preview": "- Fixed crash when using shrinkNonAdaptiveIcons due to package name mismatch.\n- Added shrinkNonAdaptiveIcons as a parame"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/3.txt",
"chars": 113,
"preview": "- Enabled shape detection by default.\n- Moved the shrinkNonAdaptiveIcons parameter to AppIconLoader constructor.\n"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/4.txt",
"chars": 51,
"preview": "- Updated iconloaderlib to Android 11 source code.\n"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/5.txt",
"chars": 149,
"preview": "- Fixed MIUI \"optimization\" that draws an extra shadow.\n- Fixed the sample app to show all apps on Android 11.\n- Publish"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/6.txt",
"chars": 113,
"preview": "- Synchronized iconloaderlib code from Android 12L.\n- Patched iconloaderlib to fix MIUI \"optimization\" reliably.\n"
},
{
"path": "fastlane/metadata/android/en-US/changelogs/7.txt",
"chars": 41,
"preview": "- Migrated appiconloader-coil to Coil 2.\n"
},
{
"path": "fastlane/metadata/android/en-US/full_description.txt",
"chars": 42,
"preview": "https://github.com/zhanghai/AppIconLoader\n"
},
{
"path": "fastlane/metadata/android/en-US/short_description.txt",
"chars": 36,
"preview": "App icon loader from AOSP Launcher3\n"
},
{
"path": "fastlane/metadata/android/en-US/title.txt",
"chars": 21,
"preview": "AppIconLoader Sample\n"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 232,
"preview": "#Thu Sep 23 01:27:10 PDT 2021\ndistributionBase=GRADLE_USER_HOME\ndistributionUrl=https\\://services.gradle.org/distributio"
},
{
"path": "gradle.properties",
"chars": 1426,
"preview": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will ov"
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2260,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@r"
},
{
"path": "sample/.gitignore",
"chars": 37,
"preview": "/.externalNativeBuild/\n/build/\n/out/\n"
},
{
"path": "sample/build.gradle",
"chars": 2040,
"preview": "apply plugin: 'com.android.application'\n\nandroid {\n compileSdkVersion 33\n buildToolsVersion '33.0.0'\n defaultCo"
},
{
"path": "sample/proguard-rules.pro",
"chars": 995,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "sample/src/main/AndroidManifest.xml",
"chars": 1630,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppGlideModule.java",
"chars": 1553,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListAdapter.java",
"chars": 3866,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListFragment.java",
"chars": 2548,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListLiveData.java",
"chars": 1356,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListLoader.java",
"chars": 5362,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/AppListViewModel.java",
"chars": 1306,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoilAppListFragment.java",
"chars": 1700,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoilInitializer.java",
"chars": 1937,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/CoordinatorScrollingFrameLayout.java",
"chars": 3983,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/FastScrollerLiftOnScrollHack.java",
"chars": 1755,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/GlideAppListFragment.java",
"chars": 1097,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/LauncherAppsCompat.java",
"chars": 1445,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/MainActivity.java",
"chars": 1642,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/MainFragment.java",
"chars": 5528,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/NewDispatchApplyWindowInsetsFrameLayout.java",
"chars": 2262,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/PackageManagerCompat.java",
"chars": 1035,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/ParcelableCloner.java",
"chars": 1246,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/SynchronousAppListFragment.java",
"chars": 1675,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/java/me/zhanghai/android/appiconloader/sample/UserHandleCompat.java",
"chars": 2458,
"preview": "/*\n * Copyright 2020 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use"
},
{
"path": "sample/src/main/res/drawable/info_icon_control_normal_24dp.xml",
"chars": 1116,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/drawable/menu_icon_control_normal_24dp.xml",
"chars": 1015,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/layout/app_item.xml",
"chars": 2226,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/layout/app_list_fragment.xml",
"chars": 1591,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/layout/main_fragment.xml",
"chars": 2657,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/menu/main.xml",
"chars": 1031,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/menu/navigation.xml",
"chars": 1086,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values/colors.xml",
"chars": 1225,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values/dimens.xml",
"chars": 723,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values/strings.xml",
"chars": 996,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values/themes.xml",
"chars": 1359,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values-night/colors.xml",
"chars": 914,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values-v23/themes.xml",
"chars": 992,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values-v27/themes.xml",
"chars": 997,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "sample/src/main/res/values-v29/themes.xml",
"chars": 921,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\n ~ Copyright 2020 Google LLC\n ~\n ~ Licensed under the Apache License, Ve"
},
{
"path": "settings.gradle",
"chars": 115,
"preview": "include ':appiconloader', ':appiconloader-coil', ':appiconloader-glide', ':appiconloader-iconloaderlib', ':sample'\n"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the zhanghai/AppIconLoader GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 96 files (136.1 KB), approximately 34.0k tokens, and a symbol index with 138 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.