Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d966de93 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 9593491 from 0c72cf7f to tm-qpr3-release

Change-Id: I3c121eb2cd3471e32ed9eedd6ce37e0287e9d8c8
parents f0fbef1c 0c72cf7f
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -212,7 +212,8 @@ public class BaseIconFactory implements AutoCloseable {
        boolean shrinkNonAdaptiveIcons = options == null || options.mShrinkNonAdaptiveIcons;
        float[] scale = new float[1];
        icon = normalizeAndWrapToAdaptiveIcon(icon, shrinkNonAdaptiveIcons, null, scale);
        Bitmap bitmap = createIconBitmap(icon, scale[0], MODE_WITH_SHADOW);
        Bitmap bitmap = createIconBitmap(icon, scale[0],
                options == null ? MODE_WITH_SHADOW : options.mGenerationMode);

        int color = (options != null && options.mExtractedColor != null)
                ? options.mExtractedColor : mColorExtractor.findDominantColorByHue(bitmap);
@@ -463,6 +464,9 @@ public class BaseIconFactory implements AutoCloseable {

        boolean mIsInstantApp;

        @BitmapGenerationMode
        int mGenerationMode = MODE_WITH_SHADOW;

        @Nullable UserHandle mUserHandle;

        @ColorInt
@@ -503,6 +507,16 @@ public class BaseIconFactory implements AutoCloseable {
            mExtractedColor = color;
            return this;
        }

        /**
         * Sets the bitmap generation mode to use for the bitmap info. Note that some generation
         * modes do not support color extraction, so consider setting a extracted color manually
         * in those cases.
         */
        public IconOptions setBitmapGenerationMode(@BitmapGenerationMode int generationMode) {
            mGenerationMode = generationMode;
            return this;
        }
    }

    /**
+19 −1
Original line number Diff line number Diff line
@@ -16,9 +16,11 @@
package com.android.launcher3.icons;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
@@ -28,6 +30,8 @@ import android.graphics.drawable.ColorDrawable;
import android.util.Log;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.core.graphics.PathParser;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -35,6 +39,7 @@ import java.io.IOException;
public class GraphicsUtils {

    private static final String TAG = "GraphicsUtils";
    private static final float MASK_SIZE = 100f;

    public static Runnable sOnNewBitmapRunnable = () -> { };

@@ -98,7 +103,20 @@ public class GraphicsUtils {
    /**
     * Returns the default path to be used by an icon
     */
    public static Path getShapePath(int size) {
    public static Path getShapePath(@NonNull Context context, int size) {
        if (IconProvider.CONFIG_ICON_MASK_RES_ID != Resources.ID_NULL) {
            Path path = PathParser.createPathFromPathData(
                    context.getString(IconProvider.CONFIG_ICON_MASK_RES_ID));
            if (path != null) {
                if (size != MASK_SIZE) {
                    Matrix m = new Matrix();
                    float scale = ((float) size) / MASK_SIZE;
                    m.setScale(scale, scale);
                    path.transform(m);
                }
                return path;
            }
        }
        AdaptiveIconDrawable drawable = new AdaptiveIconDrawable(
                new ColorDrawable(Color.BLACK), new ColorDrawable(Color.BLACK));
        drawable.setBounds(0, 0, size, size);
+1 −1
Original line number Diff line number Diff line
@@ -61,7 +61,7 @@ import java.util.function.Supplier;
public class IconProvider {

    private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
    private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
    static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
            "config_icon_mask", "string", "android");

    private static final String TAG = "IconProvider";
+1 −1
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ public class PlaceHolderIconDrawable extends FastBitmapDrawable {
    public PlaceHolderIconDrawable(BitmapInfo info, Context context) {
        super(info);

        mProgressPath = GraphicsUtils.getShapePath(100);
        mProgressPath = GraphicsUtils.getShapePath(context, 100);
        mPaint.setColor(ColorUtils.compositeColors(
                GraphicsUtils.getAttrColor(context, R.attr.loadingIconColor), info.color));
    }
+0 −193
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * 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.
 */

package com.android.app.search;

import static com.android.app.search.LayoutType.EMPTY_DIVIDER;
import static com.android.app.search.LayoutType.ICON_HORIZONTAL_TEXT;
import static com.android.app.search.LayoutType.ICON_SINGLE_VERTICAL_TEXT;
import static com.android.app.search.LayoutType.TEXT_HEADER_ROW;
import static com.android.app.search.ResultType.APPLICATION;
import static com.android.app.search.ResultType.NO_FULFILLMENT;
import static com.android.app.search.ResultType.SUGGEST;
import static com.android.app.search.SearchTargetExtras.BUNDLE_EXTRA_PROXY_WEB_ITEM;
import static com.android.app.search.SearchTargetExtras.BUNDLE_EXTRA_QUICK_LAUNCH;
import static com.android.app.search.SearchTargetExtras.getDecoratorId;
import static com.android.app.search.SearchTargetExtras.isAnswer;
import static com.android.app.search.SearchTargetExtras.isRichAnswer;
import static com.android.app.search.SearchTargetGenerator.EMPTY_DIVIDER_TARGET;
import static com.android.app.search.SearchTargetGenerator.SECTION_HEADER_TARGET;

import android.app.search.SearchTarget;

import androidx.annotation.NonNull;

import java.util.ArrayList;
import java.util.List;

public class SearchTargetProcessor {

    /**
     * Handles light merging of the web targets and the on device targets.
     *
     * @param webTargets list of results that arrive for the web blocks
     * @param deviceTargets list of results that arrive for on device blocks
     * @param isForQsb {@code true} when merge logic should be applied to home searchbox
     * @param qsbWebCount count of web results if QSB entry
     * @param allappsWebCnt web result count when there are on device result, on all apps
     * @param useFallbackAppSearch {@code true} if app corpus should be generated
     * @return merged {@link SearchTarget} array list
     */
    public static ArrayList<SearchTarget> injectSuggestResultsMiddle(
            @NonNull ArrayList<SearchTarget> webTargets,
            @NonNull ArrayList<SearchTarget> deviceTargets,
            boolean isForQsb,
            int qsbWebCount,
            int allappsWebCnt,
            boolean useFallbackAppSearch) {

        ArrayList<SearchTarget> tmpTargets = new ArrayList<>();
        // Identify insertion index, here we insert immediately after last app row
        // OR last app block.
        int insertionIdx = 0;
        int placeholderIdx = -1;
        int richAnswerPlaceholderIdx = -1;
        String lastAppBlockTargetId = "";
        boolean hasTextHeader = false;

        if (webTargets.size() > 0) {
            webTargets.get(0).getExtras().putBoolean(BUNDLE_EXTRA_PROXY_WEB_ITEM, true);
        }

        for (int i = 0; i < deviceTargets.size(); i++) {
            if (deviceTargets.get(i).getLayoutType().equals(TEXT_HEADER_ROW)) {
                hasTextHeader = true;
            }
            // Rich answer should always be above web block, so check and remove the rich answer
            // placeholder first.
            if (deviceTargets.get(i).getLayoutType().equals("richanswer_placeholder")) {
                richAnswerPlaceholderIdx = i;
                removePlaceholder(deviceTargets, richAnswerPlaceholderIdx);
            }
            if (deviceTargets.size() > i && deviceTargets.get(i).getLayoutType().equals(
                    "placeholder")) {
                insertionIdx = placeholderIdx = i;
                removePlaceholder(deviceTargets, placeholderIdx);
            }
        }

        // If device target exists and if there's any app block, we need to find the insertion point
        if (placeholderIdx < 0 && deviceTargets.size() > 0
                && deviceTargets.get(0).getResultType() == APPLICATION) {
            // Scan and get last app corpus block index
            for (int i = 0; i < deviceTargets.size(); i++) {
                SearchTarget t = deviceTargets.get(i);
                if (lastAppBlockTargetId.equals(t.getParentId())
                        || lastAppBlockTargetId.equals(getDecoratorId(t))
                        || t.getLayoutType().equals(EMPTY_DIVIDER)) {
                    continue;
                }
                if (t.getResultType() == APPLICATION
                        && (t.getLayoutType().equals(ICON_HORIZONTAL_TEXT)
                        || t.getLayoutType().equals(ICON_SINGLE_VERTICAL_TEXT))) {
                    lastAppBlockTargetId = t.getId();
                    insertionIdx = i + 1;
                } else {
                    insertionIdx = i;
                    break;
                }
            }
        }

        if (useFallbackAppSearch && deviceTargets.size() > 0) {
            insertionIdx = deviceTargets.size() + 1;
            deviceTargets.get(0).getExtras().putBoolean(BUNDLE_EXTRA_QUICK_LAUNCH, true);
            deviceTargets.add(EMPTY_DIVIDER_TARGET);
        }

        SearchTarget divider = EMPTY_DIVIDER_TARGET;
        int fallbackCount = getFallbackAndDividerCount(deviceTargets);

        if (!hasTextHeader) {
            // Add a section header for on device results if there are targets other than apps,
            // dividers and fallback.
            if (!useFallbackAppSearch && deviceTargets.size() - insertionIdx > fallbackCount) {
                divider = SECTION_HEADER_TARGET;
            }
        }
        // Composit the device and web result / insert divider
        tmpTargets.addAll(deviceTargets);

        if (!webTargets.isEmpty() && isRichAnswer(webTargets.get(0))) {
            allappsWebCnt++;
        }
        boolean nonFallbackTargetsExists = deviceTargets.size() > fallbackCount;
        if (isForQsb) {
            tmpTargets.addAll(insertionIdx, webTargets.subList(0, qsbWebCount));
            tmpTargets.add(insertionIdx + qsbWebCount, divider);
        } else if (nonFallbackTargetsExists && webTargets.size() >= allappsWebCnt) {
            tmpTargets.addAll(insertionIdx, webTargets.subList(0, allappsWebCnt));
            tmpTargets.add(insertionIdx + allappsWebCnt, divider);
        } else {
            if (deviceTargets.size() > 0 && webTargets.size() > 0) {
                tmpTargets.add(insertionIdx, EMPTY_DIVIDER_TARGET); // divider before fallback
            }
            tmpTargets.addAll(insertionIdx, webTargets);
        }
        if (webTargets.size() > 1 && isAnswer(webTargets.get(0))) {
            SearchTarget topWebTarget = webTargets.get(0);
            if (richAnswerPlaceholderIdx == -1 || !isRichAnswer(topWebTarget)) {
                // When rich answer placeholder is not set, or there's no rich answer in the list,
                // keep answer on top of web list and put a divider below.
                tmpTargets.add(insertionIdx + 1, EMPTY_DIVIDER_TARGET);
            } else {
                // Remove the answer from top of target list, then add to the placeholder position.
                tmpTargets.remove(insertionIdx);
                tmpTargets.add(richAnswerPlaceholderIdx, topWebTarget);
                tmpTargets.add(richAnswerPlaceholderIdx + 1, EMPTY_DIVIDER_TARGET);
            }
        }
        return tmpTargets;
    }


    /**
     * Count the fallback and dividers targets.
     * - Fallback targets have resultType == SUGGEST
     * - Dividers have resultType == NO_FULFILLMENT
     */
    public static int getFallbackAndDividerCount(ArrayList<SearchTarget> deviceTargets) {
        int fallback = 0;
        for (int i = 0; i < deviceTargets.size(); i++) {
            int resultType = deviceTargets.get(i).getResultType();
            if (resultType == SUGGEST || resultType == NO_FULFILLMENT) {
                fallback++;
            }
        }
        return fallback;
    }

    private static void removePlaceholder(List<SearchTarget> deviceTargets, int placeholderIdx) {
        deviceTargets.remove(placeholderIdx); // remove placeholder
        if (deviceTargets.size() > placeholderIdx) {
            String layoutType = deviceTargets.get(placeholderIdx).getLayoutType();
            if (layoutType.equals(EMPTY_DIVIDER)) {
                deviceTargets.remove(placeholderIdx); // remove the subsequent divider
            }
        }
    }

}