summaryrefslogtreecommitdiff
path: root/android/media/Utils.java
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/media/Utils.java
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/media/Utils.java')
-rw-r--r--android/media/Utils.java381
1 files changed, 381 insertions, 0 deletions
diff --git a/android/media/Utils.java b/android/media/Utils.java
new file mode 100644
index 00000000..5b62f16a
--- /dev/null
+++ b/android/media/Utils.java
@@ -0,0 +1,381 @@
+/*
+ * 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 android.media;
+
+import android.content.Context;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.provider.OpenableColumns;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Rational;
+import android.util.Size;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Vector;
+
+// package private
+class Utils {
+ private static final String TAG = "Utils";
+
+ /**
+ * Sorts distinct (non-intersecting) range array in ascending order.
+ * @throws java.lang.IllegalArgumentException if ranges are not distinct
+ */
+ public static <T extends Comparable<? super T>> void sortDistinctRanges(Range<T>[] ranges) {
+ Arrays.sort(ranges, new Comparator<Range<T>>() {
+ @Override
+ public int compare(Range<T> lhs, Range<T> rhs) {
+ if (lhs.getUpper().compareTo(rhs.getLower()) < 0) {
+ return -1;
+ } else if (lhs.getLower().compareTo(rhs.getUpper()) > 0) {
+ return 1;
+ }
+ throw new IllegalArgumentException(
+ "sample rate ranges must be distinct (" + lhs + " and " + rhs + ")");
+ }
+ });
+ }
+
+ /**
+ * Returns the intersection of two sets of non-intersecting ranges
+ * @param one a sorted set of non-intersecting ranges in ascending order
+ * @param another another sorted set of non-intersecting ranges in ascending order
+ * @return the intersection of the two sets, sorted in ascending order
+ */
+ public static <T extends Comparable<? super T>>
+ Range<T>[] intersectSortedDistinctRanges(Range<T>[] one, Range<T>[] another) {
+ int ix = 0;
+ Vector<Range<T>> result = new Vector<Range<T>>();
+ for (Range<T> range: another) {
+ while (ix < one.length &&
+ one[ix].getUpper().compareTo(range.getLower()) < 0) {
+ ++ix;
+ }
+ while (ix < one.length &&
+ one[ix].getUpper().compareTo(range.getUpper()) < 0) {
+ result.add(range.intersect(one[ix]));
+ ++ix;
+ }
+ if (ix == one.length) {
+ break;
+ }
+ if (one[ix].getLower().compareTo(range.getUpper()) <= 0) {
+ result.add(range.intersect(one[ix]));
+ }
+ }
+ return result.toArray(new Range[result.size()]);
+ }
+
+ /**
+ * Returns the index of the range that contains a value in a sorted array of distinct ranges.
+ * @param ranges a sorted array of non-intersecting ranges in ascending order
+ * @param value the value to search for
+ * @return if the value is in one of the ranges, it returns the index of that range. Otherwise,
+ * the return value is {@code (-1-index)} for the {@code index} of the range that is
+ * immediately following {@code value}.
+ */
+ public static <T extends Comparable<? super T>>
+ int binarySearchDistinctRanges(Range<T>[] ranges, T value) {
+ return Arrays.binarySearch(ranges, Range.create(value, value),
+ new Comparator<Range<T>>() {
+ @Override
+ public int compare(Range<T> lhs, Range<T> rhs) {
+ if (lhs.getUpper().compareTo(rhs.getLower()) < 0) {
+ return -1;
+ } else if (lhs.getLower().compareTo(rhs.getUpper()) > 0) {
+ return 1;
+ }
+ return 0;
+ }
+ });
+ }
+
+ /**
+ * Returns greatest common divisor
+ */
+ static int gcd(int a, int b) {
+ if (a == 0 && b == 0) {
+ return 1;
+ }
+ if (b < 0) {
+ b = -b;
+ }
+ if (a < 0) {
+ a = -a;
+ }
+ while (a != 0) {
+ int c = b % a;
+ b = a;
+ a = c;
+ }
+ return b;
+ }
+
+ /** Returns the equivalent factored range {@code newrange}, where for every
+ * {@code e}: {@code newrange.contains(e)} implies that {@code range.contains(e * factor)},
+ * and {@code !newrange.contains(e)} implies that {@code !range.contains(e * factor)}.
+ */
+ static Range<Integer>factorRange(Range<Integer> range, int factor) {
+ if (factor == 1) {
+ return range;
+ }
+ return Range.create(divUp(range.getLower(), factor), range.getUpper() / factor);
+ }
+
+ /** Returns the equivalent factored range {@code newrange}, where for every
+ * {@code e}: {@code newrange.contains(e)} implies that {@code range.contains(e * factor)},
+ * and {@code !newrange.contains(e)} implies that {@code !range.contains(e * factor)}.
+ */
+ static Range<Long>factorRange(Range<Long> range, long factor) {
+ if (factor == 1) {
+ return range;
+ }
+ return Range.create(divUp(range.getLower(), factor), range.getUpper() / factor);
+ }
+
+ private static Rational scaleRatio(Rational ratio, int num, int den) {
+ int common = gcd(num, den);
+ num /= common;
+ den /= common;
+ return new Rational(
+ (int)(ratio.getNumerator() * (double)num), // saturate to int
+ (int)(ratio.getDenominator() * (double)den)); // saturate to int
+ }
+
+ static Range<Rational> scaleRange(Range<Rational> range, int num, int den) {
+ if (num == den) {
+ return range;
+ }
+ return Range.create(
+ scaleRatio(range.getLower(), num, den),
+ scaleRatio(range.getUpper(), num, den));
+ }
+
+ static Range<Integer> alignRange(Range<Integer> range, int align) {
+ return range.intersect(
+ divUp(range.getLower(), align) * align,
+ (range.getUpper() / align) * align);
+ }
+
+ static int divUp(int num, int den) {
+ return (num + den - 1) / den;
+ }
+
+ static long divUp(long num, long den) {
+ return (num + den - 1) / den;
+ }
+
+ /**
+ * Returns least common multiple
+ */
+ private static long lcm(int a, int b) {
+ if (a == 0 || b == 0) {
+ throw new IllegalArgumentException("lce is not defined for zero arguments");
+ }
+ return (long)a * b / gcd(a, b);
+ }
+
+ static Range<Integer> intRangeFor(double v) {
+ return Range.create((int)v, (int)Math.ceil(v));
+ }
+
+ static Range<Long> longRangeFor(double v) {
+ return Range.create((long)v, (long)Math.ceil(v));
+ }
+
+ static Size parseSize(Object o, Size fallback) {
+ try {
+ return Size.parseSize((String) o);
+ } catch (ClassCastException e) {
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
+ return fallback;
+ }
+ Log.w(TAG, "could not parse size '" + o + "'");
+ return fallback;
+ }
+
+ static int parseIntSafely(Object o, int fallback) {
+ if (o == null) {
+ return fallback;
+ }
+ try {
+ String s = (String)o;
+ return Integer.parseInt(s);
+ } catch (ClassCastException e) {
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
+ return fallback;
+ }
+ Log.w(TAG, "could not parse integer '" + o + "'");
+ return fallback;
+ }
+
+ static Range<Integer> parseIntRange(Object o, Range<Integer> fallback) {
+ try {
+ String s = (String)o;
+ int ix = s.indexOf('-');
+ if (ix >= 0) {
+ return Range.create(
+ Integer.parseInt(s.substring(0, ix), 10),
+ Integer.parseInt(s.substring(ix + 1), 10));
+ }
+ int value = Integer.parseInt(s);
+ return Range.create(value, value);
+ } catch (ClassCastException e) {
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
+ return fallback;
+ } catch (IllegalArgumentException e) {
+ }
+ Log.w(TAG, "could not parse integer range '" + o + "'");
+ return fallback;
+ }
+
+ static Range<Long> parseLongRange(Object o, Range<Long> fallback) {
+ try {
+ String s = (String)o;
+ int ix = s.indexOf('-');
+ if (ix >= 0) {
+ return Range.create(
+ Long.parseLong(s.substring(0, ix), 10),
+ Long.parseLong(s.substring(ix + 1), 10));
+ }
+ long value = Long.parseLong(s);
+ return Range.create(value, value);
+ } catch (ClassCastException e) {
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
+ return fallback;
+ } catch (IllegalArgumentException e) {
+ }
+ Log.w(TAG, "could not parse long range '" + o + "'");
+ return fallback;
+ }
+
+ static Range<Rational> parseRationalRange(Object o, Range<Rational> fallback) {
+ try {
+ String s = (String)o;
+ int ix = s.indexOf('-');
+ if (ix >= 0) {
+ return Range.create(
+ Rational.parseRational(s.substring(0, ix)),
+ Rational.parseRational(s.substring(ix + 1)));
+ }
+ Rational value = Rational.parseRational(s);
+ return Range.create(value, value);
+ } catch (ClassCastException e) {
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
+ return fallback;
+ } catch (IllegalArgumentException e) {
+ }
+ Log.w(TAG, "could not parse rational range '" + o + "'");
+ return fallback;
+ }
+
+ static Pair<Size, Size> parseSizeRange(Object o) {
+ try {
+ String s = (String)o;
+ int ix = s.indexOf('-');
+ if (ix >= 0) {
+ return Pair.create(
+ Size.parseSize(s.substring(0, ix)),
+ Size.parseSize(s.substring(ix + 1)));
+ }
+ Size value = Size.parseSize(s);
+ return Pair.create(value, value);
+ } catch (ClassCastException e) {
+ } catch (NumberFormatException e) {
+ } catch (NullPointerException e) {
+ return null;
+ } catch (IllegalArgumentException e) {
+ }
+ Log.w(TAG, "could not parse size range '" + o + "'");
+ return null;
+ }
+
+ /**
+ * Creates a unique file in the specified external storage with the desired name. If the name is
+ * taken, the new file's name will have '(%d)' to avoid overwriting files.
+ *
+ * @param context {@link Context} to query the file name from.
+ * @param subdirectory One of the directories specified in {@link android.os.Environment}
+ * @param fileName desired name for the file.
+ * @param mimeType MIME type of the file to create.
+ * @return the File object in the storage, or null if an error occurs.
+ */
+ public static File getUniqueExternalFile(Context context, String subdirectory, String fileName,
+ String mimeType) {
+ File externalStorage = Environment.getExternalStoragePublicDirectory(subdirectory);
+ // Make sure the storage subdirectory exists
+ externalStorage.mkdirs();
+
+ File outFile = null;
+ try {
+ // Ensure the file has a unique name, as to not override any existing file
+ outFile = FileUtils.buildUniqueFile(externalStorage, mimeType, fileName);
+ } catch (FileNotFoundException e) {
+ // This might also be reached if the number of repeated files gets too high
+ Log.e(TAG, "Unable to get a unique file name: " + e);
+ return null;
+ }
+ return outFile;
+ }
+
+ /**
+ * Returns a file's display name from its {@link android.content.ContentResolver.SCHEME_FILE}
+ * or {@link android.content.ContentResolver.SCHEME_CONTENT} Uri. The display name of a file
+ * includes its extension.
+ *
+ * @param context Context trying to resolve the file's display name.
+ * @param uri Uri of the file.
+ * @return the file's display name, or the uri's string if something fails or the uri isn't in
+ * the schemes specified above.
+ */
+ static String getFileDisplayNameFromUri(Context context, Uri uri) {
+ String scheme = uri.getScheme();
+
+ if (ContentResolver.SCHEME_FILE.equals(scheme)) {
+ return uri.getLastPathSegment();
+ } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
+ // We need to query the ContentResolver to get the actual file name as the Uri masks it.
+ // This means we want the name used for display purposes only.
+ String[] proj = {
+ OpenableColumns.DISPLAY_NAME
+ };
+ try (Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null)) {
+ if (cursor != null && cursor.getCount() != 0) {
+ cursor.moveToFirst();
+ return cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
+ }
+ }
+ }
+
+ // This will only happen if the Uri isn't either SCHEME_CONTENT or SCHEME_FILE, so we assume
+ // it already represents the file's name.
+ return uri.toString();
+ }
+}