1b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael/*
2b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Copyright (C) 2013 The Android Open Source Project
3b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael *
4b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Licensed under the Apache License, Version 2.0 (the "License");
5b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * you may not use this file except in compliance with the License.
6b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * You may obtain a copy of the License at
7b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael *
8b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael *      http://www.apache.org/licenses/LICENSE-2.0
9b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael *
10b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Unless required by applicable law or agreed to in writing, software
11b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * distributed under the License is distributed on an "AS IS" BASIS,
12b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * See the License for the specific language governing permissions and
14b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * limitations under the License.
15b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */
16b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
17b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelpackage com.android.camera.settings;
18b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
19fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Huimport android.content.Context;
20fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Huimport android.util.DisplayMetrics;
21fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Huimport android.view.WindowManager;
22fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu
238be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohdeimport com.android.camera.exif.Rational;
248be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohdeimport com.android.camera.util.AndroidServices;
258be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohdeimport com.android.camera.util.ApiHelper;
268be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohdeimport com.android.camera.util.Size;
278d8b329da25456adb5ee45e0450680654114e125I-Jong Lin
2886d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberlingimport com.google.common.collect.Lists;
298be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohde
30b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.math.BigInteger;
31b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.ArrayList;
32b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.Arrays;
33b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.Collections;
34b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.Comparator;
35b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.HashMap;
3686d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberlingimport java.util.HashSet;
37b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.LinkedList;
38b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.List;
3986d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberlingimport java.util.Set;
4086d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling
411d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohdeimport javax.annotation.Nonnull;
4286d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberlingimport javax.annotation.ParametersAreNonnullByDefault;
4386d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling
44b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
45b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael/**
46b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * This class is used to help manage the many different resolutions available on
47b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * the device. <br/>
48b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * It allows you to specify which aspect ratios to offer the user, and then
49b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * chooses which resolutions are the most pertinent to avoid overloading the
50b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * user with so many options.
51b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */
52b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelpublic class ResolutionUtil {
532272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    /**
542272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * Different aspect ratio constants.
552272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     */
562272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    public static final Rational ASPECT_RATIO_16x9 = new Rational(16, 9);
572272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    public static final Rational ASPECT_RATIO_4x3 = new Rational(4, 3);
582272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    private static final double ASPECT_RATIO_TOLERANCE = 0.05;
59b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
60454d63f617489281971fb151abfe14730209c047Angus Kong    public static final String NEXUS_5_LARGE_16_BY_9 = "1836x3264";
61454d63f617489281971fb151abfe14730209c047Angus Kong    public static final float NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO = 16f / 9f;
622f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall    public static Size NEXUS_5_LARGE_16_BY_9_SIZE = new Size(3264, 1836);
63454d63f617489281971fb151abfe14730209c047Angus Kong
64b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
65b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * These are the preferred aspect ratios for the settings. We will take HAL
662272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * supported aspect ratios that are within ASPECT_RATIO_TOLERANCE of these values.
67b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * We will also take the maximum supported resolution for full sensor image.
68b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
69b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    private static Float[] sDesiredAspectRatios = {
70b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            16.0f / 9.0f, 4.0f / 3.0f
71b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    };
72b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
731cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    private static Size[] sDesiredAspectRatioSizes = {
741cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael            new Size(16, 9), new Size(4, 3)
751cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    };
761cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael
77b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
78b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * A resolution bucket holds a list of sizes that are of a given aspect
79b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * ratio.
80b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
81b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    private static class ResolutionBucket {
82b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        public Float aspectRatio;
83b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        /**
84b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * This is a sorted list of sizes, going from largest to smallest.
85b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         */
86b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        public List<Size> sizes = new LinkedList<Size>();
87b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        /**
88b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * This is the head of the sizes array.
89b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         */
90b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        public Size largest;
91b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        /**
92b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * This is the area of the largest size, used for sorting
93b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * ResolutionBuckets.
94b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         */
95b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        public Integer maxPixels = 0;
96b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
97b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        /**
98b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * Use this to add a new resolution to this bucket. It will insert it
99b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * into the sizes array and update appropriate members.
10008b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling         *
101b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         * @param size the new size to be added
102b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael         */
103b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        public void add(Size size) {
104b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            sizes.add(size);
105b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            Collections.sort(sizes, new Comparator<Size>() {
106b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                @Override
107b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                public int compare(Size size, Size size2) {
108b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                    // sort area greatest to least
109b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                    return Integer.compare(size2.width() * size2.height(),
110b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                            size.width() * size.height());
111b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                }
112b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            });
113b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            maxPixels = sizes.get(0).width() * sizes.get(0).height();
114b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
115b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
116b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
117b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
118b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * Given a list of camera sizes, this uses some heuristics to decide which
119b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * options to present to a user. It currently returns up to 3 sizes for each
120b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * aspect ratio. The aspect ratios returned include the ones in
121b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * sDesiredAspectRatios, and the largest full sensor ratio. T his guarantees
122b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * that users can use a full-sensor size, as well as any of the preferred
123b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * aspect ratios from above;
12408b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
125b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @param sizes A super set of all sizes to be displayed
126a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael     * @param isBackCamera true if these are sizes for the back camera
127b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @return The list of sizes to display grouped first by aspect ratio
128b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     *         (sorted by maximum area), and sorted within aspect ratio by area)
129b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
130a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael    public static List<Size> getDisplayableSizesFromSupported(List<Size> sizes, boolean isBackCamera) {
131a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael        List<ResolutionBucket> buckets = parseAvailableSizes(sizes, isBackCamera);
132b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
133b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        List<Float> sortedDesiredAspectRatios = new ArrayList<Float>();
134b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        // We want to make sure we support the maximum pixel aspect ratio, even
135b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        // if it doesn't match a desired aspect ratio
136b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        sortedDesiredAspectRatios.add(buckets.get(0).aspectRatio.floatValue());
137b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
138b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        // Now go through the buckets from largest mp to smallest, adding
139b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        // desired ratios
140b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        for (ResolutionBucket bucket : buckets) {
141b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            Float aspectRatio = bucket.aspectRatio;
142b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            if (Arrays.asList(sDesiredAspectRatios).contains(aspectRatio)
143b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                    && !sortedDesiredAspectRatios.contains(aspectRatio)) {
144b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                sortedDesiredAspectRatios.add(aspectRatio);
145b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
146b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
147b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
148b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        List<Size> result = new ArrayList<Size>(sizes.size());
149b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        for (Float targetRatio : sortedDesiredAspectRatios) {
150b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            for (ResolutionBucket bucket : buckets) {
151b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                Number aspectRatio = bucket.aspectRatio;
1522272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu                if (Math.abs(aspectRatio.floatValue() - targetRatio) <= ASPECT_RATIO_TOLERANCE) {
153b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                    result.addAll(pickUpToThree(bucket.sizes));
154b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                }
155b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
156b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
157b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        return result;
158b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
159b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
160b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
161b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * Get the area in pixels of a size.
16208b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
163b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @param size the size to measure
164b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @return the area.
165b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
166b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    private static int area(Size size) {
167b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        if (size == null) {
168b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            return 0;
169b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
170b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        return size.width() * size.height();
171b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
172b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
173b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
174b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * Given a list of sizes of a similar aspect ratio, it tries to pick evenly
175b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * spaced out options. It starts with the largest, then tries to find one at
176b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * 50% of the last chosen size for the subsequent size.
17708b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
178b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @param sizes A list of Sizes that are all of a similar aspect ratio
179b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @return A list of at least one, and no more than three representative
180b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     *         sizes from the list.
181b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
182b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    private static List<Size> pickUpToThree(List<Size> sizes) {
183b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        List<Size> result = new ArrayList<Size>();
184b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        Size largest = sizes.get(0);
185b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        result.add(largest);
186b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        Size lastSize = largest;
187b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        for (Size size : sizes) {
188b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            double targetArea = Math.pow(.5, result.size()) * area(largest);
189b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            if (area(size) < targetArea) {
190b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                // This candidate is smaller than half the mega pixels of the
191b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                // last one. Let's see whether the previous size, or this size
192b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                // is closer to the desired target.
193b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                if (!result.contains(lastSize)
194b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                        && (targetArea - area(lastSize) < area(size) - targetArea)) {
195b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                    result.add(lastSize);
196b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                } else {
197b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                    result.add(size);
198b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                }
199b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
200b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            lastSize = size;
201b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            if (result.size() == 3) {
202b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                break;
203b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
204b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
205b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
206b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        // If we have less than three, we can add the smallest size.
207b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        if (result.size() < 3 && !result.contains(lastSize)) {
208b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            result.add(lastSize);
209b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
210b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        return result;
211b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
212b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
213b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
214b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * Take an aspect ratio and squish it into a nearby desired aspect ratio, if
215b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * possible.
21608b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
217b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @param aspectRatio the aspect ratio to fuzz
2182272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * @return the closest desiredAspectRatio within ASPECT_RATIO_TOLERANCE, or the
219b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     *         original ratio
220b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
221b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    private static float fuzzAspectRatio(float aspectRatio) {
222b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        for (float desiredAspectRatio : sDesiredAspectRatios) {
2232272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            if ((Math.abs(aspectRatio - desiredAspectRatio)) < ASPECT_RATIO_TOLERANCE) {
224b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                return desiredAspectRatio;
225b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
226b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
227b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        return aspectRatio;
228b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
229b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
230b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
231b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * This takes a bunch of supported sizes and buckets them by aspect ratio.
232b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * The result is a list of buckets sorted by each bucket's largest area.
233b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * They are sorted from largest to smallest. This will bucket aspect ratios
234b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * that are close to the sDesiredAspectRatios in to the same bucket.
23508b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
236b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @param sizes all supported sizes for a camera
237a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael     * @param isBackCamera true if these are sizes for the back camera
238b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @return all of the sizes grouped by their closest aspect ratio
239b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
240a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael    private static List<ResolutionBucket> parseAvailableSizes(List<Size> sizes, boolean isBackCamera) {
241b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        HashMap<Float, ResolutionBucket> aspectRatioToBuckets = new HashMap<Float, ResolutionBucket>();
242b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
243b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        for (Size size : sizes) {
2442272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            Float aspectRatio = (float) size.getWidth() / (float) size.getHeight();
245b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            // If this aspect ratio is close to a desired Aspect Ratio,
246b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            // fuzz it so that they are bucketed together
247b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            aspectRatio = fuzzAspectRatio(aspectRatio);
248b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            ResolutionBucket bucket = aspectRatioToBuckets.get(aspectRatio);
249b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            if (bucket == null) {
250b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                bucket = new ResolutionBucket();
251b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                bucket.aspectRatio = aspectRatio;
252b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                aspectRatioToBuckets.put(aspectRatio, bucket);
253b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
254b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            bucket.add(size);
255b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        }
256a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael        if (ApiHelper.IS_NEXUS_5 && isBackCamera) {
257454d63f617489281971fb151abfe14730209c047Angus Kong            aspectRatioToBuckets.get(16 / 9.0f).add(NEXUS_5_LARGE_16_BY_9_SIZE);
258454d63f617489281971fb151abfe14730209c047Angus Kong        }
259b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        List<ResolutionBucket> sortedBuckets = new ArrayList<ResolutionBucket>(
260b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                aspectRatioToBuckets.values());
261b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        Collections.sort(sortedBuckets, new Comparator<ResolutionBucket>() {
262b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            @Override
263b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            public int compare(ResolutionBucket resolutionBucket, ResolutionBucket resolutionBucket2) {
264b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael                return Integer.compare(resolutionBucket2.maxPixels, resolutionBucket.maxPixels);
265b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael            }
266b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        });
267b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        return sortedBuckets;
268b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
269b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael
270b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    /**
271b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * Given a size, return a string describing the aspect ratio by reducing the
27208b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
273b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @param size the size to describe
274b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     * @return a string description of the aspect ratio
275b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael     */
276b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    public static String aspectRatioDescription(Size size) {
2771cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        Size aspectRatio = reduce(size);
2781cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        return aspectRatio.width() + "x" + aspectRatio.height();
2791cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    }
2801cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael
2811cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    /**
2821cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * Reduce an aspect ratio to its lowest common denominator. The ratio of the
2831cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * input and output sizes is guaranteed to be the same.
28408b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
2851cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * @param aspectRatio the aspect ratio to reduce
2861cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * @return The reduced aspect ratio which may equal the original.
2871cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     */
2881cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    public static Size reduce(Size aspectRatio) {
2891cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        BigInteger width = BigInteger.valueOf(aspectRatio.width());
2901cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        BigInteger height = BigInteger.valueOf(aspectRatio.height());
291b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        BigInteger gcd = width.gcd(height);
292b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        int numerator = Math.max(width.intValue(), height.intValue()) / gcd.intValue();
293b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael        int denominator = Math.min(width.intValue(), height.intValue()) / gcd.intValue();
2941cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        return new Size(numerator, denominator);
295b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael    }
296d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael
297d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael    /**
298d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     * Given a size return the numerator of its aspect ratio
29908b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
300d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     * @param size the size to measure
301d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     * @return the numerator
302d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     */
303d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael    public static int aspectRatioNumerator(Size size) {
3041cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        Size aspectRatio = reduce(size);
3051cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        return aspectRatio.width();
3061cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    }
3071cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael
3081cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    /**
3091cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * Given a size, return the closest aspect ratio that falls close to the
3101cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * given size.
31108b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
3121cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * @param size the size to approximate
3131cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     * @return the closest desired aspect ratio, or the original aspect ratio if
3141cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     *         none were close enough
3151cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael     */
3161cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael    public static Size getApproximateSize(Size size) {
3171cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        Size aspectRatio = reduce(size);
3181cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        float fuzzy = fuzzAspectRatio(size.width() / (float) size.height());
3191cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        int index = Arrays.asList(sDesiredAspectRatios).indexOf(fuzzy);
3201cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        if (index != -1) {
3212272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            aspectRatio = sDesiredAspectRatioSizes[index];
3221cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        }
3231cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael        return aspectRatio;
324d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael    }
325d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael
326d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael    /**
327d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     * Given a size return the numerator of its aspect ratio
32808b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling     *
329d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     * @param size
330d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     * @return the denominator
331d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael     */
332d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael    public static int aspectRatioDenominator(Size size) {
333d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael        BigInteger width = BigInteger.valueOf(size.width());
334d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael        BigInteger height = BigInteger.valueOf(size.height());
335d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael        BigInteger gcd = width.gcd(height);
336d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael        int denominator = Math.min(width.intValue(), height.intValue()) / gcd.intValue();
337d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael        return denominator;
338d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael    }
339d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael
3402272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    /**
3412272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * Returns the aspect ratio for the given size.
3422272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     *
3432272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * @param size The given size.
3442272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * @return A {@link Rational} which represents the aspect ratio.
3452272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     */
3462272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    public static Rational getAspectRatio(Size size) {
3472272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        int width = size.getWidth();
3482272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        int height = size.getHeight();
3492272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        int numerator = width;
3502272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        int denominator = height;
3512272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        if (height > width) {
3522272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            numerator = height;
3532272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            denominator = width;
3542272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        }
3552272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        return new Rational(numerator, denominator);
3562272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    }
3572272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu
3582272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    public static boolean hasSameAspectRatio(Rational ar1, Rational ar2) {
3592272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        return Math.abs(ar1.toDouble() - ar2.toDouble()) < ASPECT_RATIO_TOLERANCE;
3602272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    }
3612272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu
3622272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    /**
3638d8b329da25456adb5ee45e0450680654114e125I-Jong Lin     * Selects the maximal resolution for the given desired aspect ratio from all available
3648d8b329da25456adb5ee45e0450680654114e125I-Jong Lin     * resolutions.  If no resolution exists for the desired aspect ratio, return a resolution
3658d8b329da25456adb5ee45e0450680654114e125I-Jong Lin     * with the maximum number of pixels.
3662272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     *
3672272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * @param desiredAspectRatio The desired aspect ratio.
3682272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     * @param sizes All available resolutions.
3698d8b329da25456adb5ee45e0450680654114e125I-Jong Lin     * @return The maximal resolution for desired aspect ratio ; if no sizes are found, then
3708d8b329da25456adb5ee45e0450680654114e125I-Jong Lin     *      return size of (0,0)
3712272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu     */
3722272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    public static Size getLargestPictureSize(Rational desiredAspectRatio, List<Size> sizes) {
3738d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        int maxPixelNumNoAspect = 0;
3742272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        Size maxSize = new Size(0, 0);
3758d8b329da25456adb5ee45e0450680654114e125I-Jong Lin
3768d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // Fix for b/21758681
3778d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // Do first pass with the candidate with closest size, regardless of aspect ratio,
3788d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // to loosen the requirement of valid preview sizes.  As long as one size exists
3798d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // in the list, we should pass back a valid size.
3808d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        for (Size size : sizes) {
3818d8b329da25456adb5ee45e0450680654114e125I-Jong Lin            int pixelNum = size.getWidth() * size.getHeight();
3828d8b329da25456adb5ee45e0450680654114e125I-Jong Lin            if (pixelNum > maxPixelNumNoAspect) {
3838d8b329da25456adb5ee45e0450680654114e125I-Jong Lin                maxPixelNumNoAspect = pixelNum;
3848d8b329da25456adb5ee45e0450680654114e125I-Jong Lin                maxSize = size;
3858d8b329da25456adb5ee45e0450680654114e125I-Jong Lin            }
3868d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        }
3878d8b329da25456adb5ee45e0450680654114e125I-Jong Lin
3888d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // With second pass, override first pass with the candidate with closest
3898d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // size AND similar aspect ratio.  If there are no valid candidates are found
3908d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        // in the second pass, take the candidate from the first pass.
3918d8b329da25456adb5ee45e0450680654114e125I-Jong Lin        int maxPixelNumWithAspect = 0;
3922272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        for (Size size : sizes) {
3932272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            Rational aspectRatio = getAspectRatio(size);
3942272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            // Skip if the aspect ratio is not desired.
3952272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            if (!hasSameAspectRatio(aspectRatio, desiredAspectRatio)) {
3962272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu                continue;
3972272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            }
3982272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            int pixelNum = size.getWidth() * size.getHeight();
3998d8b329da25456adb5ee45e0450680654114e125I-Jong Lin            if (pixelNum > maxPixelNumWithAspect) {
4008d8b329da25456adb5ee45e0450680654114e125I-Jong Lin                maxPixelNumWithAspect = pixelNum;
4012272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu                maxSize = size;
4022272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu            }
4032272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        }
4048d8b329da25456adb5ee45e0450680654114e125I-Jong Lin
4052272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu        return maxSize;
4062272f8a6bfc8adca8ada7a215bef038d45256085Senpo Hu    }
407fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu
408fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu    public static DisplayMetrics getDisplayMetrics(Context context) {
409fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu        DisplayMetrics displayMetrics = new DisplayMetrics();
4108be316c7a8caf962cf3fcf5e49d332fb2718319fPaul Rohde        WindowManager wm = AndroidServices.instance().provideWindowManager();
411fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu        if (wm != null) {
412fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu            wm.getDefaultDisplay().getMetrics(displayMetrics);
413fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu        }
414fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu        return displayMetrics;
415fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu    }
416fb0496133a0b4b526dc70f6447bd4b97d95de95fSenpo Hu
41786d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    /**
41886d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * Takes selected sizes and a list of blacklisted sizes. All the blacklistes
41986d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * sizes will be removed from the 'sizes' list.
42086d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     *
42186d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * @param sizes the sizes to be filtered.
42286d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * @param blacklistString a String containing a comma-separated list of
42386d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     *            sizes that should be removed from the original list.
42486d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * @return A list that contains the filtered items.
42586d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     */
42686d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    @ParametersAreNonnullByDefault
42786d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    public static List<Size> filterBlackListedSizes(List<Size> sizes, String blacklistString) {
42886d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        String[] blacklistStringArray = blacklistString.split(",");
42986d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        if (blacklistStringArray.length == 0) {
43086d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling            return sizes;
43186d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        }
43286d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling
43386d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        Set<String> blacklistedSizes = new HashSet(Lists.newArrayList(blacklistStringArray));
43486d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        List<Size> newSizeList = new ArrayList<>();
43586d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        for (Size size : sizes) {
43686d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling            if (!isBlackListed(size, blacklistedSizes)) {
43786d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling                newSizeList.add(size);
43886d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling            }
43986d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        }
44086d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        return newSizeList;
44186d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    }
44286d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling
44386d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    /**
44486d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * Returns whether the given size is within the blacklist string.
44586d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     *
44686d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * @param size the size to check
44786d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * @param blacklistString a String containing a comma-separated list of
44886d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     *            sizes that should not be available on the device.
44986d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     * @return Whether the given size is blacklisted.
45086d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling     */
4511d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde    public static boolean isBlackListed(@Nonnull Size size, @Nonnull String blacklistString) {
45286d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        String[] blacklistStringArray = blacklistString.split(",");
45386d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        if (blacklistStringArray.length == 0) {
45486d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling            return false;
45586d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        }
45686d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        Set<String> blacklistedSizes = new HashSet(Lists.newArrayList(blacklistStringArray));
45786d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        return isBlackListed(size, blacklistedSizes);
45886d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    }
45986d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling
4601d84d7107686aa428ee2eeb1a8caf0ea3e43b1dfPaul Rohde    private static boolean isBlackListed(@Nonnull Size size, @Nonnull Set<String> blacklistedSizes) {
46186d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        String sizeStr = size.getWidth() + "x" + size.getHeight();
46286d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling        return blacklistedSizes.contains(sizeStr);
46386d753fcbb0c7474fa9b2797e76e31ef575e76cdSascha Haeberling    }
464b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael}
465