Full Code of zhanghai/AppIconLoader for AI

master f7e2b2cbb7c2 cached
96 files
136.1 KB
34.0k tokens
138 symbols
1 requests
Download .txt
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

[![Android CI status](https://github.com/zhanghai/AppIconLoader/workflows/Android%20CI/badge.svg)](https://github.com/zhanghai/AppIconLoader/actions) [![GitHub release](https://img.shields.io/github/v/release/zhanghai/AppIconLoader)](https://github.com/zhanghai/AppIconLoader/releases) [![License](https://img.shields.io/github/license/zhanghai/AppIconLoader?color=blue)](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'
Download .txt
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
Download .txt
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[![Android CI status](https://github.com/zhanghai/AppIconLoader/workflows/Android%20CI/badge.svg)](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.

Copied to clipboard!