All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.android.ide.common.build.SplitOutputMatcher Maven / Gradle / Ivy

There is a newer version: 25.3.0
Show newest version
/*
 * Copyright (C) 2014 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.ide.common.build;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.build.FilterData;
import com.android.build.OutputFile;
import com.android.build.VariantOutput;
import com.android.resources.Density;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Helper class to help with installation of multi-output variants.
 */
public class SplitOutputMatcher {

    /**
     * Determines and return the list of APKs to use based on given device density and abis.
     *
     * This uses the same logic as the store, using two passes:
     * First, find all the compatible outputs.
     * Then take the one with the highest versionCode.
     *
     * @param outputs the outputs to choose from.
     * @param variantAbiFilters a list of abi filters applied to the variant. This is used in place
     *                          of the outputs, if there is a single output with no abi filters.
     *                          If the list is null, then the variant does not restrict ABI
     *                          packaging.
     * @param deviceDensity the density of the device.
     * @param deviceAbis a list of ABIs supported by the device.
     * @return the list of APKs to install or null if none are compatible.
     */
    @NonNull
    public static List computeBestOutput(
            @NonNull List outputs,
            @Nullable Set variantAbiFilters,
            int deviceDensity,
            @NonNull List deviceAbis) {
        Density densityEnum = Density.getEnum(deviceDensity);

        String densityValue;
        if (densityEnum == null) {
            densityValue = null;
        } else {
            densityValue = densityEnum.getResourceValue();
        }

        // gather all compatible matches.
        Set matches = new HashSet();

        // find a matching output.
        for (VariantOutput variantOutput : outputs) {
            for (OutputFile output : variantOutput.getOutputs()) {
                String densityFilter = getFilter(output, OutputFile.DENSITY);
                String abiFilter = getFilter(output, OutputFile.ABI);

                if (densityFilter != null && !densityFilter.equals(densityValue)) {
                    continue;
                }

                if (abiFilter != null && !deviceAbis.contains(abiFilter)) {
                    continue;
                }
                // variantOutput can be added several times to matches.
                matches.add(variantOutput);
            }
        }

        if (matches.isEmpty()) {
            return ImmutableList.of();
        }

        VariantOutput match = Collections.max(matches, new Comparator() {
            @Override
            public int compare(VariantOutput splitOutput, VariantOutput splitOutput2) {
                return splitOutput.getVersionCode() - splitOutput2.getVersionCode();
            }
        });

        OutputFile mainOutputFile = match.getMainOutputFile();
        if (match.getOutputs().size() == 1) {
            return isMainApkCompatibleWithDevice(mainOutputFile, variantAbiFilters, deviceAbis)
                    ? ImmutableList.of(mainOutputFile)
                    : ImmutableList.of();
        } else {
            // we are dealing with pure splits.
            ImmutableList.Builder apks = ImmutableList.builder();
            apks.add(mainOutputFile);
            Optional abiCompatibleSplitApk = findAbiCompatibleSplitApk(match,
                    deviceAbis);
            if (abiCompatibleSplitApk.isPresent()) {
                apks.add(abiCompatibleSplitApk.get());
            }
            Optional densityCompatibleSplitApk = findDensityCompatibleSplitApk(match,
                    densityValue);
            if (densityCompatibleSplitApk.isPresent()) {
                apks.add(densityCompatibleSplitApk.get());
            }
            return apks.build();
        }
    }

    private static boolean isMainApkCompatibleWithDevice(
            OutputFile mainOutputFile,
            Set variantAbiFilters,
            List deviceAbis) {
        // so far, we are not dealing with the pure split files...
        if (getFilter(mainOutputFile, OutputFile.ABI) == null && variantAbiFilters != null) {
            // if we have a match that has no abi filter, and we have variant-level filters, then
            // we need to make sure that the variant filters are compatible with the device abis.
            for (String abi : deviceAbis) {
                if (variantAbiFilters.contains(abi)) {
                    return true;
                }
            }
            return false;
        }
        return true;
    }

    private static Optional findAbiCompatibleSplitApk(
            VariantOutput variantOutput,
            List deviceAbis) {

        for (String deviceAbi : deviceAbis) {
            for (OutputFile outputFile : variantOutput.getOutputs()) {
                if (outputFile.getOutputType().equals(OutputFile.SPLIT)
                        && deviceAbi.equals(getFilter(outputFile, OutputFile.ABI))) {
                    return Optional.of(outputFile);
                }
            }
        }
        return Optional.absent();
    }

    private static Optional findDensityCompatibleSplitApk(
            VariantOutput variantOutput,
            String densityValue) {
        for (OutputFile outputFile : variantOutput.getOutputs()) {
            if (outputFile.getOutputType().equals(OutputFile.SPLIT)
                    && densityValue.equals(getFilter(outputFile, OutputFile.DENSITY))) {
                return Optional.of(outputFile);
            }
        }
        return Optional.absent();
    }

    @Nullable
    private static String getFilter(@NonNull OutputFile outputFile, @NonNull String filterType) {
        for (FilterData filterData : outputFile.getFilters()) {
            if (filterData.getFilterType().equals(filterType)) {
                return filterData.getIdentifier();
            }
        }
        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy