diff options
77 files changed, 2754 insertions, 793 deletions
diff --git a/api/current.txt b/api/current.txt index c82c04d0122a..a5d8a930e40f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -944,6 +944,8 @@ package android { field public static final int popupBackground = 16843126; // 0x1010176 field public static final int popupCharacters = 16843332; // 0x1010244 field public static final int popupElevation = 16843916; // 0x101048c + field public static final int popupEnterTransition = 16844065; // 0x1010521 + field public static final int popupExitTransition = 16844066; // 0x1010522 field public static final int popupKeyboard = 16843331; // 0x1010243 field public static final int popupLayout = 16843323; // 0x101023b field public static final int popupMenuStyle = 16843520; // 0x1010300 @@ -2754,14 +2756,10 @@ package android.accessibilityservice { } public final class GestureDescription { - method public static android.accessibilityservice.GestureDescription createClick(int, int); - method public static android.accessibilityservice.GestureDescription createLongClick(int, int); - method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long); - method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long); + method public static long getMaxGestureDuration(); + method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int); method public int getStrokeCount(); - field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L - field public static final int MAX_STROKE_COUNT = 10; // 0xa } public static class GestureDescription.Builder { @@ -47196,6 +47194,8 @@ package android.widget { method public android.graphics.drawable.Drawable getBackground(); method public android.view.View getContentView(); method public float getElevation(); + method public android.transition.Transition getEnterTransition(); + method public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); @@ -50689,6 +50689,7 @@ package java.lang { method public float floatValue(); method public static int hashCode(double); method public int intValue(); + method public static boolean isFinite(double); method public static boolean isInfinite(double); method public boolean isInfinite(); method public static boolean isNaN(double); @@ -50770,6 +50771,7 @@ package java.lang { method public float floatValue(); method public static float intBitsToFloat(int); method public int intValue(); + method public static boolean isFinite(float); method public static boolean isInfinite(float); method public boolean isInfinite(); method public static boolean isNaN(float); @@ -57347,6 +57349,7 @@ package java.util { method public E get(int); method public boolean removeIf(java.util.function.Predicate<? super E>); method public int size(); + method public void sort(java.util.Comparator<? super E>); method public java.util.Spliterator<E> spliterator(); method public void trimToSize(); } @@ -57478,6 +57481,14 @@ package java.util { method public static java.util.Spliterator.OfLong spliterator(long[], int, int); method public static java.util.Spliterator.OfDouble spliterator(double[]); method public static java.util.Spliterator.OfDouble spliterator(double[], int, int); + method public static java.util.stream.Stream<T> stream(T[]); + method public static java.util.stream.Stream<T> stream(T[], int, int); + method public static java.util.stream.IntStream stream(int[]); + method public static java.util.stream.IntStream stream(int[], int, int); + method public static java.util.stream.LongStream stream(long[]); + method public static java.util.stream.LongStream stream(long[], int, int); + method public static java.util.stream.DoubleStream stream(double[]); + method public static java.util.stream.DoubleStream stream(double[], int, int); method public static java.lang.String toString(long[]); method public static java.lang.String toString(int[]); method public static java.lang.String toString(short[]); @@ -57637,11 +57648,13 @@ package java.util { method public abstract int hashCode(); method public abstract boolean isEmpty(); method public abstract java.util.Iterator<E> iterator(); + method public default java.util.stream.Stream<E> parallelStream(); method public abstract boolean remove(java.lang.Object); method public abstract boolean removeAll(java.util.Collection<?>); method public default boolean removeIf(java.util.function.Predicate<? super E>); method public abstract boolean retainAll(java.util.Collection<?>); method public abstract int size(); + method public default java.util.stream.Stream<E> stream(); method public abstract java.lang.Object[] toArray(); method public abstract T[] toArray(T[]); } @@ -58841,6 +58854,34 @@ package java.util { method public java.util.Spliterator<T> trySplit(); } + public final class SplittableRandom { + ctor public SplittableRandom(long); + ctor public SplittableRandom(); + method public java.util.stream.DoubleStream doubles(long); + method public java.util.stream.DoubleStream doubles(); + method public java.util.stream.DoubleStream doubles(long, double, double); + method public java.util.stream.DoubleStream doubles(double, double); + method public java.util.stream.IntStream ints(long); + method public java.util.stream.IntStream ints(); + method public java.util.stream.IntStream ints(long, int, int); + method public java.util.stream.IntStream ints(int, int); + method public java.util.stream.LongStream longs(long); + method public java.util.stream.LongStream longs(); + method public java.util.stream.LongStream longs(long, long, long); + method public java.util.stream.LongStream longs(long, long); + method public boolean nextBoolean(); + method public double nextDouble(); + method public double nextDouble(double); + method public double nextDouble(double, double); + method public int nextInt(); + method public int nextInt(int); + method public int nextInt(int, int); + method public long nextLong(); + method public long nextLong(long); + method public long nextLong(long, long); + method public java.util.SplittableRandom split(); + } + public class Stack extends java.util.Vector { ctor public Stack(); method public boolean empty(); @@ -60024,6 +60065,18 @@ package java.util.concurrent { public class ThreadLocalRandom extends java.util.Random { method public static java.util.concurrent.ThreadLocalRandom current(); + method public java.util.stream.DoubleStream doubles(long); + method public java.util.stream.DoubleStream doubles(); + method public java.util.stream.DoubleStream doubles(long, double, double); + method public java.util.stream.DoubleStream doubles(double, double); + method public java.util.stream.IntStream ints(long); + method public java.util.stream.IntStream ints(); + method public java.util.stream.IntStream ints(long, int, int); + method public java.util.stream.IntStream ints(int, int); + method public java.util.stream.LongStream longs(long); + method public java.util.stream.LongStream longs(); + method public java.util.stream.LongStream longs(long, long, long); + method public java.util.stream.LongStream longs(long, long); method public double nextDouble(double); method public double nextDouble(double, double); method public int nextInt(int, int); @@ -61425,6 +61478,292 @@ package java.util.regex { } +package java.util.stream { + + public abstract interface BaseStream implements java.lang.AutoCloseable { + method public abstract void close(); + method public abstract boolean isParallel(); + method public abstract java.util.Iterator<T> iterator(); + method public abstract S onClose(java.lang.Runnable); + method public abstract S parallel(); + method public abstract S sequential(); + method public abstract java.util.Spliterator<T> spliterator(); + method public abstract S unordered(); + } + + public abstract interface Collector { + method public abstract java.util.function.BiConsumer<A, T> accumulator(); + method public abstract java.util.Set<java.util.stream.Collector.Characteristics> characteristics(); + method public abstract java.util.function.BinaryOperator<A> combiner(); + method public abstract java.util.function.Function<A, R> finisher(); + method public static java.util.stream.Collector<T, R, R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...); + method public static java.util.stream.Collector<T, A, R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A, T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A, R>, java.util.stream.Collector.Characteristics...); + method public abstract java.util.function.Supplier<A> supplier(); + } + + public static final class Collector.Characteristics extends java.lang.Enum { + method public static java.util.stream.Collector.Characteristics valueOf(java.lang.String); + method public static final java.util.stream.Collector.Characteristics[] values(); + enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT; + enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH; + enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED; + } + + public final class Collectors { + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, A, RR> collectingAndThen(java.util.stream.Collector<T, A, R>, java.util.function.Function<R, RR>); + method public static java.util.stream.Collector<T, ?, java.lang.Long> counting(); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, java.util.List<T>>> groupingBy(java.util.function.Function<? super T, ? extends K>); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, D>> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, M> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, D>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, M> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence); + method public static java.util.stream.Collector<T, ?, R> mapping(java.util.function.Function<? super T, ? extends U>, java.util.stream.Collector<? super U, A, R>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> maxBy(java.util.Comparator<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> minBy(java.util.Comparator<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, T> reducing(T, java.util.function.BinaryOperator<T>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>); + method public static java.util.stream.Collector<T, ?, U> reducing(U, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, ?, C> toCollection(java.util.function.Supplier<C>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, M> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>); + method public static java.util.stream.Collector<T, ?, java.util.List<T>> toList(); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, M> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>); + method public static java.util.stream.Collector<T, ?, java.util.Set<T>> toSet(); + } + + public abstract interface DoubleStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.DoublePredicate); + method public abstract boolean anyMatch(java.util.function.DoublePredicate); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Double> boxed(); + method public static java.util.stream.DoubleStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream); + method public abstract long count(); + method public abstract java.util.stream.DoubleStream distinct(); + method public static java.util.stream.DoubleStream empty(); + method public abstract java.util.stream.DoubleStream filter(java.util.function.DoublePredicate); + method public abstract java.util.OptionalDouble findAny(); + method public abstract java.util.OptionalDouble findFirst(); + method public abstract java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>); + method public abstract void forEach(java.util.function.DoubleConsumer); + method public abstract void forEachOrdered(java.util.function.DoubleConsumer); + method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier); + method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfDouble iterator(); + method public abstract java.util.stream.DoubleStream limit(long); + method public abstract java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>); + method public abstract java.util.OptionalDouble max(); + method public abstract java.util.OptionalDouble min(); + method public abstract boolean noneMatch(java.util.function.DoublePredicate); + method public static java.util.stream.DoubleStream of(double); + method public static java.util.stream.DoubleStream of(double...); + method public abstract java.util.stream.DoubleStream parallel(); + method public abstract java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer); + method public abstract double reduce(double, java.util.function.DoubleBinaryOperator); + method public abstract java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator); + method public abstract java.util.stream.DoubleStream sequential(); + method public abstract java.util.stream.DoubleStream skip(long); + method public abstract java.util.stream.DoubleStream sorted(); + method public abstract java.util.Spliterator.OfDouble spliterator(); + method public abstract double sum(); + method public abstract java.util.DoubleSummaryStatistics summaryStatistics(); + method public abstract double[] toArray(); + } + + public static abstract interface DoubleStream.Builder implements java.util.function.DoubleConsumer { + method public abstract void accept(double); + method public default java.util.stream.DoubleStream.Builder add(double); + method public abstract java.util.stream.DoubleStream build(); + } + + public abstract interface IntStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.IntPredicate); + method public abstract boolean anyMatch(java.util.function.IntPredicate); + method public abstract java.util.stream.DoubleStream asDoubleStream(); + method public abstract java.util.stream.LongStream asLongStream(); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Integer> boxed(); + method public static java.util.stream.IntStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream); + method public abstract long count(); + method public abstract java.util.stream.IntStream distinct(); + method public static java.util.stream.IntStream empty(); + method public abstract java.util.stream.IntStream filter(java.util.function.IntPredicate); + method public abstract java.util.OptionalInt findAny(); + method public abstract java.util.OptionalInt findFirst(); + method public abstract java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>); + method public abstract void forEach(java.util.function.IntConsumer); + method public abstract void forEachOrdered(java.util.function.IntConsumer); + method public static java.util.stream.IntStream generate(java.util.function.IntSupplier); + method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfInt iterator(); + method public abstract java.util.stream.IntStream limit(long); + method public abstract java.util.stream.IntStream map(java.util.function.IntUnaryOperator); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>); + method public abstract java.util.OptionalInt max(); + method public abstract java.util.OptionalInt min(); + method public abstract boolean noneMatch(java.util.function.IntPredicate); + method public static java.util.stream.IntStream of(int); + method public static java.util.stream.IntStream of(int...); + method public abstract java.util.stream.IntStream parallel(); + method public abstract java.util.stream.IntStream peek(java.util.function.IntConsumer); + method public static java.util.stream.IntStream range(int, int); + method public static java.util.stream.IntStream rangeClosed(int, int); + method public abstract int reduce(int, java.util.function.IntBinaryOperator); + method public abstract java.util.OptionalInt reduce(java.util.function.IntBinaryOperator); + method public abstract java.util.stream.IntStream sequential(); + method public abstract java.util.stream.IntStream skip(long); + method public abstract java.util.stream.IntStream sorted(); + method public abstract java.util.Spliterator.OfInt spliterator(); + method public abstract int sum(); + method public abstract java.util.IntSummaryStatistics summaryStatistics(); + method public abstract int[] toArray(); + } + + public static abstract interface IntStream.Builder implements java.util.function.IntConsumer { + method public abstract void accept(int); + method public default java.util.stream.IntStream.Builder add(int); + method public abstract java.util.stream.IntStream build(); + } + + public abstract interface LongStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.LongPredicate); + method public abstract boolean anyMatch(java.util.function.LongPredicate); + method public abstract java.util.stream.DoubleStream asDoubleStream(); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Long> boxed(); + method public static java.util.stream.LongStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream); + method public abstract long count(); + method public abstract java.util.stream.LongStream distinct(); + method public static java.util.stream.LongStream empty(); + method public abstract java.util.stream.LongStream filter(java.util.function.LongPredicate); + method public abstract java.util.OptionalLong findAny(); + method public abstract java.util.OptionalLong findFirst(); + method public abstract java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>); + method public abstract void forEach(java.util.function.LongConsumer); + method public abstract void forEachOrdered(java.util.function.LongConsumer); + method public static java.util.stream.LongStream generate(java.util.function.LongSupplier); + method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfLong iterator(); + method public abstract java.util.stream.LongStream limit(long); + method public abstract java.util.stream.LongStream map(java.util.function.LongUnaryOperator); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>); + method public abstract java.util.OptionalLong max(); + method public abstract java.util.OptionalLong min(); + method public abstract boolean noneMatch(java.util.function.LongPredicate); + method public static java.util.stream.LongStream of(long); + method public static java.util.stream.LongStream of(long...); + method public abstract java.util.stream.LongStream parallel(); + method public abstract java.util.stream.LongStream peek(java.util.function.LongConsumer); + method public static java.util.stream.LongStream range(long, long); + method public static java.util.stream.LongStream rangeClosed(long, long); + method public abstract long reduce(long, java.util.function.LongBinaryOperator); + method public abstract java.util.OptionalLong reduce(java.util.function.LongBinaryOperator); + method public abstract java.util.stream.LongStream sequential(); + method public abstract java.util.stream.LongStream skip(long); + method public abstract java.util.stream.LongStream sorted(); + method public abstract java.util.Spliterator.OfLong spliterator(); + method public abstract long sum(); + method public abstract java.util.LongSummaryStatistics summaryStatistics(); + method public abstract long[] toArray(); + } + + public static abstract interface LongStream.Builder implements java.util.function.LongConsumer { + method public abstract void accept(long); + method public default java.util.stream.LongStream.Builder add(long); + method public abstract java.util.stream.LongStream build(); + } + + public abstract interface Stream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.Predicate<? super T>); + method public abstract boolean anyMatch(java.util.function.Predicate<? super T>); + method public static java.util.stream.Stream.Builder<T> builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, ? super T>, java.util.function.BiConsumer<R, R>); + method public abstract R collect(java.util.stream.Collector<? super T, A, R>); + method public static java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>); + method public abstract long count(); + method public abstract java.util.stream.Stream<T> distinct(); + method public static java.util.stream.Stream<T> empty(); + method public abstract java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>); + method public abstract java.util.Optional<T> findAny(); + method public abstract java.util.Optional<T> findFirst(); + method public abstract java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>>); + method public abstract java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream>); + method public abstract java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream>); + method public abstract java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream>); + method public abstract void forEach(java.util.function.Consumer<? super T>); + method public abstract void forEachOrdered(java.util.function.Consumer<? super T>); + method public static java.util.stream.Stream<T> generate(java.util.function.Supplier<T>); + method public static java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>); + method public abstract java.util.stream.Stream<T> limit(long); + method public abstract java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R>); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>); + method public abstract java.util.Optional<T> max(java.util.Comparator<? super T>); + method public abstract java.util.Optional<T> min(java.util.Comparator<? super T>); + method public abstract boolean noneMatch(java.util.function.Predicate<? super T>); + method public static java.util.stream.Stream<T> of(T); + method public static java.util.stream.Stream<T> of(T...); + method public abstract java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>); + method public abstract T reduce(T, java.util.function.BinaryOperator<T>); + method public abstract java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>); + method public abstract U reduce(U, java.util.function.BiFunction<U, ? super T, U>, java.util.function.BinaryOperator<U>); + method public abstract java.util.stream.Stream<T> skip(long); + method public abstract java.util.stream.Stream<T> sorted(); + method public abstract java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>); + method public abstract java.lang.Object[] toArray(); + method public abstract A[] toArray(java.util.function.IntFunction<A[]>); + } + + public static abstract interface Stream.Builder implements java.util.function.Consumer { + method public abstract void accept(T); + method public default java.util.stream.Stream.Builder<T> add(T); + method public abstract java.util.stream.Stream<T> build(); + } + + public final class StreamSupport { + method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean); + method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean); + method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean); + method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean); + method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean); + method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean); + method public static java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean); + method public static java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean); + } + +} + package java.util.zip { public class Adler32 implements java.util.zip.Checksum { diff --git a/api/system-current.txt b/api/system-current.txt index 24edfe0134a9..97b7710edec8 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1039,6 +1039,8 @@ package android { field public static final int popupBackground = 16843126; // 0x1010176 field public static final int popupCharacters = 16843332; // 0x1010244 field public static final int popupElevation = 16843916; // 0x101048c + field public static final int popupEnterTransition = 16844065; // 0x1010521 + field public static final int popupExitTransition = 16844066; // 0x1010522 field public static final int popupKeyboard = 16843331; // 0x1010243 field public static final int popupLayout = 16843323; // 0x101023b field public static final int popupMenuStyle = 16843520; // 0x1010300 @@ -2856,14 +2858,10 @@ package android.accessibilityservice { } public final class GestureDescription { - method public static android.accessibilityservice.GestureDescription createClick(int, int); - method public static android.accessibilityservice.GestureDescription createLongClick(int, int); - method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long); - method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long); + method public static long getMaxGestureDuration(); + method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int); method public int getStrokeCount(); - field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L - field public static final int MAX_STROKE_COUNT = 10; // 0xa } public static class GestureDescription.Builder { @@ -31386,6 +31384,7 @@ package android.os { field public static final int RELEASE_FLAG_WAIT_FOR_NO_PROXIMITY = 1; // 0x1 field public static final deprecated int SCREEN_BRIGHT_WAKE_LOCK = 10; // 0xa field public static final deprecated int SCREEN_DIM_WAKE_LOCK = 6; // 0x6 + field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3 field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1 field public static final int USER_ACTIVITY_EVENT_OTHER = 0; // 0x0 field public static final int USER_ACTIVITY_EVENT_TOUCH = 2; // 0x2 @@ -48671,9 +48670,8 @@ package android.webkit { field public static final int LIBLOAD_FAILED_TO_LOAD_LIBRARY = 6; // 0x6 field public static final int LIBLOAD_FAILED_TO_OPEN_RELRO_FILE = 5; // 0x5 field public static final int LIBLOAD_FAILED_WAITING_FOR_RELRO = 3; // 0x3 - field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9; // 0x9 + field public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // 0x8 field public static final int LIBLOAD_SUCCESS = 0; // 0x0 - field public static final int LIBLOAD_WEBVIEW_BEING_REPLACED = 8; // 0x8 field public static final int LIBLOAD_WRONG_PACKAGE_NAME = 1; // 0x1 } @@ -50260,6 +50258,8 @@ package android.widget { method public android.graphics.drawable.Drawable getBackground(); method public android.view.View getContentView(); method public float getElevation(); + method public android.transition.Transition getEnterTransition(); + method public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); @@ -53753,6 +53753,7 @@ package java.lang { method public float floatValue(); method public static int hashCode(double); method public int intValue(); + method public static boolean isFinite(double); method public static boolean isInfinite(double); method public boolean isInfinite(); method public static boolean isNaN(double); @@ -53834,6 +53835,7 @@ package java.lang { method public float floatValue(); method public static float intBitsToFloat(int); method public int intValue(); + method public static boolean isFinite(float); method public static boolean isInfinite(float); method public boolean isInfinite(); method public static boolean isNaN(float); @@ -60411,6 +60413,7 @@ package java.util { method public E get(int); method public boolean removeIf(java.util.function.Predicate<? super E>); method public int size(); + method public void sort(java.util.Comparator<? super E>); method public java.util.Spliterator<E> spliterator(); method public void trimToSize(); } @@ -60542,6 +60545,14 @@ package java.util { method public static java.util.Spliterator.OfLong spliterator(long[], int, int); method public static java.util.Spliterator.OfDouble spliterator(double[]); method public static java.util.Spliterator.OfDouble spliterator(double[], int, int); + method public static java.util.stream.Stream<T> stream(T[]); + method public static java.util.stream.Stream<T> stream(T[], int, int); + method public static java.util.stream.IntStream stream(int[]); + method public static java.util.stream.IntStream stream(int[], int, int); + method public static java.util.stream.LongStream stream(long[]); + method public static java.util.stream.LongStream stream(long[], int, int); + method public static java.util.stream.DoubleStream stream(double[]); + method public static java.util.stream.DoubleStream stream(double[], int, int); method public static java.lang.String toString(long[]); method public static java.lang.String toString(int[]); method public static java.lang.String toString(short[]); @@ -60701,11 +60712,13 @@ package java.util { method public abstract int hashCode(); method public abstract boolean isEmpty(); method public abstract java.util.Iterator<E> iterator(); + method public default java.util.stream.Stream<E> parallelStream(); method public abstract boolean remove(java.lang.Object); method public abstract boolean removeAll(java.util.Collection<?>); method public default boolean removeIf(java.util.function.Predicate<? super E>); method public abstract boolean retainAll(java.util.Collection<?>); method public abstract int size(); + method public default java.util.stream.Stream<E> stream(); method public abstract java.lang.Object[] toArray(); method public abstract T[] toArray(T[]); } @@ -61905,6 +61918,34 @@ package java.util { method public java.util.Spliterator<T> trySplit(); } + public final class SplittableRandom { + ctor public SplittableRandom(long); + ctor public SplittableRandom(); + method public java.util.stream.DoubleStream doubles(long); + method public java.util.stream.DoubleStream doubles(); + method public java.util.stream.DoubleStream doubles(long, double, double); + method public java.util.stream.DoubleStream doubles(double, double); + method public java.util.stream.IntStream ints(long); + method public java.util.stream.IntStream ints(); + method public java.util.stream.IntStream ints(long, int, int); + method public java.util.stream.IntStream ints(int, int); + method public java.util.stream.LongStream longs(long); + method public java.util.stream.LongStream longs(); + method public java.util.stream.LongStream longs(long, long, long); + method public java.util.stream.LongStream longs(long, long); + method public boolean nextBoolean(); + method public double nextDouble(); + method public double nextDouble(double); + method public double nextDouble(double, double); + method public int nextInt(); + method public int nextInt(int); + method public int nextInt(int, int); + method public long nextLong(); + method public long nextLong(long); + method public long nextLong(long, long); + method public java.util.SplittableRandom split(); + } + public class Stack extends java.util.Vector { ctor public Stack(); method public boolean empty(); @@ -63088,6 +63129,18 @@ package java.util.concurrent { public class ThreadLocalRandom extends java.util.Random { method public static java.util.concurrent.ThreadLocalRandom current(); + method public java.util.stream.DoubleStream doubles(long); + method public java.util.stream.DoubleStream doubles(); + method public java.util.stream.DoubleStream doubles(long, double, double); + method public java.util.stream.DoubleStream doubles(double, double); + method public java.util.stream.IntStream ints(long); + method public java.util.stream.IntStream ints(); + method public java.util.stream.IntStream ints(long, int, int); + method public java.util.stream.IntStream ints(int, int); + method public java.util.stream.LongStream longs(long); + method public java.util.stream.LongStream longs(); + method public java.util.stream.LongStream longs(long, long, long); + method public java.util.stream.LongStream longs(long, long); method public double nextDouble(double); method public double nextDouble(double, double); method public int nextInt(int, int); @@ -64489,6 +64542,292 @@ package java.util.regex { } +package java.util.stream { + + public abstract interface BaseStream implements java.lang.AutoCloseable { + method public abstract void close(); + method public abstract boolean isParallel(); + method public abstract java.util.Iterator<T> iterator(); + method public abstract S onClose(java.lang.Runnable); + method public abstract S parallel(); + method public abstract S sequential(); + method public abstract java.util.Spliterator<T> spliterator(); + method public abstract S unordered(); + } + + public abstract interface Collector { + method public abstract java.util.function.BiConsumer<A, T> accumulator(); + method public abstract java.util.Set<java.util.stream.Collector.Characteristics> characteristics(); + method public abstract java.util.function.BinaryOperator<A> combiner(); + method public abstract java.util.function.Function<A, R> finisher(); + method public static java.util.stream.Collector<T, R, R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...); + method public static java.util.stream.Collector<T, A, R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A, T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A, R>, java.util.stream.Collector.Characteristics...); + method public abstract java.util.function.Supplier<A> supplier(); + } + + public static final class Collector.Characteristics extends java.lang.Enum { + method public static java.util.stream.Collector.Characteristics valueOf(java.lang.String); + method public static final java.util.stream.Collector.Characteristics[] values(); + enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT; + enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH; + enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED; + } + + public final class Collectors { + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, A, RR> collectingAndThen(java.util.stream.Collector<T, A, R>, java.util.function.Function<R, RR>); + method public static java.util.stream.Collector<T, ?, java.lang.Long> counting(); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, java.util.List<T>>> groupingBy(java.util.function.Function<? super T, ? extends K>); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, D>> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, M> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, D>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, M> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence); + method public static java.util.stream.Collector<T, ?, R> mapping(java.util.function.Function<? super T, ? extends U>, java.util.stream.Collector<? super U, A, R>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> maxBy(java.util.Comparator<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> minBy(java.util.Comparator<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, T> reducing(T, java.util.function.BinaryOperator<T>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>); + method public static java.util.stream.Collector<T, ?, U> reducing(U, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, ?, C> toCollection(java.util.function.Supplier<C>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, M> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>); + method public static java.util.stream.Collector<T, ?, java.util.List<T>> toList(); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, M> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>); + method public static java.util.stream.Collector<T, ?, java.util.Set<T>> toSet(); + } + + public abstract interface DoubleStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.DoublePredicate); + method public abstract boolean anyMatch(java.util.function.DoublePredicate); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Double> boxed(); + method public static java.util.stream.DoubleStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream); + method public abstract long count(); + method public abstract java.util.stream.DoubleStream distinct(); + method public static java.util.stream.DoubleStream empty(); + method public abstract java.util.stream.DoubleStream filter(java.util.function.DoublePredicate); + method public abstract java.util.OptionalDouble findAny(); + method public abstract java.util.OptionalDouble findFirst(); + method public abstract java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>); + method public abstract void forEach(java.util.function.DoubleConsumer); + method public abstract void forEachOrdered(java.util.function.DoubleConsumer); + method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier); + method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfDouble iterator(); + method public abstract java.util.stream.DoubleStream limit(long); + method public abstract java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>); + method public abstract java.util.OptionalDouble max(); + method public abstract java.util.OptionalDouble min(); + method public abstract boolean noneMatch(java.util.function.DoublePredicate); + method public static java.util.stream.DoubleStream of(double); + method public static java.util.stream.DoubleStream of(double...); + method public abstract java.util.stream.DoubleStream parallel(); + method public abstract java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer); + method public abstract double reduce(double, java.util.function.DoubleBinaryOperator); + method public abstract java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator); + method public abstract java.util.stream.DoubleStream sequential(); + method public abstract java.util.stream.DoubleStream skip(long); + method public abstract java.util.stream.DoubleStream sorted(); + method public abstract java.util.Spliterator.OfDouble spliterator(); + method public abstract double sum(); + method public abstract java.util.DoubleSummaryStatistics summaryStatistics(); + method public abstract double[] toArray(); + } + + public static abstract interface DoubleStream.Builder implements java.util.function.DoubleConsumer { + method public abstract void accept(double); + method public default java.util.stream.DoubleStream.Builder add(double); + method public abstract java.util.stream.DoubleStream build(); + } + + public abstract interface IntStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.IntPredicate); + method public abstract boolean anyMatch(java.util.function.IntPredicate); + method public abstract java.util.stream.DoubleStream asDoubleStream(); + method public abstract java.util.stream.LongStream asLongStream(); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Integer> boxed(); + method public static java.util.stream.IntStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream); + method public abstract long count(); + method public abstract java.util.stream.IntStream distinct(); + method public static java.util.stream.IntStream empty(); + method public abstract java.util.stream.IntStream filter(java.util.function.IntPredicate); + method public abstract java.util.OptionalInt findAny(); + method public abstract java.util.OptionalInt findFirst(); + method public abstract java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>); + method public abstract void forEach(java.util.function.IntConsumer); + method public abstract void forEachOrdered(java.util.function.IntConsumer); + method public static java.util.stream.IntStream generate(java.util.function.IntSupplier); + method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfInt iterator(); + method public abstract java.util.stream.IntStream limit(long); + method public abstract java.util.stream.IntStream map(java.util.function.IntUnaryOperator); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>); + method public abstract java.util.OptionalInt max(); + method public abstract java.util.OptionalInt min(); + method public abstract boolean noneMatch(java.util.function.IntPredicate); + method public static java.util.stream.IntStream of(int); + method public static java.util.stream.IntStream of(int...); + method public abstract java.util.stream.IntStream parallel(); + method public abstract java.util.stream.IntStream peek(java.util.function.IntConsumer); + method public static java.util.stream.IntStream range(int, int); + method public static java.util.stream.IntStream rangeClosed(int, int); + method public abstract int reduce(int, java.util.function.IntBinaryOperator); + method public abstract java.util.OptionalInt reduce(java.util.function.IntBinaryOperator); + method public abstract java.util.stream.IntStream sequential(); + method public abstract java.util.stream.IntStream skip(long); + method public abstract java.util.stream.IntStream sorted(); + method public abstract java.util.Spliterator.OfInt spliterator(); + method public abstract int sum(); + method public abstract java.util.IntSummaryStatistics summaryStatistics(); + method public abstract int[] toArray(); + } + + public static abstract interface IntStream.Builder implements java.util.function.IntConsumer { + method public abstract void accept(int); + method public default java.util.stream.IntStream.Builder add(int); + method public abstract java.util.stream.IntStream build(); + } + + public abstract interface LongStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.LongPredicate); + method public abstract boolean anyMatch(java.util.function.LongPredicate); + method public abstract java.util.stream.DoubleStream asDoubleStream(); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Long> boxed(); + method public static java.util.stream.LongStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream); + method public abstract long count(); + method public abstract java.util.stream.LongStream distinct(); + method public static java.util.stream.LongStream empty(); + method public abstract java.util.stream.LongStream filter(java.util.function.LongPredicate); + method public abstract java.util.OptionalLong findAny(); + method public abstract java.util.OptionalLong findFirst(); + method public abstract java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>); + method public abstract void forEach(java.util.function.LongConsumer); + method public abstract void forEachOrdered(java.util.function.LongConsumer); + method public static java.util.stream.LongStream generate(java.util.function.LongSupplier); + method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfLong iterator(); + method public abstract java.util.stream.LongStream limit(long); + method public abstract java.util.stream.LongStream map(java.util.function.LongUnaryOperator); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>); + method public abstract java.util.OptionalLong max(); + method public abstract java.util.OptionalLong min(); + method public abstract boolean noneMatch(java.util.function.LongPredicate); + method public static java.util.stream.LongStream of(long); + method public static java.util.stream.LongStream of(long...); + method public abstract java.util.stream.LongStream parallel(); + method public abstract java.util.stream.LongStream peek(java.util.function.LongConsumer); + method public static java.util.stream.LongStream range(long, long); + method public static java.util.stream.LongStream rangeClosed(long, long); + method public abstract long reduce(long, java.util.function.LongBinaryOperator); + method public abstract java.util.OptionalLong reduce(java.util.function.LongBinaryOperator); + method public abstract java.util.stream.LongStream sequential(); + method public abstract java.util.stream.LongStream skip(long); + method public abstract java.util.stream.LongStream sorted(); + method public abstract java.util.Spliterator.OfLong spliterator(); + method public abstract long sum(); + method public abstract java.util.LongSummaryStatistics summaryStatistics(); + method public abstract long[] toArray(); + } + + public static abstract interface LongStream.Builder implements java.util.function.LongConsumer { + method public abstract void accept(long); + method public default java.util.stream.LongStream.Builder add(long); + method public abstract java.util.stream.LongStream build(); + } + + public abstract interface Stream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.Predicate<? super T>); + method public abstract boolean anyMatch(java.util.function.Predicate<? super T>); + method public static java.util.stream.Stream.Builder<T> builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, ? super T>, java.util.function.BiConsumer<R, R>); + method public abstract R collect(java.util.stream.Collector<? super T, A, R>); + method public static java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>); + method public abstract long count(); + method public abstract java.util.stream.Stream<T> distinct(); + method public static java.util.stream.Stream<T> empty(); + method public abstract java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>); + method public abstract java.util.Optional<T> findAny(); + method public abstract java.util.Optional<T> findFirst(); + method public abstract java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>>); + method public abstract java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream>); + method public abstract java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream>); + method public abstract java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream>); + method public abstract void forEach(java.util.function.Consumer<? super T>); + method public abstract void forEachOrdered(java.util.function.Consumer<? super T>); + method public static java.util.stream.Stream<T> generate(java.util.function.Supplier<T>); + method public static java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>); + method public abstract java.util.stream.Stream<T> limit(long); + method public abstract java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R>); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>); + method public abstract java.util.Optional<T> max(java.util.Comparator<? super T>); + method public abstract java.util.Optional<T> min(java.util.Comparator<? super T>); + method public abstract boolean noneMatch(java.util.function.Predicate<? super T>); + method public static java.util.stream.Stream<T> of(T); + method public static java.util.stream.Stream<T> of(T...); + method public abstract java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>); + method public abstract T reduce(T, java.util.function.BinaryOperator<T>); + method public abstract java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>); + method public abstract U reduce(U, java.util.function.BiFunction<U, ? super T, U>, java.util.function.BinaryOperator<U>); + method public abstract java.util.stream.Stream<T> skip(long); + method public abstract java.util.stream.Stream<T> sorted(); + method public abstract java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>); + method public abstract java.lang.Object[] toArray(); + method public abstract A[] toArray(java.util.function.IntFunction<A[]>); + } + + public static abstract interface Stream.Builder implements java.util.function.Consumer { + method public abstract void accept(T); + method public default java.util.stream.Stream.Builder<T> add(T); + method public abstract java.util.stream.Stream<T> build(); + } + + public final class StreamSupport { + method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean); + method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean); + method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean); + method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean); + method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean); + method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean); + method public static java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean); + method public static java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean); + } + +} + package java.util.zip { public class Adler32 implements java.util.zip.Checksum { diff --git a/api/test-current.txt b/api/test-current.txt index 97e3b950735f..fa2669c26435 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -944,6 +944,8 @@ package android { field public static final int popupBackground = 16843126; // 0x1010176 field public static final int popupCharacters = 16843332; // 0x1010244 field public static final int popupElevation = 16843916; // 0x101048c + field public static final int popupEnterTransition = 16844065; // 0x1010521 + field public static final int popupExitTransition = 16844066; // 0x1010522 field public static final int popupKeyboard = 16843331; // 0x1010243 field public static final int popupLayout = 16843323; // 0x101023b field public static final int popupMenuStyle = 16843520; // 0x1010300 @@ -2754,14 +2756,10 @@ package android.accessibilityservice { } public final class GestureDescription { - method public static android.accessibilityservice.GestureDescription createClick(int, int); - method public static android.accessibilityservice.GestureDescription createLongClick(int, int); - method public static android.accessibilityservice.GestureDescription createPinch(int, int, int, int, float, long); - method public static android.accessibilityservice.GestureDescription createSwipe(int, int, int, int, long); + method public static long getMaxGestureDuration(); + method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(int); method public int getStrokeCount(); - field public static final long MAX_GESTURE_DURATION_MS = 60000L; // 0xea60L - field public static final int MAX_STROKE_COUNT = 10; // 0xa } public static class GestureDescription.Builder { @@ -47269,6 +47267,8 @@ package android.widget { method public android.graphics.drawable.Drawable getBackground(); method public android.view.View getContentView(); method public float getElevation(); + method public android.transition.Transition getEnterTransition(); + method public android.transition.Transition getExitTransition(); method public int getHeight(); method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); @@ -50762,6 +50762,7 @@ package java.lang { method public float floatValue(); method public static int hashCode(double); method public int intValue(); + method public static boolean isFinite(double); method public static boolean isInfinite(double); method public boolean isInfinite(); method public static boolean isNaN(double); @@ -50843,6 +50844,7 @@ package java.lang { method public float floatValue(); method public static float intBitsToFloat(int); method public int intValue(); + method public static boolean isFinite(float); method public static boolean isInfinite(float); method public boolean isInfinite(); method public static boolean isNaN(float); @@ -57420,6 +57422,7 @@ package java.util { method public E get(int); method public boolean removeIf(java.util.function.Predicate<? super E>); method public int size(); + method public void sort(java.util.Comparator<? super E>); method public java.util.Spliterator<E> spliterator(); method public void trimToSize(); } @@ -57551,6 +57554,14 @@ package java.util { method public static java.util.Spliterator.OfLong spliterator(long[], int, int); method public static java.util.Spliterator.OfDouble spliterator(double[]); method public static java.util.Spliterator.OfDouble spliterator(double[], int, int); + method public static java.util.stream.Stream<T> stream(T[]); + method public static java.util.stream.Stream<T> stream(T[], int, int); + method public static java.util.stream.IntStream stream(int[]); + method public static java.util.stream.IntStream stream(int[], int, int); + method public static java.util.stream.LongStream stream(long[]); + method public static java.util.stream.LongStream stream(long[], int, int); + method public static java.util.stream.DoubleStream stream(double[]); + method public static java.util.stream.DoubleStream stream(double[], int, int); method public static java.lang.String toString(long[]); method public static java.lang.String toString(int[]); method public static java.lang.String toString(short[]); @@ -57710,11 +57721,13 @@ package java.util { method public abstract int hashCode(); method public abstract boolean isEmpty(); method public abstract java.util.Iterator<E> iterator(); + method public default java.util.stream.Stream<E> parallelStream(); method public abstract boolean remove(java.lang.Object); method public abstract boolean removeAll(java.util.Collection<?>); method public default boolean removeIf(java.util.function.Predicate<? super E>); method public abstract boolean retainAll(java.util.Collection<?>); method public abstract int size(); + method public default java.util.stream.Stream<E> stream(); method public abstract java.lang.Object[] toArray(); method public abstract T[] toArray(T[]); } @@ -58914,6 +58927,34 @@ package java.util { method public java.util.Spliterator<T> trySplit(); } + public final class SplittableRandom { + ctor public SplittableRandom(long); + ctor public SplittableRandom(); + method public java.util.stream.DoubleStream doubles(long); + method public java.util.stream.DoubleStream doubles(); + method public java.util.stream.DoubleStream doubles(long, double, double); + method public java.util.stream.DoubleStream doubles(double, double); + method public java.util.stream.IntStream ints(long); + method public java.util.stream.IntStream ints(); + method public java.util.stream.IntStream ints(long, int, int); + method public java.util.stream.IntStream ints(int, int); + method public java.util.stream.LongStream longs(long); + method public java.util.stream.LongStream longs(); + method public java.util.stream.LongStream longs(long, long, long); + method public java.util.stream.LongStream longs(long, long); + method public boolean nextBoolean(); + method public double nextDouble(); + method public double nextDouble(double); + method public double nextDouble(double, double); + method public int nextInt(); + method public int nextInt(int); + method public int nextInt(int, int); + method public long nextLong(); + method public long nextLong(long); + method public long nextLong(long, long); + method public java.util.SplittableRandom split(); + } + public class Stack extends java.util.Vector { ctor public Stack(); method public boolean empty(); @@ -60097,6 +60138,18 @@ package java.util.concurrent { public class ThreadLocalRandom extends java.util.Random { method public static java.util.concurrent.ThreadLocalRandom current(); + method public java.util.stream.DoubleStream doubles(long); + method public java.util.stream.DoubleStream doubles(); + method public java.util.stream.DoubleStream doubles(long, double, double); + method public java.util.stream.DoubleStream doubles(double, double); + method public java.util.stream.IntStream ints(long); + method public java.util.stream.IntStream ints(); + method public java.util.stream.IntStream ints(long, int, int); + method public java.util.stream.IntStream ints(int, int); + method public java.util.stream.LongStream longs(long); + method public java.util.stream.LongStream longs(); + method public java.util.stream.LongStream longs(long, long, long); + method public java.util.stream.LongStream longs(long, long); method public double nextDouble(double); method public double nextDouble(double, double); method public int nextInt(int, int); @@ -61498,6 +61551,292 @@ package java.util.regex { } +package java.util.stream { + + public abstract interface BaseStream implements java.lang.AutoCloseable { + method public abstract void close(); + method public abstract boolean isParallel(); + method public abstract java.util.Iterator<T> iterator(); + method public abstract S onClose(java.lang.Runnable); + method public abstract S parallel(); + method public abstract S sequential(); + method public abstract java.util.Spliterator<T> spliterator(); + method public abstract S unordered(); + } + + public abstract interface Collector { + method public abstract java.util.function.BiConsumer<A, T> accumulator(); + method public abstract java.util.Set<java.util.stream.Collector.Characteristics> characteristics(); + method public abstract java.util.function.BinaryOperator<A> combiner(); + method public abstract java.util.function.Function<A, R> finisher(); + method public static java.util.stream.Collector<T, R, R> of(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, T>, java.util.function.BinaryOperator<R>, java.util.stream.Collector.Characteristics...); + method public static java.util.stream.Collector<T, A, R> of(java.util.function.Supplier<A>, java.util.function.BiConsumer<A, T>, java.util.function.BinaryOperator<A>, java.util.function.Function<A, R>, java.util.stream.Collector.Characteristics...); + method public abstract java.util.function.Supplier<A> supplier(); + } + + public static final class Collector.Characteristics extends java.lang.Enum { + method public static java.util.stream.Collector.Characteristics valueOf(java.lang.String); + method public static final java.util.stream.Collector.Characteristics[] values(); + enum_constant public static final java.util.stream.Collector.Characteristics CONCURRENT; + enum_constant public static final java.util.stream.Collector.Characteristics IDENTITY_FINISH; + enum_constant public static final java.util.stream.Collector.Characteristics UNORDERED; + } + + public final class Collectors { + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> averagingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, A, RR> collectingAndThen(java.util.stream.Collector<T, A, R>, java.util.function.Function<R, RR>); + method public static java.util.stream.Collector<T, ?, java.lang.Long> counting(); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, java.util.List<T>>> groupingBy(java.util.function.Function<? super T, ? extends K>); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, D>> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, M> groupingBy(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, java.util.List<T>>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, D>> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, M> groupingByConcurrent(java.util.function.Function<? super T, ? extends K>, java.util.function.Supplier<M>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence); + method public static java.util.stream.Collector<java.lang.CharSequence, ?, java.lang.String> joining(java.lang.CharSequence, java.lang.CharSequence, java.lang.CharSequence); + method public static java.util.stream.Collector<T, ?, R> mapping(java.util.function.Function<? super T, ? extends U>, java.util.stream.Collector<? super U, A, R>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> maxBy(java.util.Comparator<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> minBy(java.util.Comparator<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, java.util.List<T>>> partitioningBy(java.util.function.Predicate<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.Map<java.lang.Boolean, D>> partitioningBy(java.util.function.Predicate<? super T>, java.util.stream.Collector<? super T, A, D>); + method public static java.util.stream.Collector<T, ?, T> reducing(T, java.util.function.BinaryOperator<T>); + method public static java.util.stream.Collector<T, ?, java.util.Optional<T>> reducing(java.util.function.BinaryOperator<T>); + method public static java.util.stream.Collector<T, ?, U> reducing(U, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, java.util.DoubleSummaryStatistics> summarizingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.IntSummaryStatistics> summarizingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.util.LongSummaryStatistics> summarizingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Double> summingDouble(java.util.function.ToDoubleFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Integer> summingInt(java.util.function.ToIntFunction<? super T>); + method public static java.util.stream.Collector<T, ?, java.lang.Long> summingLong(java.util.function.ToLongFunction<? super T>); + method public static java.util.stream.Collector<T, ?, C> toCollection(java.util.function.Supplier<C>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>); + method public static java.util.stream.Collector<T, ?, java.util.concurrent.ConcurrentMap<K, U>> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, M> toConcurrentMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>); + method public static java.util.stream.Collector<T, ?, java.util.List<T>> toList(); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>); + method public static java.util.stream.Collector<T, ?, java.util.Map<K, U>> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>); + method public static java.util.stream.Collector<T, ?, M> toMap(java.util.function.Function<? super T, ? extends K>, java.util.function.Function<? super T, ? extends U>, java.util.function.BinaryOperator<U>, java.util.function.Supplier<M>); + method public static java.util.stream.Collector<T, ?, java.util.Set<T>> toSet(); + } + + public abstract interface DoubleStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.DoublePredicate); + method public abstract boolean anyMatch(java.util.function.DoublePredicate); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Double> boxed(); + method public static java.util.stream.DoubleStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjDoubleConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.DoubleStream concat(java.util.stream.DoubleStream, java.util.stream.DoubleStream); + method public abstract long count(); + method public abstract java.util.stream.DoubleStream distinct(); + method public static java.util.stream.DoubleStream empty(); + method public abstract java.util.stream.DoubleStream filter(java.util.function.DoublePredicate); + method public abstract java.util.OptionalDouble findAny(); + method public abstract java.util.OptionalDouble findFirst(); + method public abstract java.util.stream.DoubleStream flatMap(java.util.function.DoubleFunction<? extends java.util.stream.DoubleStream>); + method public abstract void forEach(java.util.function.DoubleConsumer); + method public abstract void forEachOrdered(java.util.function.DoubleConsumer); + method public static java.util.stream.DoubleStream generate(java.util.function.DoubleSupplier); + method public static java.util.stream.DoubleStream iterate(double, java.util.function.DoubleUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfDouble iterator(); + method public abstract java.util.stream.DoubleStream limit(long); + method public abstract java.util.stream.DoubleStream map(java.util.function.DoubleUnaryOperator); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.DoubleToIntFunction); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.DoubleToLongFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.DoubleFunction<? extends U>); + method public abstract java.util.OptionalDouble max(); + method public abstract java.util.OptionalDouble min(); + method public abstract boolean noneMatch(java.util.function.DoublePredicate); + method public static java.util.stream.DoubleStream of(double); + method public static java.util.stream.DoubleStream of(double...); + method public abstract java.util.stream.DoubleStream parallel(); + method public abstract java.util.stream.DoubleStream peek(java.util.function.DoubleConsumer); + method public abstract double reduce(double, java.util.function.DoubleBinaryOperator); + method public abstract java.util.OptionalDouble reduce(java.util.function.DoubleBinaryOperator); + method public abstract java.util.stream.DoubleStream sequential(); + method public abstract java.util.stream.DoubleStream skip(long); + method public abstract java.util.stream.DoubleStream sorted(); + method public abstract java.util.Spliterator.OfDouble spliterator(); + method public abstract double sum(); + method public abstract java.util.DoubleSummaryStatistics summaryStatistics(); + method public abstract double[] toArray(); + } + + public static abstract interface DoubleStream.Builder implements java.util.function.DoubleConsumer { + method public abstract void accept(double); + method public default java.util.stream.DoubleStream.Builder add(double); + method public abstract java.util.stream.DoubleStream build(); + } + + public abstract interface IntStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.IntPredicate); + method public abstract boolean anyMatch(java.util.function.IntPredicate); + method public abstract java.util.stream.DoubleStream asDoubleStream(); + method public abstract java.util.stream.LongStream asLongStream(); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Integer> boxed(); + method public static java.util.stream.IntStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjIntConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.IntStream concat(java.util.stream.IntStream, java.util.stream.IntStream); + method public abstract long count(); + method public abstract java.util.stream.IntStream distinct(); + method public static java.util.stream.IntStream empty(); + method public abstract java.util.stream.IntStream filter(java.util.function.IntPredicate); + method public abstract java.util.OptionalInt findAny(); + method public abstract java.util.OptionalInt findFirst(); + method public abstract java.util.stream.IntStream flatMap(java.util.function.IntFunction<? extends java.util.stream.IntStream>); + method public abstract void forEach(java.util.function.IntConsumer); + method public abstract void forEachOrdered(java.util.function.IntConsumer); + method public static java.util.stream.IntStream generate(java.util.function.IntSupplier); + method public static java.util.stream.IntStream iterate(int, java.util.function.IntUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfInt iterator(); + method public abstract java.util.stream.IntStream limit(long); + method public abstract java.util.stream.IntStream map(java.util.function.IntUnaryOperator); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.IntToDoubleFunction); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.IntToLongFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.IntFunction<? extends U>); + method public abstract java.util.OptionalInt max(); + method public abstract java.util.OptionalInt min(); + method public abstract boolean noneMatch(java.util.function.IntPredicate); + method public static java.util.stream.IntStream of(int); + method public static java.util.stream.IntStream of(int...); + method public abstract java.util.stream.IntStream parallel(); + method public abstract java.util.stream.IntStream peek(java.util.function.IntConsumer); + method public static java.util.stream.IntStream range(int, int); + method public static java.util.stream.IntStream rangeClosed(int, int); + method public abstract int reduce(int, java.util.function.IntBinaryOperator); + method public abstract java.util.OptionalInt reduce(java.util.function.IntBinaryOperator); + method public abstract java.util.stream.IntStream sequential(); + method public abstract java.util.stream.IntStream skip(long); + method public abstract java.util.stream.IntStream sorted(); + method public abstract java.util.Spliterator.OfInt spliterator(); + method public abstract int sum(); + method public abstract java.util.IntSummaryStatistics summaryStatistics(); + method public abstract int[] toArray(); + } + + public static abstract interface IntStream.Builder implements java.util.function.IntConsumer { + method public abstract void accept(int); + method public default java.util.stream.IntStream.Builder add(int); + method public abstract java.util.stream.IntStream build(); + } + + public abstract interface LongStream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.LongPredicate); + method public abstract boolean anyMatch(java.util.function.LongPredicate); + method public abstract java.util.stream.DoubleStream asDoubleStream(); + method public abstract java.util.OptionalDouble average(); + method public abstract java.util.stream.Stream<java.lang.Long> boxed(); + method public static java.util.stream.LongStream.Builder builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.ObjLongConsumer<R>, java.util.function.BiConsumer<R, R>); + method public static java.util.stream.LongStream concat(java.util.stream.LongStream, java.util.stream.LongStream); + method public abstract long count(); + method public abstract java.util.stream.LongStream distinct(); + method public static java.util.stream.LongStream empty(); + method public abstract java.util.stream.LongStream filter(java.util.function.LongPredicate); + method public abstract java.util.OptionalLong findAny(); + method public abstract java.util.OptionalLong findFirst(); + method public abstract java.util.stream.LongStream flatMap(java.util.function.LongFunction<? extends java.util.stream.LongStream>); + method public abstract void forEach(java.util.function.LongConsumer); + method public abstract void forEachOrdered(java.util.function.LongConsumer); + method public static java.util.stream.LongStream generate(java.util.function.LongSupplier); + method public static java.util.stream.LongStream iterate(long, java.util.function.LongUnaryOperator); + method public abstract java.util.PrimitiveIterator.OfLong iterator(); + method public abstract java.util.stream.LongStream limit(long); + method public abstract java.util.stream.LongStream map(java.util.function.LongUnaryOperator); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.LongToDoubleFunction); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.LongToIntFunction); + method public abstract java.util.stream.Stream<U> mapToObj(java.util.function.LongFunction<? extends U>); + method public abstract java.util.OptionalLong max(); + method public abstract java.util.OptionalLong min(); + method public abstract boolean noneMatch(java.util.function.LongPredicate); + method public static java.util.stream.LongStream of(long); + method public static java.util.stream.LongStream of(long...); + method public abstract java.util.stream.LongStream parallel(); + method public abstract java.util.stream.LongStream peek(java.util.function.LongConsumer); + method public static java.util.stream.LongStream range(long, long); + method public static java.util.stream.LongStream rangeClosed(long, long); + method public abstract long reduce(long, java.util.function.LongBinaryOperator); + method public abstract java.util.OptionalLong reduce(java.util.function.LongBinaryOperator); + method public abstract java.util.stream.LongStream sequential(); + method public abstract java.util.stream.LongStream skip(long); + method public abstract java.util.stream.LongStream sorted(); + method public abstract java.util.Spliterator.OfLong spliterator(); + method public abstract long sum(); + method public abstract java.util.LongSummaryStatistics summaryStatistics(); + method public abstract long[] toArray(); + } + + public static abstract interface LongStream.Builder implements java.util.function.LongConsumer { + method public abstract void accept(long); + method public default java.util.stream.LongStream.Builder add(long); + method public abstract java.util.stream.LongStream build(); + } + + public abstract interface Stream implements java.util.stream.BaseStream { + method public abstract boolean allMatch(java.util.function.Predicate<? super T>); + method public abstract boolean anyMatch(java.util.function.Predicate<? super T>); + method public static java.util.stream.Stream.Builder<T> builder(); + method public abstract R collect(java.util.function.Supplier<R>, java.util.function.BiConsumer<R, ? super T>, java.util.function.BiConsumer<R, R>); + method public abstract R collect(java.util.stream.Collector<? super T, A, R>); + method public static java.util.stream.Stream<T> concat(java.util.stream.Stream<? extends T>, java.util.stream.Stream<? extends T>); + method public abstract long count(); + method public abstract java.util.stream.Stream<T> distinct(); + method public static java.util.stream.Stream<T> empty(); + method public abstract java.util.stream.Stream<T> filter(java.util.function.Predicate<? super T>); + method public abstract java.util.Optional<T> findAny(); + method public abstract java.util.Optional<T> findFirst(); + method public abstract java.util.stream.Stream<R> flatMap(java.util.function.Function<? super T, ? extends java.util.stream.Stream<? extends R>>); + method public abstract java.util.stream.DoubleStream flatMapToDouble(java.util.function.Function<? super T, ? extends java.util.stream.DoubleStream>); + method public abstract java.util.stream.IntStream flatMapToInt(java.util.function.Function<? super T, ? extends java.util.stream.IntStream>); + method public abstract java.util.stream.LongStream flatMapToLong(java.util.function.Function<? super T, ? extends java.util.stream.LongStream>); + method public abstract void forEach(java.util.function.Consumer<? super T>); + method public abstract void forEachOrdered(java.util.function.Consumer<? super T>); + method public static java.util.stream.Stream<T> generate(java.util.function.Supplier<T>); + method public static java.util.stream.Stream<T> iterate(T, java.util.function.UnaryOperator<T>); + method public abstract java.util.stream.Stream<T> limit(long); + method public abstract java.util.stream.Stream<R> map(java.util.function.Function<? super T, ? extends R>); + method public abstract java.util.stream.DoubleStream mapToDouble(java.util.function.ToDoubleFunction<? super T>); + method public abstract java.util.stream.IntStream mapToInt(java.util.function.ToIntFunction<? super T>); + method public abstract java.util.stream.LongStream mapToLong(java.util.function.ToLongFunction<? super T>); + method public abstract java.util.Optional<T> max(java.util.Comparator<? super T>); + method public abstract java.util.Optional<T> min(java.util.Comparator<? super T>); + method public abstract boolean noneMatch(java.util.function.Predicate<? super T>); + method public static java.util.stream.Stream<T> of(T); + method public static java.util.stream.Stream<T> of(T...); + method public abstract java.util.stream.Stream<T> peek(java.util.function.Consumer<? super T>); + method public abstract T reduce(T, java.util.function.BinaryOperator<T>); + method public abstract java.util.Optional<T> reduce(java.util.function.BinaryOperator<T>); + method public abstract U reduce(U, java.util.function.BiFunction<U, ? super T, U>, java.util.function.BinaryOperator<U>); + method public abstract java.util.stream.Stream<T> skip(long); + method public abstract java.util.stream.Stream<T> sorted(); + method public abstract java.util.stream.Stream<T> sorted(java.util.Comparator<? super T>); + method public abstract java.lang.Object[] toArray(); + method public abstract A[] toArray(java.util.function.IntFunction<A[]>); + } + + public static abstract interface Stream.Builder implements java.util.function.Consumer { + method public abstract void accept(T); + method public default java.util.stream.Stream.Builder<T> add(T); + method public abstract java.util.stream.Stream<T> build(); + } + + public final class StreamSupport { + method public static java.util.stream.DoubleStream doubleStream(java.util.Spliterator.OfDouble, boolean); + method public static java.util.stream.DoubleStream doubleStream(java.util.function.Supplier<? extends java.util.Spliterator.OfDouble>, int, boolean); + method public static java.util.stream.IntStream intStream(java.util.Spliterator.OfInt, boolean); + method public static java.util.stream.IntStream intStream(java.util.function.Supplier<? extends java.util.Spliterator.OfInt>, int, boolean); + method public static java.util.stream.LongStream longStream(java.util.Spliterator.OfLong, boolean); + method public static java.util.stream.LongStream longStream(java.util.function.Supplier<? extends java.util.Spliterator.OfLong>, int, boolean); + method public static java.util.stream.Stream<T> stream(java.util.Spliterator<T>, boolean); + method public static java.util.stream.Stream<T> stream(java.util.function.Supplier<? extends java.util.Spliterator<T>>, int, boolean); + } + +} + package java.util.zip { public class Adler32 implements java.util.zip.Checksum { diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index ac3b8e360b2e..ddc5b0c7eb08 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -851,6 +851,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnificationScale(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain scale", re); + re.rethrowFromSystemServer(); } } return 1.0f; @@ -879,6 +880,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnificationCenterX(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain center X", re); + re.rethrowFromSystemServer(); } } return 0.0f; @@ -907,6 +909,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnificationCenterY(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain center Y", re); + re.rethrowFromSystemServer(); } } return 0.0f; @@ -933,6 +936,7 @@ public abstract class AccessibilityService extends Service { return connection.getMagnifiedRegion(); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to obtain magnified region", re); + re.rethrowFromSystemServer(); } } return Region.obtain(); @@ -961,6 +965,7 @@ public abstract class AccessibilityService extends Service { return connection.resetMagnification(animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to reset", re); + re.rethrowFromSystemServer(); } } return false; @@ -989,6 +994,7 @@ public abstract class AccessibilityService extends Service { scale, Float.NaN, Float.NaN, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to set scale", re); + re.rethrowFromSystemServer(); } } return false; @@ -1020,6 +1026,7 @@ public abstract class AccessibilityService extends Service { Float.NaN, centerX, centerY, animate); } catch (RemoteException re) { Log.w(LOG_TAG, "Failed to set center", re); + re.rethrowFromSystemServer(); } } return false; @@ -1254,10 +1261,7 @@ public abstract class AccessibilityService extends Service { Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re); re.rethrowFromSystemServer(); } - } else { - throw new RuntimeException("AccessibilityServiceConnection is null"); } - return false; } @@ -1301,6 +1305,7 @@ public abstract class AccessibilityService extends Service { return connection.performGlobalAction(action); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while calling performGlobalAction", re); + re.rethrowFromSystemServer(); } } return false; @@ -1349,6 +1354,7 @@ public abstract class AccessibilityService extends Service { return connection.getServiceInfo(); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while getting AccessibilityServiceInfo", re); + re.rethrowFromSystemServer(); } } return null; @@ -1382,6 +1388,7 @@ public abstract class AccessibilityService extends Service { AccessibilityInteractionClient.getInstance().clearCache(); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); + re.rethrowFromSystemServer(); } } } diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index 14aabcf69a6c..7a0c89b75a4a 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -43,135 +43,32 @@ import java.util.List; */ public final class GestureDescription { /** Gestures may contain no more than this many strokes */ - public static final int MAX_STROKE_COUNT = 10; + private static final int MAX_STROKE_COUNT = 10; /** * Upper bound on total gesture duration. Nearly all gestures will be much shorter. */ - public static final long MAX_GESTURE_DURATION_MS = 60 * 1000; + private static final long MAX_GESTURE_DURATION_MS = 60 * 1000; private final List<StrokeDescription> mStrokes = new ArrayList<>(); private final float[] mTempPos = new float[2]; /** - * Create a description of a click gesture + * Get the upper limit for the number of strokes a gesture may contain. * - * @param x The x coordinate to click. Must not be negative. - * @param y The y coordinate to click. Must not be negative. - * - * @return A description of a click at (x, y) + * @return The maximum number of strokes. */ - public static GestureDescription createClick(@IntRange(from = 0) int x, - @IntRange(from = 0) int y) { - Path clickPath = new Path(); - clickPath.moveTo(x, y); - clickPath.lineTo(x + 1, y); - return new GestureDescription( - new StrokeDescription(clickPath, 0, ViewConfiguration.getTapTimeout())); + public static int getMaxStrokeCount() { + return MAX_STROKE_COUNT; } /** - * Create a description of a long click gesture - * - * @param x The x coordinate to click. Must not be negative. - * @param y The y coordinate to click. Must not be negative. + * Get the upper limit on a gesture's duration. * - * @return A description of a click at (x, y) + * @return The maximum duration in milliseconds. */ - public static GestureDescription createLongClick(@IntRange(from = 0) int x, - @IntRange(from = 0) int y) { - Path clickPath = new Path(); - clickPath.moveTo(x, y); - clickPath.lineTo(x + 1, y); - int longPressTime = ViewConfiguration.getLongPressTimeout(); - return new GestureDescription( - new StrokeDescription(clickPath, 0, longPressTime + (longPressTime / 2))); - } - - /** - * Create a description of a swipe gesture - * - * @param startX The x coordinate of the starting point. Must not be negative. - * @param startY The y coordinate of the starting point. Must not be negative. - * @param endX The x coordinate of the ending point. Must not be negative. - * @param endY The y coordinate of the ending point. Must not be negative. - * @param duration The time, in milliseconds, to complete the gesture. Must not be negative. - * - * @return A description of a swipe from ({@code startX}, {@code startY}) to - * ({@code endX}, {@code endY}) that takes {@code duration} milliseconds. Returns {@code null} - * if the path specified for the swipe is invalid. - */ - public static GestureDescription createSwipe(@IntRange(from = 0) int startX, - @IntRange(from = 0) int startY, - @IntRange(from = 0) int endX, - @IntRange(from = 0) int endY, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) { - Path swipePath = new Path(); - swipePath.moveTo(startX, startY); - swipePath.lineTo(endX, endY); - return new GestureDescription(new StrokeDescription(swipePath, 0, duration)); - } - - /** - * Create a description for a pinch (or zoom) gesture. - * - * @param centerX The x coordinate of the center of the pinch. Must not be negative. - * @param centerY The y coordinate of the center of the pinch. Must not be negative. - * @param startSpacing The spacing of the touch points at the beginning of the gesture. Must not - * be negative. - * @param endSpacing The spacing of the touch points at the end of the gesture. Must not be - * negative. - * @param orientation The angle, in degrees, of the gesture. 0 represents a horizontal pinch - * @param duration The time, in milliseconds, to complete the gesture. Must not be negative. - * - * @return A description of a pinch centered at ({code centerX}, {@code centerY}) that starts - * with the touch points spaced by {@code startSpacing} and ends with them spaced by - * {@code endSpacing} that lasts {@code duration} ms. Returns {@code null} if either path - * specified for the pinch is invalid. - */ - public static GestureDescription createPinch(@IntRange(from = 0) int centerX, - @IntRange(from = 0) int centerY, - @IntRange(from = 0) int startSpacing, - @IntRange(from = 0) int endSpacing, - float orientation, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) { - if ((startSpacing < 0) || (endSpacing < 0)) { - throw new IllegalArgumentException("Pinch spacing cannot be negative"); - } - float[] startPoint1 = new float[2]; - float[] endPoint1 = new float[2]; - float[] startPoint2 = new float[2]; - float[] endPoint2 = new float[2]; - - /* Build points for a horizontal gesture centered at the origin */ - startPoint1[0] = startSpacing / 2; - startPoint1[1] = 0; - endPoint1[0] = endSpacing / 2; - endPoint1[1] = 0; - startPoint2[0] = -startSpacing / 2; - startPoint2[1] = 0; - endPoint2[0] = -endSpacing / 2; - endPoint2[1] = 0; - - /* Rotate and translate the points */ - Matrix matrix = new Matrix(); - matrix.setRotate(orientation); - matrix.postTranslate(centerX, centerY); - matrix.mapPoints(startPoint1); - matrix.mapPoints(endPoint1); - matrix.mapPoints(startPoint2); - matrix.mapPoints(endPoint2); - - Path path1 = new Path(); - path1.moveTo(startPoint1[0], startPoint1[1]); - path1.lineTo(endPoint1[0], endPoint1[1]); - Path path2 = new Path(); - path2.moveTo(startPoint2[0], startPoint2[1]); - path2.lineTo(endPoint2[0], endPoint2[1]); - - return new GestureDescription(Arrays.asList( - new StrokeDescription(path1, 0, duration), - new StrokeDescription(path2, 0, duration))); + public static long getMaxGestureDuration() { + return MAX_GESTURE_DURATION_MS; } private GestureDescription() {} @@ -180,10 +77,6 @@ public final class GestureDescription { mStrokes.addAll(strokes); } - private GestureDescription(StrokeDescription stroke) { - mStrokes.add(stroke); - } - /** * Get the number of stroke in the gesture. * @@ -278,21 +171,23 @@ public final class GestureDescription { */ public Builder addStroke(@NonNull StrokeDescription strokeDescription) { if (mStrokes.size() >= MAX_STROKE_COUNT) { - throw new RuntimeException("Attempting to add too many strokes to a gesture"); + throw new IllegalStateException( + "Attempting to add too many strokes to a gesture"); } mStrokes.add(strokeDescription); if (getTotalDuration(mStrokes) > MAX_GESTURE_DURATION_MS) { mStrokes.remove(strokeDescription); - throw new RuntimeException("Gesture would exceed maximum duration with new stroke"); + throw new IllegalStateException( + "Gesture would exceed maximum duration with new stroke"); } return this; } public GestureDescription build() { if (mStrokes.size() == 0) { - throw new RuntimeException("Gestures must have at least one stroke"); + throw new IllegalStateException("Gestures must have at least one stroke"); } return new GestureDescription(mStrokes); } @@ -317,8 +212,8 @@ public final class GestureDescription { * Must not be negative. */ public StrokeDescription(@NonNull Path path, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long startTime, - @IntRange(from = 0, to = MAX_GESTURE_DURATION_MS) long duration) { + @IntRange(from = 0) long startTime, + @IntRange(from = 0) long duration) { if (duration <= 0) { throw new IllegalArgumentException("Duration must be positive"); } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 33fd1dbda63d..13e8e75a4db7 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.IntDef; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -49,6 +50,8 @@ import android.view.Window; import com.android.internal.content.ReferrerIntent; import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -78,7 +81,15 @@ public class Instrumentation { public static final String REPORT_KEY_STREAMRESULT = "stream"; private static final String TAG = "Instrumentation"; - + + /** + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({0, UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}) + public @interface UiAutomationFlags {}; + + private final Object mSync = new Object(); private ActivityThread mThread = null; private MessageQueue mMessageQueue = null; @@ -1876,7 +1887,7 @@ public class Instrumentation { * * @see UiAutomation */ - public UiAutomation getUiAutomation(int flags) { + public UiAutomation getUiAutomation(@UiAutomationFlags int flags) { if (mUiAutomationConnection != null) { if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) { mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(), diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 8724d5ece5ca..d865f34568fa 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -160,7 +160,8 @@ public class LauncherApps { * as defined in {@link #hasShortcutHostPermission()}, will receive it. * * @param packageName The name of the package that has the shortcuts. - * @param shortcuts all shortcuts from the package (dynamic and/or pinned). + * @param shortcuts all shortcuts from the package (dynamic and/or pinned). Only "key" + * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}. * @param user The UserHandle of the profile that generated the change. */ public void onShortcutsChanged(@NonNull String packageName, diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index b5c1f30f02ec..ae75e3f9a156 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -46,8 +46,6 @@ import java.lang.annotation.RetentionPolicy; * if necessary, and persisted. * * We will disallow byte[] icons, because they can easily go over binder size limit. - * - * TODO Move save/load to this class */ public class ShortcutInfo implements Parcelable { /* @hide */ diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 04e64af8c25e..ffd9d8969e65 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -1099,6 +1099,11 @@ public final class CameraManager { if (oldHandler == null) { updateCallbackLocked(callback, handler); } + + // If not connected to camera service, schedule a reconnect to camera service. + if (mCameraService == null) { + scheduleCameraServiceReconnectionLocked(); + } } } @@ -1123,6 +1128,11 @@ public final class CameraManager { if (oldHandler == null) { updateTorchCallbackLocked(callback, handler); } + + // If not connected to camera service, schedule a reconnect to camera service. + if (mCameraService == null) { + scheduleCameraServiceReconnectionLocked(); + } } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e1c7ad77bd25..6df2a9d693d3 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -477,10 +477,10 @@ public abstract class BatteryStats implements Parcelable { * also be bumped. */ static final String[] USER_ACTIVITY_TYPES = { - "other", "button", "touch" + "other", "button", "touch", "accessibility" }; - public static final int NUM_USER_ACTIVITY_TYPES = 3; + public static final int NUM_USER_ACTIVITY_TYPES = 4; public abstract void noteUserActivityLocked(int type); public abstract boolean hasUserActivity(); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 2a4507ca027e..cb4caea93adb 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -318,6 +318,13 @@ public final class PowerManager { public static final int USER_ACTIVITY_EVENT_TOUCH = 2; /** + * User activity event type: Accessibility taking action on behalf of user. + * @hide + */ + @SystemApi + public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; + + /** * User activity flag: If already dimmed, extend the dim timeout * but do not brighten. This flag is useful for keeping the screen on * a little longer without causing a visible change such as when diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java index def303acf94c..b06d503a209d 100644 --- a/core/java/android/provider/BlockedNumberContract.java +++ b/core/java/android/provider/BlockedNumberContract.java @@ -44,7 +44,8 @@ import android.os.Bundle; * Only the system, the default SMS application, and the default phone app * (See {@link android.telecom.TelecomManager#getDefaultDialerPackage()}), and carrier apps * (See {@link android.service.carrier.CarrierService}) can read, and write to the blockednumber - * provider. + * provider. However, {@link #canCurrentUserBlockNumbers(Context)} can be accessed by any + * application. * </p> * * <h3> Data </h3> diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index aa11e3f96ef5..816d9c48539e 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4994,6 +4994,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (child.hasFocus()) { requestChildFocus(child, child.findFocus()); } + dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE + && isShown()); } /** diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl index 5697dfc0188c..9434f0ccac4e 100644 --- a/core/java/android/webkit/IWebViewUpdateService.aidl +++ b/core/java/android/webkit/IWebViewUpdateService.aidl @@ -54,6 +54,11 @@ interface IWebViewUpdateService { WebViewProviderInfo[] getValidWebViewPackages(); /** + * Fetch all packages that could potentially implement WebView. + */ + WebViewProviderInfo[] getAllWebViewPackages(); + + /** * Used by DevelopmentSetting to get the name of the WebView provider currently in use. */ String getCurrentWebViewPackageName(); diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 0751ab009c80..f1bf890b3c86 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -103,8 +103,7 @@ public final class WebViewFactory { public static final int LIBLOAD_FAILED_JNI_CALL = 7; // more error codes for waiting for WebView preparation - public static final int LIBLOAD_WEBVIEW_BEING_REPLACED = 8; - public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 9; + public static final int LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN = 8; // error for namespace lookup public static final int LIBLOAD_FAILED_TO_FIND_NAMESPACE = 10; @@ -115,8 +114,6 @@ public final class WebViewFactory { return "Time out waiting for Relro files being created"; case LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES: return "No WebView installed"; - case LIBLOAD_WEBVIEW_BEING_REPLACED: - return "Time out waiting for WebView to be replaced"; case LIBLOAD_FAILED_WAITING_FOR_WEBVIEW_REASON_UNKNOWN: return "Crashed for unknown reason"; } @@ -131,98 +128,9 @@ public final class WebViewFactory { public MissingWebViewPackageException(Exception e) { super(e); } } - private static String TAG_START = "webviewproviders"; - private static String TAG_WEBVIEW_PROVIDER = "webviewprovider"; - private static String TAG_PACKAGE_NAME = "packageName"; - private static String TAG_DESCRIPTION = "description"; - // Whether or not the provider must be explicitly chosen by the user to be used. - private static String TAG_AVAILABILITY = "availableByDefault"; - private static String TAG_SIGNATURE = "signature"; - private static String TAG_FALLBACK = "isFallback"; - - /** - * Reads all signatures at the current depth (within the current provider) from the XML parser. - */ - private static String[] readSignatures(XmlResourceParser parser) throws IOException, - XmlPullParserException { - List<String> signatures = new ArrayList<String>(); - int outerDepth = parser.getDepth(); - while(XmlUtils.nextElementWithin(parser, outerDepth)) { - if (parser.getName().equals(TAG_SIGNATURE)) { - // Parse the value within the signature tag - String signature = parser.nextText(); - signatures.add(signature); - } else { - Log.e(LOGTAG, "Found an element in a webview provider that is not a signature"); - } - } - return signatures.toArray(new String[signatures.size()]); - } - - /** - * Returns all packages declared in the framework resources as potential WebView providers. - * @hide - * */ - public static WebViewProviderInfo[] getWebViewPackages() { - int numFallbackPackages = 0; - XmlResourceParser parser = null; - List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>(); - try { - parser = AppGlobals.getInitialApplication().getResources().getXml( - com.android.internal.R.xml.config_webview_packages); - XmlUtils.beginDocument(parser, TAG_START); - while(true) { - XmlUtils.nextElement(parser); - String element = parser.getName(); - if (element == null) { - break; - } - if (element.equals(TAG_WEBVIEW_PROVIDER)) { - String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); - if (packageName == null) { - throw new MissingWebViewPackageException( - "WebView provider in framework resources missing package name"); - } - String description = parser.getAttributeValue(null, TAG_DESCRIPTION); - if (description == null) { - throw new MissingWebViewPackageException( - "WebView provider in framework resources missing description"); - } - boolean availableByDefault = "true".equals( - parser.getAttributeValue(null, TAG_AVAILABILITY)); - boolean isFallback = "true".equals( - parser.getAttributeValue(null, TAG_FALLBACK)); - WebViewProviderInfo currentProvider = - new WebViewProviderInfo(packageName, description, availableByDefault, - isFallback, readSignatures(parser)); - if (currentProvider.isFallbackPackage()) { - numFallbackPackages++; - if (numFallbackPackages > 1) { - throw new AndroidRuntimeException( - "There can be at most one webview fallback package."); - } - } - webViewProviders.add(currentProvider); - } - else { - Log.e(LOGTAG, "Found an element that is not a webview provider"); - } - } - } catch(XmlPullParserException e) { - throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); - } catch(IOException e) { - throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); - } finally { - if (parser != null) parser.close(); - } - return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); - } - - // TODO (gsennton) remove when committing webview xts test change public static String getWebViewPackageName() { - WebViewProviderInfo[] providers = getWebViewPackages(); - return providers[0].packageName; + return null; } /** diff --git a/core/java/android/webkit/WebViewProviderInfo.java b/core/java/android/webkit/WebViewProviderInfo.java index 64c2caa58fd5..75ccf355ecd7 100644 --- a/core/java/android/webkit/WebViewProviderInfo.java +++ b/core/java/android/webkit/WebViewProviderInfo.java @@ -150,6 +150,8 @@ public class WebViewProviderInfo implements Parcelable { private WebViewProviderInfo(Parcel in) { packageName = in.readString(); description = in.readString(); + availableByDefault = (in.readInt() > 0); + isFallback = (in.readInt() > 0); signatures = in.createStringArray(); packageInfo = null; } @@ -163,6 +165,8 @@ public class WebViewProviderInfo implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(packageName); out.writeString(description); + out.writeInt(availableByDefault ? 1 : 0); + out.writeInt(isFallback ? 1 : 0); out.writeStringArray(signatures); } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 4bcb40655772..942cbcbe3701 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1827,7 +1827,7 @@ public class Editor { return; } - Layout layout = getActiveLayout(); + Layout layout = mTextView.getLayout(); final int offset = mTextView.getSelectionStart(); final int line = layout.getLineForOffset(offset); final int top = layout.getLineTop(line); @@ -2192,27 +2192,35 @@ public class Editor { mCursorDrawable[cursorIndex] = mTextView.getContext().getDrawable( mTextView.mCursorDrawableRes); final Drawable drawable = mCursorDrawable[cursorIndex]; - final int left = clampCursorHorizontalPosition(drawable, horizontal); + final int left = clampHorizontalPosition(drawable, horizontal); final int width = drawable.getIntrinsicWidth(); drawable.setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } /** - * Return clamped position for the cursor. If the cursor is within the boundaries of the view, - * then it is offset with the left padding of the cursor drawable. If the cursor is at + * Return clamped position for the drawable. If the drawable is within the boundaries of the + * view, then it is offset with the left padding of the cursor drawable. If the drawable is at * the beginning or the end of the text then its drawable edge is aligned with left or right of - * the view boundary. + * the view boundary. If the drawable is null, horizontal parameter is aligned to left or right + * of the view. * - * @param drawable Cursor drawable. - * @param horizontal Horizontal position for the cursor. - * @return The clamped horizontal position for the cursor. + * @param drawable Drawable. Can be null. + * @param horizontal Horizontal position for the drawable. + * @return The clamped horizontal position for the drawable. */ - private final int clampCursorHorizontalPosition(final Drawable drawable, float - horizontal) { + private int clampHorizontalPosition(@Nullable final Drawable drawable, float horizontal) { horizontal = Math.max(0.5f, horizontal - 0.5f); if (mTempRect == null) mTempRect = new Rect(); - drawable.getPadding(mTempRect); + + int drawableWidth = 0; + if (drawable != null) { + drawable.getPadding(mTempRect); + drawableWidth = drawable.getIntrinsicWidth(); + } else { + mTempRect.setEmpty(); + } + int scrollX = mTextView.getScrollX(); float horizontalDiff = horizontal - scrollX; int viewClippedWidth = mTextView.getWidth() - mTextView.getCompoundPaddingLeft() @@ -2221,9 +2229,11 @@ public class Editor { final int left; if (horizontalDiff >= (viewClippedWidth - 1f)) { // at the rightmost position - final int cursorWidth = drawable.getIntrinsicWidth(); - left = viewClippedWidth + scrollX - (cursorWidth - mTempRect.right); - } else if (Math.abs(horizontalDiff) <= 1f) { + left = viewClippedWidth + scrollX - (drawableWidth - mTempRect.right); + } else if (Math.abs(horizontalDiff) <= 1f || + (TextUtils.isEmpty(mTextView.getText()) + && (TextView.VERY_WIDE - scrollX) <= (viewClippedWidth + 1f) + && horizontal <= 1f)) { // at the leftmost position left = scrollX - mTempRect.left; } else { @@ -3772,10 +3782,10 @@ public class Editor { + mHandleHeight); } else { // We have a single cursor. - Layout layout = getActiveLayout(); + Layout layout = mTextView.getLayout(); int line = layout.getLineForOffset(mTextView.getSelectionStart()); - float primaryHorizontal = - layout.getPrimaryHorizontal(mTextView.getSelectionStart()); + float primaryHorizontal = clampHorizontalPosition(null, + layout.getPrimaryHorizontal(mTextView.getSelectionStart())); mSelectionBounds.set( primaryHorizontal, layout.getLineTop(line), @@ -4152,7 +4162,7 @@ public class Editor { prepareCursorControllers(); return; } - layout = getActiveLayout(); + layout = mTextView.getLayout(); boolean offsetChanged = offset != mPreviousOffset; if (offsetChanged || parentScrolled) { @@ -4322,19 +4332,6 @@ public class Editor { public void onDetached() {} } - /** - * Returns the active layout (hint or text layout). Note that the text layout can be null. - */ - private Layout getActiveLayout() { - Layout layout = mTextView.getLayout(); - Layout hintLayout = mTextView.getHintLayout(); - if (TextUtils.isEmpty(layout.getText()) && hintLayout != null && - !TextUtils.isEmpty(hintLayout.getText())) { - layout = hintLayout; - } - return layout; - } - private class InsertionHandleView extends HandleView { private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000; private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds @@ -4431,7 +4428,7 @@ public class Editor { final Drawable drawable = mCursorCount > 0 ? mCursorDrawable[0] : null; if (drawable != null) { final float horizontal = layout.getPrimaryHorizontal(offset); - return clampCursorHorizontalPosition(drawable, horizontal) + mTempRect.left; + return clampHorizontalPosition(drawable, horizontal) + mTempRect.left; } return super.getCursorHorizontalPosition(layout, offset); } diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 04d344f0caa6..222a040d2b3c 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -913,7 +913,9 @@ public class ImageView extends View { if (mDrawable != null) { mDrawable.setCallback(null); unscheduleDrawable(mDrawable); - mDrawable.setVisible(false, false); + if (isAttachedToWindow()) { + mDrawable.setVisible(false, false); + } } mDrawable = d; @@ -924,8 +926,9 @@ public class ImageView extends View { if (d.isStateful()) { d.setState(getDrawableState()); } - d.setVisible(isAttachedToWindow() && getWindowVisibility() == VISIBLE && isShown(), - true); + if (isAttachedToWindow()) { + d.setVisible(getWindowVisibility() == VISIBLE && isShown(), true); + } d.setLevel(mLevel); mDrawableWidth = d.getIntrinsicWidth(); mDrawableHeight = d.getIntrinsicHeight(); diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index e0ef86c26b2b..9e8f778f1819 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -869,10 +869,10 @@ public class LinearLayout extends ViewGroup { // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds. If we skipped // measurement on any children, we need to measure them now. - final int delta = heightSize - mTotalLength + int remainingExcess = heightSize - mTotalLength + (mAllowInconsistentMeasurement ? 0 : consumedExcessSpace); - if (skippedMeasure || delta != 0 && totalWeight > 0.0f) { - final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; + if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) { + float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; mTotalLength = 0; @@ -883,9 +883,12 @@ public class LinearLayout extends ViewGroup { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final float childExtra = lp.weight; - if (childExtra > 0) { - final int share = (int) (childExtra * delta / weightSum); + final float childWeight = lp.weight; + if (childWeight > 0) { + final int share = (int) (childWeight * remainingExcess / remainingWeightSum); + remainingExcess -= share; + remainingWeightSum -= childWeight; + final int childHeight; if (lp.height == 0 && (!mAllowInconsistentMeasurement || heightMode == MeasureSpec.EXACTLY)) { @@ -1244,10 +1247,10 @@ public class LinearLayout extends ViewGroup { // Either expand children with weight to take up available space or // shrink them if they extend beyond our current bounds. If we skipped // measurement on any children, we need to measure them now. - final int delta = widthSize - mTotalLength + int remainingExcess = widthSize - mTotalLength + (mAllowInconsistentMeasurement ? 0 : usedExcessSpace); - if (skippedMeasure || delta != 0 && totalWeight > 0.0f) { - final float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; + if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) { + float remainingWeightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight; maxAscent[0] = maxAscent[1] = maxAscent[2] = maxAscent[3] = -1; maxDescent[0] = maxDescent[1] = maxDescent[2] = maxDescent[3] = -1; @@ -1262,9 +1265,12 @@ public class LinearLayout extends ViewGroup { } final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final float childExtra = lp.weight; - if (childExtra > 0) { - final int share = (int) (childExtra * delta / weightSum); + final float childWeight = lp.weight; + if (childWeight > 0) { + final int share = (int) (childWeight * remainingExcess / remainingWeightSum); + remainingExcess -= share; + remainingWeightSum -= childWeight; + final int childWidth; if (lp.width == 0 && (!mAllowInconsistentMeasurement || widthMode == MeasureSpec.EXACTLY)) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index dcadb6af7320..36e0c77613aa 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -879,12 +879,13 @@ public class ListPopupWindow implements ShowableListMenu { /** * Filter key down events. By forwarding key down events to this function, * views using non-modal ListPopupWindow can have it handle key selection of items. - * + * * @param keyCode keyCode param passed to the host view's onKeyDown * @param event event param passed to the host view's onKeyDown * @return true if the event was handled, false if it was ignored. - * + * * @see #setModal(boolean) + * @see #onKeyUp(int, KeyEvent) */ public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { // when the drop down is shown, we drive it directly @@ -972,14 +973,15 @@ public class ListPopupWindow implements ShowableListMenu { } /** - * Filter key down events. By forwarding key up events to this function, + * Filter key up events. By forwarding key up events to this function, * views using non-modal ListPopupWindow can have it handle key selection of items. - * + * * @param keyCode keyCode param passed to the host view's onKeyUp * @param event event param passed to the host view's onKeyUp * @return true if the event was handled, false if it was ignored. - * + * * @see #setModal(boolean) + * @see #onKeyDown(int, KeyEvent) */ public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) { @@ -998,11 +1000,11 @@ public class ListPopupWindow implements ShowableListMenu { * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)} * events to this function, views using ListPopupWindow can have it dismiss the popup * when the back key is pressed. - * + * * @param keyCode keyCode param passed to the host view's onKeyPreIme * @param event event param passed to the host view's onKeyPreIme * @return true if the event was handled, false if it was ignored. - * + * * @see #setModal(boolean) */ public boolean onKeyPreIme(int keyCode, @NonNull KeyEvent event) { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index dd6a41f9d209..a1417f0cbb68 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -16,12 +16,10 @@ package android.widget; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; - import com.android.internal.R; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.content.res.TypedArray; import android.graphics.PixelFormat; @@ -54,10 +52,46 @@ import android.view.WindowManager.LayoutParams; import java.lang.ref.WeakReference; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; + /** - * <p>A popup window that can be used to display an arbitrary view. The popup - * window is a floating container that appears on top of the current - * activity.</p> + * <p> + * This class represents a popup window that can be used to display an + * arbitrary view. The popup window is a floating container that appears on top + * of the current activity. + * </p> + * <a name="Animation"></a> + * <h3>Animation</h3> + * <p> + * On all versions of Android, popup window enter and exit animations may be + * specified by calling {@link #setAnimationStyle(int)} and passing the + * resource ID for an animation style that defines {@code windowEnterAnimation} + * and {@code windowExitAnimation}. For example, passing + * {@link android.R.style#Animation_Dialog} will give a scale and alpha + * animation. + * </br> + * A window animation style may also be specified in the popup window's style + * XML via the {@link android.R.styleable#PopupWindow_popupAnimationStyle popupAnimationStyle} + * attribute. + * </p> + * <p> + * Starting with API 23, more complex popup window enter and exit transitions + * may be specified by calling either {@link #setEnterTransition(Transition)} + * or {@link #setExitTransition(Transition)} and passing a {@link Transition}. + * </br> + * Popup enter and exit transitions may also be specified in the popup window's + * style XML via the {@link android.R.styleable#PopupWindow_popupEnterTransition popupEnterTransition} + * and {@link android.R.styleable#PopupWindow_popupExitTransition popupExitTransition} + * attributes, respectively. + * </p> + * + * @attr ref android.R.styleable#PopupWindow_overlapAnchor + * @attr ref android.R.styleable#PopupWindow_popupAnimationStyle + * @attr ref android.R.styleable#PopupWindow_popupBackground + * @attr ref android.R.styleable#PopupWindow_popupElevation + * @attr ref android.R.styleable#PopupWindow_popupEnterTransition + * @attr ref android.R.styleable#PopupWindow_popupExitTransition * * @see android.widget.AutoCompleteTextView * @see android.widget.Spinner @@ -351,15 +385,54 @@ public class PopupWindow { setFocusable(focusable); } - public void setEnterTransition(Transition enterTransition) { + /** + * Sets the enter transition to be used when the popup window is shown. + * + * @param enterTransition the enter transition, or {@code null} to clear + * @see #getEnterTransition() + * @attr ref android.R.styleable#PopupWindow_popupEnterTransition + */ + public void setEnterTransition(@Nullable Transition enterTransition) { mEnterTransition = enterTransition; } - public void setExitTransition(Transition exitTransition) { + /** + * Returns the enter transition to be used when the popup window is shown. + * + * @return the enter transition, or {@code null} if not set + * @see #setEnterTransition(Transition) + * @attr ref android.R.styleable#PopupWindow_popupEnterTransition + */ + @Nullable + public Transition getEnterTransition() { + return mEnterTransition; + } + + /** + * Sets the exit transition to be used when the popup window is dismissed. + * + * @param exitTransition the exit transition, or {@code null} to clear + * @see #getExitTransition() + * @attr ref android.R.styleable#PopupWindow_popupExitTransition + */ + public void setExitTransition(@Nullable Transition exitTransition) { mExitTransition = exitTransition; } /** + * Returns the exit transition to be used when the popup window is + * dismissed. + * + * @return the exit transition, or {@code null} if not set + * @see #setExitTransition(Transition) + * @attr ref android.R.styleable#PopupWindow_popupExitTransition + */ + @Nullable + public Transition getExitTransition() { + return mExitTransition; + } + + /** * Sets the bounds used as the epicenter of the enter and exit transitions. * <p> * Transitions use a point or Rect, referred to as the epicenter, to orient diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 506035087ead..31950970219f 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -285,8 +285,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private static final RectF TEMP_RECTF = new RectF(); - // XXX should be much larger - private static final int VERY_WIDE = 1024*1024; + /** @hide */ + static final int VERY_WIDE = 1024 * 1024; // XXX should be much larger private static final int ANIMATED_SCROLL_GAP = 250; private static final InputFilter[] NO_FILTERS = new InputFilter[0]; diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 9897b125dea3..80e1db0527a5 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -128,8 +128,8 @@ public class ResolverActivity extends Activity { com.android.internal.R.string.whichSendApplication, com.android.internal.R.string.whichSendApplicationNamed), SENDTO(Intent.ACTION_SENDTO, - com.android.internal.R.string.whichSendApplication, - com.android.internal.R.string.whichSendApplicationNamed), + com.android.internal.R.string.whichSendToApplication, + com.android.internal.R.string.whichSendToApplicationNamed), SEND_MULTIPLE(Intent.ACTION_SEND_MULTIPLE, com.android.internal.R.string.whichSendApplication, com.android.internal.R.string.whichSendApplicationNamed), diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 8de9c098e31a..d11787d86571 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -107,7 +107,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 141 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 142 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index ce03bb81c08b..86820306d634 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -308,9 +308,6 @@ public final class FloatingToolbar { private static final int MIN_OVERFLOW_SIZE = 2; private static final int MAX_OVERFLOW_SIZE = 4; - /* The duration of the overflow button vector animation duration. */ - private static final int OVERFLOW_BUTTON_ANIMATION_DELAY = 400; - private final Context mContext; private final View mParent; // Parent for the popup window. private final PopupWindow mPopupWindow; @@ -377,18 +374,6 @@ public final class FloatingToolbar { } }; - /* Runnable to reset the overflow button's drawable after an overflow transition. */ - private final Runnable mResetOverflowButtonDrawable = new Runnable() { - @Override - public void run() { - if (mIsOverflowOpen) { - mOverflowButton.setImageDrawable(mArrow); - } else { - mOverflowButton.setImageDrawable(mOverflow); - } - } - }; - private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing. private boolean mHidden; // tracks whether this popup is hidden or hiding. @@ -902,7 +887,9 @@ public final class FloatingToolbar { final Size containerSize = mOverflowPanelSize; setSize(mContentContainer, containerSize); mMainPanel.setAlpha(0); + mMainPanel.setVisibility(View.INVISIBLE); mOverflowPanel.setAlpha(1); + mOverflowPanel.setVisibility(View.VISIBLE); mOverflowButton.setImageDrawable(mArrow); // Update x-coordinates depending on RTL state. @@ -941,7 +928,9 @@ public final class FloatingToolbar { final Size containerSize = mMainPanelSize; setSize(mContentContainer, containerSize); mMainPanel.setAlpha(1); + mMainPanel.setVisibility(View.VISIBLE); mOverflowPanel.setAlpha(0); + mOverflowPanel.setVisibility(View.INVISIBLE); mOverflowButton.setImageDrawable(mOverflow); if (hasOverflow()) { @@ -1327,8 +1316,6 @@ public final class FloatingToolbar { mToArrow.start(); openOverflow(); } - overflowButton.postDelayed( - mResetOverflowButtonDrawable, OVERFLOW_BUTTON_ANIMATION_DELAY); } }); return overflowButton; @@ -1389,6 +1376,10 @@ public final class FloatingToolbar { // Disable the overflow button while it's animating. // It will be re-enabled when the animation stops. mOverflowButton.setEnabled(false); + // Ensure both panels have visibility turned on when the overflow animation + // starts. + mMainPanel.setVisibility(View.VISIBLE); + mOverflowPanel.setVisibility(View.VISIBLE); } @Override diff --git a/core/res/res/anim/progress_indeterminate_rotation_material.xml b/core/res/res/anim/progress_indeterminate_rotation_material.xml index 5d3ba22a29a7..6e12105615ec 100644 --- a/core/res/res/anim/progress_indeterminate_rotation_material.xml +++ b/core/res/res/anim/progress_indeterminate_rotation_material.xml @@ -16,7 +16,7 @@ --> <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" - android:duration="6665" + android:duration="4444" android:interpolator="@android:anim/linear_interpolator" android:propertyName="rotation" android:repeatCount="-1" diff --git a/core/res/res/drawable/vector_drawable_progress_bar_large.xml b/core/res/res/drawable/vector_drawable_progress_bar_large.xml index cd678f1c9d81..6448f3ba9c29 100644 --- a/core/res/res/drawable/vector_drawable_progress_bar_large.xml +++ b/core/res/res/drawable/vector_drawable_progress_bar_large.xml @@ -16,22 +16,22 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="76dp" android:width="76dp" - android:viewportHeight="48" - android:viewportWidth="48" + android:viewportHeight="76" + android:viewportWidth="76" android:tint="?attr/colorControlActivated"> <group android:name="root" - android:translateX="24.0" - android:translateY="24.0" > + android:translateX="38.0" + android:translateY="38.0" > <path android:name="progressBar" android:fillColor="#00000000" - android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38" + android:pathData="M0, 0 m 0, -29 a 29,29 0 1,1 0,58 a 29,29 0 1,1 0,-58" android:strokeColor="@color/white" android:strokeLineCap="square" android:strokeLineJoin="miter" - android:strokeWidth="4" + android:strokeWidth="6" android:trimPathEnd="0" android:trimPathOffset="0" android:trimPathStart="0" /> diff --git a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml index 7f038f44b197..80e335563e7b 100644 --- a/core/res/res/drawable/vector_drawable_progress_bar_medium.xml +++ b/core/res/res/drawable/vector_drawable_progress_bar_medium.xml @@ -27,7 +27,7 @@ <path android:name="progressBar" android:fillColor="#00000000" - android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38" + android:pathData="M0, 0 m 0, -18 a 18,18 0 1,1 0,36 a 18,18 0 1,1 0,-36" android:strokeColor="@color/white" android:strokeLineCap="square" android:strokeLineJoin="miter" diff --git a/core/res/res/drawable/vector_drawable_progress_bar_small.xml b/core/res/res/drawable/vector_drawable_progress_bar_small.xml index 562578859be4..ebb632a00a19 100644 --- a/core/res/res/drawable/vector_drawable_progress_bar_small.xml +++ b/core/res/res/drawable/vector_drawable_progress_bar_small.xml @@ -16,22 +16,22 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="16dp" android:width="16dp" - android:viewportHeight="48" - android:viewportWidth="48" + android:viewportHeight="16" + android:viewportWidth="16" android:tint="?attr/colorControlActivated"> <group android:name="root" - android:translateX="24.0" - android:translateY="24.0" > + android:translateX="8.0" + android:translateY="8.0" > <path android:name="progressBar" android:fillColor="#00000000" - android:pathData="M0, 0 m 0, -19 a 19,19 0 1,1 0,38 a 19,19 0 1,1 0,-38" + android:pathData="M0, 0 m 0, -5.9375 a 5.9375,5.9375 0 1,1 0,11.875 a 5.9375,5.9375 0 1,1 0,-11.875" android:strokeColor="@color/white" android:strokeLineCap="square" android:strokeLineJoin="miter" - android:strokeWidth="4" + android:strokeWidth="2.125" android:trimPathEnd="0" android:trimPathOffset="0" android:trimPathStart="0" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 3c5b4c0042bd..7b85c7ed331f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2707,6 +2707,8 @@ <public type="attr" name="tunerCount" /> <public type="attr" name="nfcAntennaPositionDrawable" /> <public type="attr" name="fillType" /> + <public type="attr" name="popupEnterTransition" /> + <public type="attr" name="popupExitTransition" /> <public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" /> <public type="style" name="Widget.Material.SeekBar.Discrete" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 5d083d7f43c7..ba9bcaa03239 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2567,6 +2567,13 @@ <!-- Title of intent resolver dialog when selecting a sharing application to run and a previously used application is known. --> <string name="whichSendApplicationNamed">Share with %1$s</string> + <!-- Title of intent resolver dialog when selecting an application to run to + send content to a specific recipient. Often used for email. --> + <string name="whichSendToApplication">Send using</string> + <!-- Title of intent resolver dialog when selecting an application to run to + send content to a specific recipient and a previously used application is known. + Often used for email. --> + <string name="whichSendToApplicationNamed">Send using %1$s</string> <!-- Title of intent resolver dialog when selecting a HOME application to run. --> <string name="whichHomeApplication">Select a Home app</string> <!-- Title of intent resolver dialog when selecting a HOME application to run @@ -2936,8 +2943,8 @@ <!-- Title of the pop-up dialog in which the user switches keyboard, also known as input method. --> <string name="select_input_method">Change keyboard</string> - <!-- Title of a button to open the settings to enable or disable keyboards, also known as input methods [CHAR LIMIT=30] --> - <string name="configure_input_methods">Choose keyboards</string> + <!-- Title of a button to open the settings to enable or disable other soft keyboards (also known as input methods) [CHAR LIMIT=30] --> + <string name="configure_input_methods">Other keyboards</string> <!-- Summary text of a toggle switch to enable/disable use of the IME while a physical keyboard is connected --> <string name="show_ime">Keep it on screen while physical keyboard is active</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f01cce320b08..8b7818312495 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2213,6 +2213,8 @@ <java-symbol type="string" name="whichEditApplicationNamed" /> <java-symbol type="string" name="whichSendApplication" /> <java-symbol type="string" name="whichSendApplicationNamed" /> + <java-symbol type="string" name="whichSendToApplication" /> + <java-symbol type="string" name="whichSendToApplicationNamed" /> <java-symbol type="attr" name="lightY" /> <java-symbol type="attr" name="lightZ" /> <java-symbol type="attr" name="lightRadius" /> diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index bfa2b10fdf4f..b78077892c9b 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -111,7 +111,7 @@ <!-- accessibility test permissions --> <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" /> - <application android:theme="@style/Theme"> + <application android:theme="@style/Theme" android:supportsRtl="true"> <uses-library android:name="android.test.runner" /> <uses-library android:name="org.apache.http.legacy" android:required="false" /> <meta-data diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java index 3be9cfc2d6e6..e1b305f56b3b 100644 --- a/core/tests/coretests/src/android/text/method/BackspaceTest.java +++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java @@ -69,6 +69,10 @@ public class BackspaceTest extends KeyListenerTestCase { public void testCombiningEnclosingKeycaps() { EditorState state = new EditorState(); + state.setByString("'1' U+E0101 U+20E3 |"); + backspace(state, 0); + state.assertEquals("|"); + // multiple COMBINING ENCLOSING KEYCAP state.setByString("'1' U+20E3 U+20E3 |"); backspace(state, 0); @@ -157,6 +161,19 @@ public class BackspaceTest extends KeyListenerTestCase { public void testEmojiZWJSequence() { EditorState state = new EditorState(); + // U+200D is ZERO WIDTH JOINER. + state.setByString("U+1F441 U+200D U+1F5E8 |"); + backspace(state, 0); + state.assertEquals("|"); + + state.setByString("U+1F441 U+200D U+1F5E8 U+FE0E |"); + backspace(state, 0); + state.assertEquals("|"); + + state.setByString("U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468 |"); + backspace(state, 0); + state.assertEquals("|"); + // End with ZERO WIDTH JOINER state.setByString("U+1F441 U+200D |"); backspace(state, 0); @@ -224,6 +241,11 @@ public class BackspaceTest extends KeyListenerTestCase { public void testEmojiModifier() { EditorState state = new EditorState(); + // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2. + state.setByString("U+1F466 U+1F3FB |"); + backspace(state, 0); + state.assertEquals("|"); + // Isolated emoji modifier state.setByString("U+1F3FB |"); backspace(state, 0); diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java index f7dab2d7bdbf..0fed77cc481f 100644 --- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java +++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java @@ -137,6 +137,15 @@ public class ForwardDeleteTest extends KeyListenerTestCase { public void testEmojiZeroWidthJoinerSequence() { EditorState state = new EditorState(); + // U+200D is ZERO WIDTH JOINER. + state.setByString("| U+1F441 U+200D U+1F5E8"); + forwardDelete(state, 0); + state.assertEquals("|"); + + state.setByString("| U+1F468 U+200D U+2764 U+FE0F U+200D U+1F48B U+200D U+1F468"); + forwardDelete(state, 0); + state.assertEquals("|"); + // End with ZERO WIDTH JOINER state.setByString("| U+1F441 U+200D"); forwardDelete(state, 0); @@ -188,6 +197,11 @@ public class ForwardDeleteTest extends KeyListenerTestCase { public void testEmojiModifier() { EditorState state = new EditorState(); + // U+1F3FB is EMOJI MODIFIER FITZPATRICK TYPE-1-2. + state.setByString("| U+1F466 U+1F3FB"); + forwardDelete(state, 0); + state.assertEquals("|"); + // Isolated emoji modifier state.setByString("| U+1F3FB"); forwardDelete(state, 0); diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java index 04c8b8cacb42..6d650ffaa66a 100644 --- a/core/tests/coretests/src/android/widget/EditorCursorTest.java +++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java @@ -16,16 +16,34 @@ package android.widget; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; +import android.view.Choreographer; import android.view.ViewGroup; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft; +import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.isEmptyString; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.sameInstance; + public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewActivity> { + + private final static String LTR_STRING = "aaaaaaaaaaaaaaaaaaaaaa"; + private final static String LTR_HINT = "hint"; + private final static String RTL_STRING = "Ù…Ø±ØØ¨Ø§ الروبوت Ù…Ø±ØØ¨Ø§ الروبوت Ù…Ø±ØØ¨Ø§ الروبوت"; + private final static String RTL_HINT = "الروبوت"; + private final static int CURSOR_BLINK_MS = 500; + private EditText mEditText; - private final String RTL_STRING = "Ù…Ø±ØØ¨Ø§ الروبوت Ù…Ø±ØØ¨Ø§ الروبوت Ù…Ø±ØØ¨Ø§ الروبوت"; public EditorCursorTest() { super(TextViewActivity.class); @@ -55,110 +73,160 @@ public class EditorCursorTest extends ActivityInstrumentationTestCase2<TextViewA @Override public void run() { getActivity().setContentView(layout); - mEditText.requestFocus(); } }); getInstrumentation().waitForIdleSync(); + onView(sameInstance(mEditText)).perform(click()); } @SmallTest - public void testCursorIsInViewBoundariesWhenOnRightForLtr() throws Exception { + public void testCursorIsInViewBoundariesWhenOnRightForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the end (right), // cursor is drawn to the right edge of the view - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa"); - int length = mEditText.getText().length(); - mEditText.setSelection(length, length); - } - }); - getInstrumentation().waitForIdleSync(); + setEditTextText(LTR_STRING, LTR_STRING.length()); - Editor editor = mEditText.getEditorForTesting(); - Drawable drawable = editor.getCursorDrawable()[0]; - Rect drawableBounds = drawable.getBounds(); - Rect drawablePadding = new Rect(); - drawable.getPadding(drawablePadding); - - // right edge of the view including the scroll - int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight() - - mEditText.getCompoundPaddingLeft() + +mEditText.getScrollX(); - int diff = drawableBounds.right - drawablePadding.right - maxRight; - assertTrue(diff >= 0 && diff <= 1); + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); } @SmallTest - public void testCursorIsInViewBoundariesWhenOnLeftForLtr() throws Exception { + public void testCursorIsInViewBoundariesWhenOnLeftForLtr() { // Asserts that when an EditText has LTR text, and cursor is at the beginning, // cursor is drawn to the left edge of the view + setEditTextText(LTR_STRING, 0); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - mEditText.setText("aaaaaaaaaaaaaaaaaaaaaa"); - mEditText.setSelection(0, 0); - } - }); - getInstrumentation().waitForIdleSync(); - - Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; - Rect drawableBounds = drawable.getBounds(); - Rect drawablePadding = new Rect(); - drawable.getPadding(drawablePadding); - - int diff = drawableBounds.left + drawablePadding.left; - assertTrue(diff >= 0 && diff <= 1); + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest - public void testCursorIsInViewBoundariesWhenOnRightForRtl() throws Exception { + public void testCursorIsInViewBoundariesWhenOnRightForRtl() { // Asserts that when an EditText has RTL text, and cursor is at the end, // cursor is drawn to the left edge of the view + setEditTextText(RTL_STRING, 0); - getActivity().runOnUiThread(new Runnable() { - @Override - public void run() { - mEditText.setText(RTL_STRING); - mEditText.setSelection(0, 0); - } - }); - getInstrumentation().waitForIdleSync(); + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + } + + @SmallTest + public void testCursorIsInViewBoundariesWhenOnLeftForRtl() { + // Asserts that when an EditText has RTL text, and cursor is at the beginning, + // cursor is drawn to the right edge of the view + setEditTextText(RTL_STRING, RTL_STRING.length()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); + } + + /* Tests for cursor positioning with hint */ + @SmallTest + public void testCursorIsOnLeft_withFirstStrongLtrAlgorithm() { + setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + assertThat(mEditText.getHint(), nullValue()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); - Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; - Rect drawableBounds = drawable.getBounds(); - Rect drawablePadding = new Rect(); - drawable.getPadding(drawablePadding); + setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); - int maxRight = mEditText.getWidth() - mEditText.getCompoundPaddingRight() - - mEditText.getCompoundPaddingLeft() + mEditText.getScrollX(); + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); - int diff = drawableBounds.right - drawablePadding.right - maxRight; - assertTrue(diff >= 0 && diff <= 1); + setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_LTR, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); } @SmallTest - public void testCursorIsInViewBoundariesWhenOnLeftForRtl() throws Exception { - // Asserts that when an EditText has RTL text, and cursor is at the beginning, - // cursor is drawn to the right edge of the view + public void testCursorIsOnRight_withFirstStrongRtlAlgorithm() { + setEditTextHint(null, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + assertThat(mEditText.getHint(), nullValue()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + + setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + + setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_FIRST_STRONG_RTL, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + } + + @SmallTest + public void testCursorIsOnLeft_withLtrAlgorithm() { + setEditTextHint(null, TextView.TEXT_DIRECTION_LTR, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + assertThat(mEditText.getHint(), nullValue()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); + + setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_LTR, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); + setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_LTR, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnLeft()); + } + + @SmallTest + public void testCursorIsOnRight_withRtlAlgorithm() { + setEditTextHint(null, TextView.TEXT_DIRECTION_RTL, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + assertThat(mEditText.getHint(), nullValue()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + + setEditTextHint(LTR_HINT, TextView.TEXT_DIRECTION_RTL, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + + setEditTextHint(RTL_HINT, TextView.TEXT_DIRECTION_RTL, 0); + assertThat(mEditText.getText().toString(), isEmptyString()); + + onView(sameInstance(mEditText)).check(hasInsertionPointerOnRight()); + } + + private void setEditTextProperties(final String text, final String hint, + final Integer textDirection, final Integer selection) { getActivity().runOnUiThread(new Runnable() { @Override public void run() { - mEditText.setText(RTL_STRING); - int length = mEditText.getText().length(); - mEditText.setSelection(length, length); + if (textDirection != null) mEditText.setTextDirection(textDirection); + if (text != null) mEditText.setText(text); + if (hint != null) mEditText.setHint(hint); + if (selection != null) mEditText.setSelection(selection); } }); getInstrumentation().waitForIdleSync(); - Drawable drawable = mEditText.getEditorForTesting().getCursorDrawable()[0]; - Rect drawableBounds = drawable.getBounds(); - Rect drawablePadding = new Rect(); - drawable.getPadding(drawablePadding); + // wait for cursor to be drawn. updateCursorPositions function is called during draw() and + // only when cursor is visible during blink. + final CountDownLatch latch = new CountDownLatch(1); + mEditText.postOnAnimationDelayed(new Runnable() { + @Override + public void run() { + latch.countDown(); + } + }, CURSOR_BLINK_MS); + try { + assertThat("Problem while waiting for the cursor to blink", + latch.await(10, TimeUnit.SECONDS), equalTo(true)); + } catch (Exception e) { + fail("Problem while waiting for the cursor to blink"); + } + } - int diff = drawableBounds.left - mEditText.getScrollX() + drawablePadding.left; - assertTrue(diff >= 0 && diff <= 1); + private void setEditTextHint(final String hint, final int textDirection, final int selection) { + setEditTextProperties(null, hint, textDirection, selection); } + private void setEditTextText(final String text, final Integer selection) { + setEditTextProperties(text, null, null, selection); + } } diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index 4a4727fd891f..91d57e78b25e 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -16,8 +16,10 @@ package android.widget; +import static android.support.test.espresso.action.ViewActions.longClick; import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles; import static android.widget.espresso.DragHandleUtils.onHandleView; +import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex; @@ -49,6 +51,7 @@ import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.text.Selection; import android.text.Spannable; +import android.text.InputType; import android.view.KeyEvent; import static org.hamcrest.Matchers.anyOf; @@ -234,6 +237,33 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV } @SmallTest + public void testToolbarAppearsAfterSelection_withFirstStringLtrAlgorithmAndRtlHint() + throws Exception { + // after the hint layout change, the floating toolbar was not visible in the case below + // this test tests that the floating toolbar is displayed on the screen and is visible to + // user. + final TextView textView = (TextView) getActivity().findViewById(R.id.textview); + textView.post(new Runnable() { + @Override + public void run() { + textView.setTextDirection(TextView.TEXT_DIRECTION_FIRST_STRONG_LTR); + textView.setInputType(InputType.TYPE_CLASS_TEXT); + textView.setSingleLine(true); + textView.setHint("الروبوت"); + } + }); + getInstrumentation().waitForIdleSync(); + + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test")); + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1)); + onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click()); + onView(withId(R.id.textview)).perform(longClick()); + sleepForFloatingToolbarPopup(); + + assertFloatingToolbarIsDisplayed(); + } + + @SmallTest public void testToolbarAndInsertionHandle() throws Exception { final String text = "text"; onView(withId(R.id.textview)).perform(click()); diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java index f02fe004e5b8..0f7f359447ad 100644 --- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java +++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java @@ -54,6 +54,16 @@ public class FloatingToolbarEspressoUtils { } /** + * Creates a {@link ViewInteraction} for the floating bar menu item with the given matcher. + * + * @param matcher The matcher for the menu item. + */ + public static ViewInteraction onFloatingToolBarItem(Matcher<View> matcher) { + return onView(matcher) + .inRoot(withDecorView(hasDescendant(withTagValue(is(TAG))))); + } + + /** * Asserts that the floating toolbar is displayed on screen. * * @throws AssertionError if the assertion fails diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java index 37c7425ce478..6e44cd83b048 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java @@ -19,15 +19,23 @@ package android.widget.espresso; import static android.support.test.espresso.matcher.ViewMatchers.assertThat; import static com.android.internal.util.Preconditions.checkNotNull; import static org.hamcrest.Matchers.is; +import static org.hamcrest.number.IsCloseTo.closeTo; +import android.annotation.IntDef; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.support.test.espresso.NoMatchingViewException; import android.support.test.espresso.ViewAssertion; import android.view.View; +import android.widget.EditText; import android.widget.TextView; import junit.framework.AssertionFailedError; import org.hamcrest.Matcher; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A collection of assertions on a {@link android.widget.TextView}. */ @@ -113,6 +121,22 @@ public final class TextViewAssertions { } /** + * Returns a {@link ViewAssertion} that asserts that the EditText insertion pointer is on + * the left edge. + */ + public static ViewAssertion hasInsertionPointerOnLeft() { + return new CursorPositionAssertion(CursorPositionAssertion.LEFT); + } + + /** + * Returns a {@link ViewAssertion} that asserts that the EditText insertion pointer is on + * the right edge. + */ + public static ViewAssertion hasInsertionPointerOnRight() { + return new CursorPositionAssertion(CursorPositionAssertion.RIGHT); + } + + /** * A {@link ViewAssertion} to check the selected text in a {@link TextView}. */ private static final class TextSelectionAssertion implements ViewAssertion { @@ -142,4 +166,54 @@ public final class TextViewAssertions { } } } + + /** + * {@link ViewAssertion} to check that EditText cursor is on a given position. + */ + static class CursorPositionAssertion implements ViewAssertion { + + @Retention(RetentionPolicy.SOURCE) + @IntDef({LEFT, RIGHT}) + public @interface CursorEdgePositionType {} + public static final int LEFT = 0; + public static final int RIGHT = 1; + + private final int mPosition; + + private CursorPositionAssertion(@CursorEdgePositionType int position) { + this.mPosition = position; + } + + @Override + public void check(View view, NoMatchingViewException exception) { + if (!(view instanceof EditText)) { + throw new AssertionFailedError("View should be an instance of EditText"); + } + EditText editText = (EditText) view; + Drawable drawable = editText.getEditorForTesting().getCursorDrawable()[0]; + Rect drawableBounds = drawable.getBounds(); + Rect drawablePadding = new Rect(); + drawable.getPadding(drawablePadding); + + final int diff; + final String positionStr; + switch (mPosition) { + case LEFT: + positionStr = "left"; + diff = drawableBounds.left - editText.getScrollX() + drawablePadding.left; + break; + case RIGHT: + positionStr = "right"; + int maxRight = editText.getWidth() - editText.getCompoundPaddingRight() + - editText.getCompoundPaddingLeft() + editText.getScrollX(); + diff = drawableBounds.right - drawablePadding.right - maxRight; + break; + default: + throw new AssertionFailedError("Unknown position for cursor assertion"); + } + + assertThat("Cursor should be on the " + positionStr, Double.valueOf(diff), + closeTo(0f, 1f)); + } + } } diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd index e3f1d9e7769f..ca7255bdf6a8 100644 --- a/docs/html/guide/topics/graphics/hardware-accel.jd +++ b/docs/html/guide/topics/graphics/hardware-accel.jd @@ -284,7 +284,7 @@ changed.</li> </tr> <tr> <td class="label_neg">drawPicture()</td> - <td class="value_neg">23</td> + <td class="value_pos">23</td> </tr> <tr> <td class="label_pos">drawPosText()</td> @@ -421,14 +421,6 @@ changed.</li> <td colspan="5" class="s5">Xfermode</td> </tr> <tr> - <td class="label_neg">AvoidXfermode</td> - <td class="value_neg">✗</td> - </tr> - <tr> - <td class="label_neg">PixelXorXfermode</td> - <td class="value_neg">✗</td> - </tr> - <tr> <td class="label_neg">PorterDuff.Mode.DARKEN (framebuffer)</td> <td class="value_neg">✗</td> </tr> diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 1fc236a2a6d3..ed358d3d270a 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -605,8 +605,9 @@ public class ExifInterface { // not only getting information from EXIF but also from some JPEG special segments such as // MARKER_COM for user comment and MARKER_SOFx for image width and height. - // Identifier for APP1 segment in JPEG - private static final byte[] IDENTIFIER_APP1 = "Exif\0\0".getBytes(Charset.forName("US-ASCII")); + // Identifier for EXIF APP1 segment in JPEG + private static final byte[] IDENTIFIER_EXIF_APP1 = + "Exif\0\0".getBytes(Charset.forName("US-ASCII")); // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start // of frame(baseline DCT) and the image size info exists in its beginning part. @@ -1125,7 +1126,9 @@ public class ExifInterface { String time = getAttribute(TAG_GPS_TIMESTAMP); if (date == null || time == null || (!sNonZeroTimePattern.matcher(date).matches() - && !sNonZeroTimePattern.matcher(time).matches())) return -1; + && !sNonZeroTimePattern.matcher(time).matches())) { + return -1; + } String dateTimeString = date + ' ' + time; @@ -1176,7 +1179,6 @@ public class ExifInterface { DataInputStream dataInputStream = new DataInputStream(inputStream); byte marker; int bytesRead = 0; - ++bytesRead; if ((marker = dataInputStream.readByte()) != MARKER) { throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); } @@ -1184,8 +1186,8 @@ public class ExifInterface { if (dataInputStream.readByte() != MARKER_SOI) { throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff)); } + ++bytesRead; while (true) { - ++bytesRead; marker = dataInputStream.readByte(); if (marker != MARKER) { throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff)); @@ -1195,36 +1197,40 @@ public class ExifInterface { if (DEBUG) { Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff)); } + ++bytesRead; // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and // the image data will terminate right after. if (marker == MARKER_EOI || marker == MARKER_SOS) { break; } - bytesRead += 2; int length = dataInputStream.readUnsignedShort() - 2; + bytesRead += 2; + if (DEBUG) { + Log.d(TAG, "JPEG segment: " + marker + " (length: " + (length + 2) + ")"); + } if (length < 0) { throw new IOException("Invalid length"); } - bytesRead += length; switch (marker) { case MARKER_APP1: { if (DEBUG) { Log.d(TAG, "MARKER_APP1"); } - bytesRead -= length; if (length < 6) { - throw new IOException("Invalid exif"); + // Skip if it's not an EXIF APP1 segment. + break; } byte[] identifier = new byte[6]; if (inputStream.read(identifier) != 6) { throw new IOException("Invalid exif"); } - if (!Arrays.equals(identifier, IDENTIFIER_APP1)) { - throw new IOException("Invalid app1 identifier"); - } bytesRead += 6; length -= 6; + if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { + // Skip if it's not an EXIF APP1 segment. + break; + } if (length <= 0) { throw new IOException("Invalid exif"); } @@ -1246,6 +1252,7 @@ public class ExifInterface { if (dataInputStream.read(bytes) != length) { throw new IOException("Invalid exif"); } + length = 0; setAttribute("UserComment", new String(bytes, Charset.forName("US-ASCII"))); break; } @@ -1279,6 +1286,7 @@ public class ExifInterface { throw new IOException("Invalid length"); } dataInputStream.skipBytes(length); + bytesRead += length; } } @@ -1292,68 +1300,84 @@ public class ExifInterface { } DataInputStream dataInputStream = new DataInputStream(inputStream); ExifDataOutputStream dataOutputStream = new ExifDataOutputStream(outputStream); - int bytesRead = 0; - ++bytesRead; if (dataInputStream.readByte() != MARKER) { throw new IOException("Invalid marker"); } dataOutputStream.writeByte(MARKER); - ++bytesRead; if (dataInputStream.readByte() != MARKER_SOI) { throw new IOException("Invalid marker"); } dataOutputStream.writeByte(MARKER_SOI); + // Write EXIF APP1 segment + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(MARKER_APP1); + writeExifSegment(dataOutputStream, 6); + byte[] bytes = new byte[4096]; while (true) { - ++bytesRead; if (dataInputStream.readByte() != MARKER) { throw new IOException("Invalid marker"); } - dataOutputStream.writeByte(MARKER); - ++bytesRead; byte marker = dataInputStream.readByte(); - dataOutputStream.writeByte(marker); switch (marker) { case MARKER_APP1: { - // Rewrite EXIF segment int length = dataInputStream.readUnsignedShort() - 2; if (length < 0) { throw new IOException("Invalid length"); } - bytesRead += 2; + byte[] identifier = new byte[6]; + if (length >= 6) { + if (dataInputStream.read(identifier) != 6) { + throw new IOException("Invalid exif"); + } + if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) { + // Skip the original EXIF APP1 segment. + if (dataInputStream.skip(length - 6) != length - 6) { + throw new IOException("Invalid length"); + } + break; + } + } + // Copy non-EXIF APP1 segment. + dataOutputStream.writeUnsignedShort(length + 2); + if (length >= 6) { + length -= 6; + dataOutputStream.write(identifier); + } int read; - while ((read = dataInputStream.read( - bytes, 0, Math.min(length, bytes.length))) > 0) { + while (length > 0 && (read = dataInputStream.read( + bytes, 0, Math.min(length, bytes.length))) >= 0) { + dataOutputStream.write(bytes, 0, read); length -= read; } - bytesRead += length; - writeExifSegment(dataOutputStream, bytesRead); break; } case MARKER_EOI: case MARKER_SOS: { + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(marker); // Copy all the remaining data Streams.copy(dataInputStream, dataOutputStream); return; } default: { // Copy JPEG segment + dataOutputStream.writeByte(MARKER); + dataOutputStream.writeByte(marker); int length = dataInputStream.readUnsignedShort(); dataOutputStream.writeUnsignedShort(length); + length -= 2; if (length < 0) { throw new IOException("Invalid length"); } - length -= 2; - bytesRead += 2; int read; - while ((read = dataInputStream.read( - bytes, 0, Math.min(length, bytes.length))) > 0) { + while (length > 0 && (read = dataInputStream.read( + bytes, 0, Math.min(length, bytes.length))) >= 0) { dataOutputStream.write(bytes, 0, read); length -= read; } - bytesRead += length; break; } } @@ -1924,7 +1948,7 @@ public class ExifInterface { // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10. dataOutputStream.writeUnsignedShort(totalSize); - dataOutputStream.write(IDENTIFIER_APP1); + dataOutputStream.write(IDENTIFIER_EXIF_APP1); dataOutputStream.writeShort(BYTE_ALIGN_MM); dataOutputStream.writeUnsignedShort(0x2a); dataOutputStream.writeUnsignedInt(8); diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml index 86087c3ffa50..ebb3969b7819 100644 --- a/packages/DocumentsUI/res/values/config.xml +++ b/packages/DocumentsUI/res/values/config.xml @@ -21,4 +21,8 @@ <!-- Intentionally unset. Vendors should set this in an overlay. --> <string name="trusted_quick_viewer_package" translatable="false"></string> <bool name="list_divider_inset_left">true</bool> + <!-- Indicates if the home directory should be hidden in the roots list, that is presented + in the drawer/left side panel ) --> + <bool name="home_root_hidden">true</bool> + </resources> diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index e7406e68302d..0d098e69b73b 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -211,13 +211,29 @@ <!-- Text in the button asking user to deny access to a given directory. --> <string name="deny">Deny</string> <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). --> - <plurals name="delete_confirmation_message"> - <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item> - <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item> - </plurals> <!-- Label text showing user how many items are selected. Can be one or more elements. --> <plurals name="elements_selected"> <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> selected</item> <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item> </plurals> + + <!-- Dialog text shown to users when asking if they want to delete a file (a confirmation) --> + <string name="delete_filename_confirmation_message">Delete \"<xliff:g id="name" example="cat.jpg">%1$s</xliff:g>\"?</string> + <!-- Dialog text shown to users when asking if they want to delete a folder (a confirmation) --> + <string name="delete_foldername_confirmation_message">Delete folder \"<xliff:g id="name" example="Photos">%1$s</xliff:g>\" and its contents?</string> + <!-- Dialog text shown to users when asking if they want to delete files (a confirmation). --> + <plurals name="delete_files_confirmation_message"> + <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> file?</item> + <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> files?</item> + </plurals> + <!-- Dialog text shown to users when asking if they want to delete folders (a confirmation). --> + <plurals name="delete_folders_confirmation_message"> + <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> folder and its contents?</item> + <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> folders and their contents?</item> + </plurals> + <!-- Dialog text shown to users when asking if they want to delete mixed type items: files and folders (a confirmation). --> + <plurals name="delete_items_confirmation_message"> + <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> item?</item> + <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> items?</item> + </plurals> </resources> diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index 9f83c042b111..54e628732a60 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -317,7 +317,10 @@ public class RootsFragment extends Fragment { for (final RootInfo root : roots) { final RootItem item = new RootItem(root); - if (root.isLibrary()) { + + if (root.isHome() && isHomeRootHidden(context)) { + continue; + } else if (root.isLibrary()) { if (DEBUG) Log.d(TAG, "Adding " + root + " as library."); libraries.add(item); } else { @@ -367,6 +370,13 @@ public class RootsFragment extends Fragment { } } + /* + * Indicates if the home directory should be hidden in the roots list. + */ + private boolean isHomeRootHidden(Context context) { + return context.getResources().getBoolean(R.bool.home_root_hidden); + } + @Override public View getView(int position, View convertView, ViewGroup parent) { final Item item = getItem(position); diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java index c32bbff0c911..6f1863e313c5 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java +++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java @@ -151,6 +151,14 @@ public final class Shared { return sCollator.compare(lhs, rhs); } + /** + * Compare two strings against each other using system default collator in a + * case-insensitive mode. + */ + public static int compareToIgnoreCase(String lhs, String rhs) { + return sCollator.compare(lhs, rhs); + } + public static boolean isHardwareKeyboardAvailable(Context context) { return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS; } diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index 63a834f85274..60e4b9abd4fe 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -688,6 +688,39 @@ public class DirectoryFragment extends Fragment }.execute(selected); } + private String generateDeleteMessage(final List<DocumentInfo> docs) { + String message; + int dirsCount = 0; + + for (DocumentInfo doc : docs) { + if (doc.isDirectory()) { + ++dirsCount; + } + } + + if (docs.size() == 1) { + // Deleteing 1 file xor 1 folder in cwd + message = dirsCount == 0 + ? getActivity().getString(R.string.delete_filename_confirmation_message, + docs.get(0).displayName) + : getActivity().getString(R.string.delete_foldername_confirmation_message, + docs.get(0).displayName); + } else if (dirsCount == 0) { + // Deleting only files in cwd + message = Shared.getQuantityString(getActivity(), + R.plurals.delete_files_confirmation_message, docs.size()); + } else if (dirsCount == docs.size()) { + // Deleting only folders in cwd + message = Shared.getQuantityString(getActivity(), + R.plurals.delete_folders_confirmation_message, docs.size()); + } else { + // Deleting mixed items (files and folders) in cwd + message = Shared.getQuantityString(getActivity(), + R.plurals.delete_items_confirmation_message, docs.size()); + } + return message; + } + private void deleteDocuments(final Selection selected) { assert(!selected.isEmpty()); @@ -698,11 +731,7 @@ public class DirectoryFragment extends Fragment TextView message = (TextView) mInflater.inflate(R.layout.dialog_delete_confirmation, null); - message.setText( - Shared.getQuantityString( - getActivity(), - R.plurals.delete_confirmation_message, - docs.size())); + message.setText(generateDeleteMessage(docs)); // This "insta-hides" files that are being deleted, because // the delete operation may be not execute immediately (it diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java index c5ee59237fb0..3642b01bef59 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/Model.java @@ -20,10 +20,9 @@ import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.State.SORT_ORDER_DISPLAY_NAME; import static com.android.documentsui.State.SORT_ORDER_LAST_MODIFIED; import static com.android.documentsui.State.SORT_ORDER_SIZE; -import static com.android.documentsui.model.DocumentInfo.getCursorLong; -import static com.android.documentsui.model.DocumentInfo.getCursorString; import android.database.Cursor; +import android.database.MergeCursor; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; @@ -48,6 +47,7 @@ import java.util.Map; @VisibleForTesting public class Model { private static final String TAG = "Model"; + private static final String EMPTY = ""; private boolean mIsLoading; private List<UpdateListener> mUpdateListeners = new ArrayList<>(); @@ -62,34 +62,17 @@ public class Model { private String mIds[] = new String[0]; private int mSortOrder = SORT_ORDER_DISPLAY_NAME; + private int mAuthorityIndex = -1; + private int mDocIdIndex = -1; + private int mMimeTypeIndex = -1; + private int mDisplayNameIndex = -1; + private int mSizeIndex = -1; + private int mLastModifiedIndex = -1; + @Nullable String info; @Nullable String error; @Nullable DocumentInfo doc; - /** - * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique - * string that can be used to identify the document referred to by the cursor. - * - * @param c A cursor that refers to a document. - */ - private static String createModelId(Cursor c) { - // TODO: Maybe more efficient to use just the document ID, in cases where there is only one - // authority (which should be the majority of cases). - return createModelId( - getCursorString(c, RootCursorWrapper.COLUMN_AUTHORITY), - getCursorString(c, Document.COLUMN_DOCUMENT_ID)); - } - - /** - * Generates a Model ID for a cursor entry that refers to a document. The Model ID is a unique - * string that can be used to identify the document referred to by the cursor. - * - * @param c A cursor that refers to a document. - */ - static String createModelId(String authority, String docId) { - return authority + "|" + docId; - } - private void notifyUpdateListeners() { for (UpdateListener listener: mUpdateListeners) { listener.onModelUpdate(this); @@ -127,6 +110,14 @@ public class Model { mCursor = result.cursor; mCursorCount = mCursor.getCount(); mSortOrder = result.sortOrder; + mAuthorityIndex = mCursor.getColumnIndex(RootCursorWrapper.COLUMN_AUTHORITY); + assert(mAuthorityIndex != -1); + mDocIdIndex = mCursor.getColumnIndex(Document.COLUMN_DOCUMENT_ID); + mMimeTypeIndex = mCursor.getColumnIndex(Document.COLUMN_MIME_TYPE); + mDisplayNameIndex = mCursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME); + mLastModifiedIndex = mCursor.getColumnIndex(Document.COLUMN_LAST_MODIFIED); + mSizeIndex = mCursor.getColumnIndex(Document.COLUMN_SIZE); + doc = result.doc; updateModelData(); @@ -173,22 +164,29 @@ public class Model { for (int pos = 0; pos < mCursorCount; ++pos) { mCursor.moveToNext(); positions[pos] = pos; - mIds[pos] = createModelId(mCursor); - mimeType = getCursorString(mCursor, Document.COLUMN_MIME_TYPE); + // Generates a Model ID for a cursor entry that refers to a document. The Model ID is a + // unique string that can be used to identify the document referred to by the cursor. + // If the cursor is a merged cursor over multiple authorities, then prefix the ids + // with the authority to avoid collisions. + if (mCursor instanceof MergeCursor) { + mIds[pos] = getStringOrEmpty(mAuthorityIndex) + "|" + getStringOrEmpty(mDocIdIndex); + } else { + mIds[pos] = getStringOrEmpty(mDocIdIndex); + } + + mimeType = getStringOrEmpty(mMimeTypeIndex); isDirs[pos] = Document.MIME_TYPE_DIR.equals(mimeType); - switch(mSortOrder) { + switch (mSortOrder) { case SORT_ORDER_DISPLAY_NAME: - final String displayName = getCursorString( - mCursor, Document.COLUMN_DISPLAY_NAME); - displayNames[pos] = displayName; + displayNames[pos] = getStringOrEmpty(mDisplayNameIndex); break; case SORT_ORDER_LAST_MODIFIED: - longValues[pos] = getLastModified(mCursor); + longValues[pos] = getLastModified(); break; case SORT_ORDER_SIZE: - longValues[pos] = getCursorLong(mCursor, Document.COLUMN_SIZE); + longValues[pos] = getDocSize(); break; } } @@ -244,7 +242,7 @@ public class Model { } else { final String lhs = pivotValue; final String rhs = sortKey[mid]; - compare = Shared.compareToIgnoreCaseNullable(lhs, rhs); + compare = Shared.compareToIgnoreCase(lhs, rhs); } if (compare < 0) { @@ -365,13 +363,42 @@ public class Model { } /** + * @return Value of the string column, or an empty string if no value, or empty value. + */ + private String getStringOrEmpty(int columnIndex) { + if (columnIndex == -1) + return EMPTY; + final String result = mCursor.getString(columnIndex); + return result != null ? result : EMPTY; + } + + /** * @return Timestamp for the given document. Some docs (e.g. active downloads) have a null - * timestamp - these will be replaced with MAX_LONG so that such files get sorted to the top - * when sorting by date. + * or missing timestamp - these will be replaced with MAX_LONG so that such files get sorted to + * the top when sorting by date. */ - long getLastModified(Cursor cursor) { - long l = getCursorLong(mCursor, Document.COLUMN_LAST_MODIFIED); - return (l == -1) ? Long.MAX_VALUE : l; + private long getLastModified() { + if (mLastModifiedIndex == -1) + return Long.MAX_VALUE; + try { + final long result = mCursor.getLong(mLastModifiedIndex); + return result > 0 ? result : Long.MAX_VALUE; + } catch (NumberFormatException e) { + return Long.MAX_VALUE; + } + } + + /** + * @return Size for the given document. If the size is unknown or invalid, returns 0. + */ + private long getDocSize() { + if (mSizeIndex == -1) + return 0; + try { + return mCursor.getLong(mSizeIndex); + } catch (NumberFormatException e) { + return 0; + } } public @Nullable Cursor getItem(String modelId) { diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java index e4afc3d8199c..f2949196f2a1 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java @@ -58,18 +58,23 @@ public class FilesActivityUiTest extends ActivityTest<FilesActivity> { public void testRootsListed() throws Exception { initTestFiles(); - bots.roots.openRoot(ROOT_0_ID); - // Should also have Drive, but that requires pre-configuration of devices // We omit for now. - bots.roots.assertHasRoots( + bots.roots.assertRootsPresent( "Images", "Videos", "Audio", "Downloads", - "Documents", ROOT_0_ID, ROOT_1_ID); + + // Separate logic for "Documents" root, which presence depends on the config setting + boolean homeRootHidden = context.getResources().getBoolean(R.bool.home_root_hidden); + if (homeRootHidden) { + bots.roots.assertRootsAbsent("Documents"); + } else { + bots.roots.assertRootsPresent("Documents"); + } } public void testFilesListed() throws Exception { diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java index 356fd01957e6..096af10bab5f 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/RootsListBot.java @@ -69,7 +69,7 @@ public class RootsListBot extends BaseBot { mDevice.waitForIdle(); } - public void assertHasRoots(String... labels) throws UiObjectNotFoundException { + public void assertRootsPresent(String... labels) throws UiObjectNotFoundException { List<String> missing = new ArrayList<>(); for (String label : labels) { if (!findRoot(label).exists()) { @@ -82,6 +82,18 @@ public class RootsListBot extends BaseBot { } } + public void assertRootsAbsent(String... labels) throws UiObjectNotFoundException { + List<String> unexpected = new ArrayList<>(); + for (String label : labels) { + if (findRoot(label).exists()) { + unexpected.add(label); + } + } + if (!unexpected.isEmpty()) { + Assert.fail("Unexpected roots " + unexpected); + } + } + public void assertHasFocus() { assertHasFocus(ROOTS_LIST_ID); } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java index c6ad5116d93b..3536593564af 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/ModelTest.java @@ -284,7 +284,7 @@ public class ModelTest extends AndroidTestCase { String id = Integer.toString(i); row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY); row.add(Document.COLUMN_DOCUMENT_ID, id); - currentDownloads.add(Model.createModelId(AUTHORITY, id)); + currentDownloads.add(id); } DirectoryResult r = new DirectoryResult(); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java index d8c29db81a77..2d819ffcdf07 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestModel.java @@ -62,9 +62,7 @@ public class TestModel extends Model { update(r); } - // Note that model id includes authority qualifier and is distinct - // WRT documentId because of this. String idForPosition(int p) { - return createModelId(mAuthority, Integer.toString(p)); + return Integer.toString(p); } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java index 7a6aad6a96f5..2d3935b6c9e7 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/RemotePrintDocument.java @@ -870,39 +870,56 @@ public final class RemotePrintDocument { // AsyncCommand.AsyncCommandHandler#handleMessage if (isFailed()) { if (DEBUG) { - Log.i(LOG_TAG, "[CALLBACK] on canceled layout command"); + Log.i(LOG_TAG, "[CALLBACK] on failed layout command"); } return; - } else { - if (message.what != MSG_ON_LAYOUT_STARTED) { - // No need to force cancel anymore if layout finished + } + + int sequence; + int what = message.what; + switch (what) { + case MSG_ON_LAYOUT_FINISHED: removeForceCancel(); - } + sequence = message.arg2; + break; + case MSG_ON_LAYOUT_FAILED: + case MSG_ON_LAYOUT_CANCELED: + removeForceCancel(); + // $FALL-THROUGH - message uses the same format as "started" + case MSG_ON_LAYOUT_STARTED: + // Don't remote force-cancel as command is still running and might need to + // be canceled later + sequence = message.arg1; + break; + default: + // not reached + sequence = -1; + } + + // If we are canceling any result is treated as a cancel + if (isCanceling() && what != MSG_ON_LAYOUT_STARTED) { + what = MSG_ON_LAYOUT_CANCELED; } - switch (message.what) { + switch (what) { case MSG_ON_LAYOUT_STARTED: { ICancellationSignal cancellation = (ICancellationSignal) message.obj; - final int sequence = message.arg1; handleOnLayoutStarted(cancellation, sequence); } break; case MSG_ON_LAYOUT_FINISHED: { PrintDocumentInfo info = (PrintDocumentInfo) message.obj; final boolean changed = (message.arg1 == 1); - final int sequence = message.arg2; handleOnLayoutFinished(info, changed, sequence); } break; case MSG_ON_LAYOUT_FAILED: { CharSequence error = (CharSequence) message.obj; - final int sequence = message.arg1; handleOnLayoutFailed(error, sequence); } break; case MSG_ON_LAYOUT_CANCELED: { - final int sequence = message.arg1; handleOnLayoutCanceled(sequence); } break; } @@ -1142,18 +1159,30 @@ public final class RemotePrintDocument { // AsyncCommand.AsyncCommandHandler#handleMessage if (isFailed()) { if (DEBUG) { - Log.i(LOG_TAG, "[CALLBACK] on canceled write command"); + Log.i(LOG_TAG, "[CALLBACK] on failed write command"); } return; - } else { - if (message.what != MSG_ON_WRITE_STARTED) { - // No need to force cancel anymore if write finished + } + + int what = message.what; + switch (what) { + case MSG_ON_WRITE_FINISHED: + case MSG_ON_WRITE_FAILED: + case MSG_ON_WRITE_CANCELED: removeForceCancel(); - } + case MSG_ON_WRITE_STARTED: + // Don't remote force-cancel as command is still running and might need to + // be canceled later + break; + } + + // If we are canceling any result is treated as a cancel + if (isCanceling() && what != MSG_ON_WRITE_STARTED) { + what = MSG_ON_WRITE_CANCELED; } - switch (message.what) { + switch (what) { case MSG_ON_WRITE_STARTED: { ICancellationSignal cancellation = (ICancellationSignal) message.obj; final int sequence = message.arg1; diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index ad4823eb086c..4e1180d051bf 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -159,6 +159,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private static final int STATE_PRINTER_UNAVAILABLE = 6; private static final int STATE_UPDATE_SLOW = 7; private static final int STATE_PRINT_COMPLETED = 8; + private static final int STATE_FINISHING = 9; private static final int UI_STATE_PREVIEW = 0; private static final int UI_STATE_ERROR = 1; @@ -2029,11 +2030,17 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } private void doFinish() { - if (mPrintedDocument.isUpdating()) { + if (mPrintedDocument != null && mPrintedDocument.isUpdating()) { // The printedDocument will call doFinish() when the current command finishes return; } + if (mState == STATE_FINISHING) { + return; + } + + mState = STATE_FINISHING; + if (mPrinterRegistry != null) { mPrinterRegistry.setTrackedPrinter(null); } diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml index 5a6553ff90db..9c2c0ab9faa3 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml @@ -27,29 +27,19 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingEnd="12dp" - android:background="@android:color/white" - android:textColor="#D9000000" + android:textColor="@color/ksh_keyword_color" android:textSize="16sp" android:maxLines="5" android:singleLine="false" android:scrollHorizontally="false" - android:layout_alignParentStart="true" - android:minWidth="100dp" - android:maxWidth="260dp"/> - <!--TODO: introduce and use a layout that allows wrapping and right align --> - <LinearLayout + android:layout_alignParentStart="true"/> + <com.android.systemui.statusbar.KeyboardShortcutKeysLayout android:id="@+id/keyboard_shortcuts_item_container" android:layout_toEndOf="@+id/keyboard_shortcuts_keyword" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@android:color/white" android:layout_alignParentEnd="true" - android:gravity="end" android:textSize="14sp" - android:paddingStart="0dp" - android:paddingEnd="0dp" - android:scrollHorizontally="false" - android:minWidth="100dp" - android:maxWidth="260dp"/> + android:scrollHorizontally="false"/> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml index 80a478a6d3d8..6cb8470eaf21 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml @@ -22,4 +22,4 @@ android:paddingStart="24dp" android:paddingTop="20dp" android:paddingEnd="24dp" - android:paddingBottom="13dp" /> + android:paddingBottom="13dp"/> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml index f73ee1532ba3..7aba1cf6e0dc 100644 --- a/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml +++ b/packages/SystemUI/res/layout/keyboard_shortcuts_view.xml @@ -17,7 +17,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="488dp" + android:layout_width="@dimen/ksh_layout_width" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index a2fa3b976922..66963c43e8dc 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -104,4 +104,7 @@ <!-- The side padding for the task stack. --> <dimen name="recents_stack_left_right_padding">64dp</dimen> + + <!-- Keyboard shortcuts helper --> + <dimen name="ksh_layout_width">488dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index eeed0cf62287..a3f8b8551def 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -167,6 +167,7 @@ <!-- Keyboard shortcuts colors --> <color name="ksh_system_group_color">#ff00bcd4</color> <color name="ksh_application_group_color">#fff44336</color> + <color name="ksh_keyword_color">#d9000000</color> <!-- Background color of edit overflow --> <color name="qs_edit_overflow_bg">#455A64</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6a9f45606bab..8b433f984d65 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -639,4 +639,7 @@ <dimen name="battery_detail_graph_space_top">27dp</dimen> <dimen name="battery_detail_graph_space_bottom">27dp</dimen> + + <!-- Keyboard shortcuts helper --> + <dimen name="ksh_layout_width">@dimen/match_parent</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java new file mode 100644 index 000000000000..ba3e774ea2b7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2016 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.systemui.statusbar; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; + +/** + * Layout used as a container for keyboard shortcut keys. It's children are wrapped and right + * aligned. + */ +public final class KeyboardShortcutKeysLayout extends ViewGroup { + private int mLineHeight; + + public KeyboardShortcutKeysLayout(Context context) { + super(context); + } + + public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(); + int childCount = getChildCount(); + int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingTop() - getPaddingBottom(); + int lineHeight = 0; + int xPos = getPaddingLeft(); + int yPos = getPaddingTop(); + + int childHeightMeasureSpec; + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); + } else { + childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + } + + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + if (child.getVisibility() != GONE) { + LayoutParams layoutParams = (LayoutParams) child.getLayoutParams(); + child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), + childHeightMeasureSpec); + int childWidth = child.getMeasuredWidth(); + lineHeight = Math.max(lineHeight, + child.getMeasuredHeight() + layoutParams.mVerticalSpacing); + + if (xPos + childWidth > width) { + xPos = getPaddingLeft(); + yPos += lineHeight; + } + xPos += childWidth + layoutParams.mHorizontalSpacing; + } + } + this.mLineHeight = lineHeight; + + if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.UNSPECIFIED) { + height = yPos + lineHeight; + } else if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) { + if (yPos + lineHeight < height) { + height = yPos + lineHeight; + } + } + setMeasuredDimension(width, height); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + int spacing = getHorizontalVerticalSpacing(); + return new LayoutParams(spacing, spacing); + } + + @Override + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams layoutParams) { + int spacing = getHorizontalVerticalSpacing(); + return new LayoutParams(spacing, spacing, layoutParams); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return (p instanceof LayoutParams); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + int childCount = getChildCount(); + int fullRowWidth = r - l; + int xPos = getPaddingLeft(); + int yPos = getPaddingTop(); + int lastHorizontalSpacing = 0; + // The index of the child which starts the current row. + int rowStartIdx = 0; + + // Go through all the children. + for (int i = 0; i < childCount; i++) { + View currentChild = getChildAt(i); + if (currentChild.getVisibility() != GONE) { + int currentChildWidth = currentChild.getMeasuredWidth(); + LayoutParams lp = (LayoutParams) currentChild.getLayoutParams(); + + // If the current child does not fit on this row. + if (xPos + currentChildWidth > fullRowWidth) { + // Layout all the children on this row but the current one. + layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos, + lastHorizontalSpacing); + // Update the positions for starting on the new row. + xPos = getPaddingLeft(); + yPos += mLineHeight; + rowStartIdx = i; + } + + xPos += currentChildWidth + lp.mHorizontalSpacing; + lastHorizontalSpacing = lp.mHorizontalSpacing; + } + } + + // Lay out the children on the last row. + if (rowStartIdx < childCount) { + layoutChildrenOnRow(rowStartIdx, childCount, fullRowWidth, xPos, yPos, + lastHorizontalSpacing); + } + } + + private int getHorizontalVerticalSpacing() { + DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + return (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 4, displayMetrics); + } + + private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos, + int yPos, int lastHorizontalSpacing) { + int freeSpace = fullRowWidth - xPos + lastHorizontalSpacing; + xPos = getPaddingLeft() + freeSpace; + + for (int j = startIndex; j < endIndex; ++j) { + View currentChild = getChildAt(j); + currentChild.layout( + xPos, + yPos, + xPos + currentChild.getMeasuredWidth(), + yPos + currentChild.getMeasuredHeight()); + xPos += currentChild.getMeasuredWidth() + + ((LayoutParams) currentChild.getLayoutParams()).mHorizontalSpacing; + } + } + + public static class LayoutParams extends ViewGroup.LayoutParams { + public final int mHorizontalSpacing; + public final int mVerticalSpacing; + + public LayoutParams(int horizontalSpacing, int verticalSpacing, + ViewGroup.LayoutParams viewGroupLayout) { + super(viewGroupLayout); + this.mHorizontalSpacing = horizontalSpacing; + this.mVerticalSpacing = verticalSpacing; + } + + public LayoutParams(int mHorizontalSpacing, int verticalSpacing) { + super(0, 0); + this.mHorizontalSpacing = mHorizontalSpacing; + this.mVerticalSpacing = verticalSpacing; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 0b7bfa8871ee..60c2fa61830c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -29,6 +29,7 @@ import android.view.KeyboardShortcutGroup; import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager.KeyboardShortcutsReceiver; import android.widget.LinearLayout; @@ -153,7 +154,7 @@ public class KeyboardShortcuts { .findViewById(R.id.keyboard_shortcuts_keyword); textView.setText(info.getLabel()); - LinearLayout shortcutItemsContainer = (LinearLayout) shortcutView + ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView .findViewById(R.id.keyboard_shortcuts_item_container); List<String> shortcutKeys = getHumanReadableShortcutKeys(info); final int shortcutKeysSize = shortcutKeys.size(); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 933b402edf57..9ce81ed2f10c 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -56,6 +56,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -174,6 +175,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private final PackageManager mPackageManager; + private final PowerManager mPowerManager; + private final WindowManagerInternal mWindowManagerService; private final SecurityPolicy mSecurityPolicy; @@ -232,6 +235,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public AccessibilityManagerService(Context context) { mContext = context; mPackageManager = mContext.getPackageManager(); + mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWindowManagerService = LocalServices.getService(WindowManagerInternal.class); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); mSecurityPolicy = new SecurityPolicy(); @@ -1802,7 +1806,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private KeyEventDispatcher getKeyEventDispatcher() { if (mKeyEventDispatcher == null) { mKeyEventDispatcher = new KeyEventDispatcher( - mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock); + mMainHandler, MainHandler.MSG_SEND_KEY_EVENT_TO_INPUT_FILTER, mLock, + mPowerManager); } return mKeyEventDispatcher; } @@ -2703,6 +2708,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { final int interrogatingPid = Binder.getCallingPid(); final long identityToken = Binder.clearCallingIdentity(); try { + // Regardless of whether or not the action succeeds, it was generated by an + // accessibility service that is driven by user actions, so note user activity. + mPowerManager.userActivity(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); + connection.performAccessibilityAction(accessibilityNodeId, action, arguments, interactionId, callback, mFetchFlags, interrogatingPid, interrogatingTid); } catch (RemoteException re) { @@ -2724,6 +2734,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } final long identity = Binder.clearCallingIdentity(); try { + mPowerManager.userActivity(SystemClock.uptimeMillis(), + PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); switch (action) { case AccessibilityService.GLOBAL_ACTION_BACK: { sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK); diff --git a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java index 34695654c6f7..e03c16e25fc2 100644 --- a/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java +++ b/services/accessibility/java/com/android/server/accessibility/KeyEventDispatcher.java @@ -17,8 +17,10 @@ package com.android.server.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; +import android.os.Binder; import android.os.Handler; import android.os.Message; +import android.os.PowerManager; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Pools; @@ -69,6 +71,7 @@ public class KeyEventDispatcher { private final Handler mHandlerToSendKeyEventsToInputFilter; private final int mMessageTypeForSendKeyEvent; private final Handler mKeyEventTimeoutHandler; + private final PowerManager mPowerManager; /** * @param handlerToSendKeyEventsToInputFilter The handler to which to post {@code KeyEvent}s @@ -77,9 +80,12 @@ public class KeyEventDispatcher { * message that carries a {@code KeyEvent} to be sent to the input filter * @param lock The lock used for all synchronization in this package. This lock must be held * when calling {@code notifyKeyEventLocked} + * @param powerManager The power manager to alert to user activity if a KeyEvent is processed + * by a service */ public KeyEventDispatcher(Handler handlerToSendKeyEventsToInputFilter, - int messageTypeForSendKeyEvent, Object lock) { + int messageTypeForSendKeyEvent, Object lock, + PowerManager powerManager) { if (InputEventConsistencyVerifier.isInstrumentationEnabled()) { mSentEventsVerifier = new InputEventConsistencyVerifier( this, 0, KeyEventDispatcher.class.getSimpleName()); @@ -91,6 +97,7 @@ public class KeyEventDispatcher { mKeyEventTimeoutHandler = new Handler(mHandlerToSendKeyEventsToInputFilter.getLooper(), new Callback()); mLock = lock; + mPowerManager = powerManager; } /** @@ -165,7 +172,16 @@ public class KeyEventDispatcher { PendingKeyEvent pendingEvent = removeEventFromListLocked(mPendingEventsMap.get(service), sequence); if (pendingEvent != null) { - pendingEvent.handled |= handled; + if (handled && !pendingEvent.handled) { + pendingEvent.handled = handled; + final long identity = Binder.clearCallingIdentity(); + try { + mPowerManager.userActivity(pendingEvent.event.getEventTime(), + PowerManager.USER_ACTIVITY_EVENT_ACCESSIBILITY, 0); + } finally { + Binder.restoreCallingIdentity(identity); + } + } removeReferenceToPendingEventLocked(pendingEvent); } } @@ -241,7 +257,7 @@ public class KeyEventDispatcher { int policyFlags = pendingEvent.policyFlags | WindowManagerPolicy.FLAG_PASS_TO_USER; mHandlerToSendKeyEventsToInputFilter .obtainMessage(mMessageTypeForSendKeyEvent, policyFlags, 0, pendingEvent.event) - .sendToTarget(); + .sendToTarget(); } else { pendingEvent.event.recycle(); } diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 7209814312af..fb1cda7edb54 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -37,10 +37,7 @@ import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.os.Environment; import android.os.RemoteException; -import android.os.SystemClock; import android.os.UserHandle; -import android.provider.Settings.System; -import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -116,6 +113,7 @@ class RecentTasks extends ArrayList<TaskRecord> { // on file when we loaded them. if (mPersistedTaskIds.get(userId) == null) { mPersistedTaskIds.put(userId, mTaskPersister.loadPersistedTaskIdsForUser(userId)); + Slog.i(TAG, "Loaded persisted task ids for user " + userId); } } @@ -146,6 +144,12 @@ class RecentTasks extends ArrayList<TaskRecord> { TaskRecord task = get(i); if (task.isPersistable && (task.stack == null || !task.stack.isHomeStack())) { // Set of persisted taskIds for task.userId should not be null here + // TODO Investigate why it can happen. For now initialize with an empty set + if (mPersistedTaskIds.get(task.userId) == null) { + Slog.wtf(TAG, "No task ids found for userId " + task.userId + ". task=" + task + + " mPersistedTaskIds=" + mPersistedTaskIds); + mPersistedTaskIds.put(task.userId, new SparseBooleanArray()); + } mPersistedTaskIds.get(task.userId).put(task.taskId, true); } } diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java index 11fd3bc61ca6..ceb7db6b8523 100644 --- a/services/core/java/com/android/server/am/TaskPersister.java +++ b/services/core/java/com/android/server/am/TaskPersister.java @@ -88,6 +88,7 @@ public class TaskPersister { private final ActivityStackSupervisor mStackSupervisor; private final RecentTasks mRecentTasks; private final SparseArray<SparseBooleanArray> mTaskIdsInFile = new SparseArray<>(); + private final File mTaskIdsDir; /** * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes @@ -139,12 +140,22 @@ public class TaskPersister { } } + mTaskIdsDir = new File(Environment.getDataDirectory(), "system_de"); mStackSupervisor = stackSupervisor; mService = service; mRecentTasks = recentTasks; mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread"); } + @VisibleForTesting + TaskPersister(File workingDir) { + mTaskIdsDir = workingDir; + mStackSupervisor = null; + mService = null; + mRecentTasks = null; + mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThreadTest"); + } + void startPersisting() { if (!mLazyTaskWriterThread.isAlive()) { mLazyTaskWriterThread.start(); @@ -207,8 +218,8 @@ public class TaskPersister { return persistedTaskIds.clone(); } - private void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, - int userId) { + @VisibleForTesting + void maybeWritePersistedTaskIdsForUser(@NonNull SparseBooleanArray taskIds, int userId) { if (userId < 0) { return; } @@ -565,8 +576,12 @@ public class TaskPersister { return BitmapFactory.decodeFile(filename); } - static File getUserPersistedTaskIdsFile(int userId) { - return new File(Environment.getDataSystemDeDirectory(userId), PERSISTED_TASK_IDS_FILENAME); + private File getUserPersistedTaskIdsFile(int userId) { + File userTaskIdsDir = new File(mTaskIdsDir, String.valueOf(userId)); + if (!userTaskIdsDir.exists() && !userTaskIdsDir.mkdirs()) { + Slog.e(TAG, "Error while creating user directory: " + userTaskIdsDir); + } + return new File(userTaskIdsDir, PERSISTED_TASK_IDS_FILENAME); } static File getUserTasksDir(int userId) { diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index b4df689b14a8..f471af6be143 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1384,8 +1384,13 @@ public class AudioService extends IAudioService.Stub { synchronized (mHdmiPlaybackClient) { int keyCode = (direction == -1) ? KeyEvent.KEYCODE_VOLUME_DOWN : KeyEvent.KEYCODE_VOLUME_UP; - mHdmiPlaybackClient.sendKeyEvent(keyCode, true); - mHdmiPlaybackClient.sendKeyEvent(keyCode, false); + final long ident = Binder.clearCallingIdentity(); + try { + mHdmiPlaybackClient.sendKeyEvent(keyCode, true); + mHdmiPlaybackClient.sendKeyEvent(keyCode, false); + } finally { + Binder.restoreCallingIdentity(ident); + } } } } diff --git a/services/core/java/com/android/server/connectivity/ApfFilter.java b/services/core/java/com/android/server/connectivity/ApfFilter.java index 8195319dd289..d62a0b3fb090 100644 --- a/services/core/java/com/android/server/connectivity/ApfFilter.java +++ b/services/core/java/com/android/server/connectivity/ApfFilter.java @@ -90,7 +90,7 @@ public class ApfFilter { } private static final String TAG = "ApfFilter"; - private static final boolean VDBG = true; + private static final boolean VDBG = false; private final ConnectivityService mConnectivityService; private final NetworkAgentInfo mNai; diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 863a5ed802f2..2f0532a249dc 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -550,7 +550,7 @@ public class LauncherAppsService extends SystemService { } } - void postToPackageMonitor(Runnable r) { + void postToPackageMonitorHandler(Runnable r) { mPackageMonitor.getRegisteredHandler().post(r); } @@ -694,7 +694,7 @@ public class LauncherAppsService extends SystemService { @Override public void onShortcutChanged(@NonNull String packageName, @UserIdInt int userId) { - postToPackageMonitor(() -> onShortcutChangedInner(packageName, userId)); + postToPackageMonitorHandler(() -> onShortcutChangedInner(packageName, userId)); } private void onShortcutChangedInner(@NonNull String packageName, diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 39aa45532a3c..e831bb1a67ee 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -44,6 +44,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Environment; import android.os.Handler; +import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.PersistableBundle; import android.os.Process; @@ -96,20 +97,18 @@ import java.util.function.Predicate; /** * TODO: * - * - Detect when already registered instances are passed to APIs again, which might break - * internal bitmap handling. + * - Default launcher check does take a few ms. Worth caching. * * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res * -> Need to scan all packages when a user starts too. * -> Clear data -> remove all dynamic? but not the pinned? * - * - Pinned per each launcher package (multiple launchers) - * - * - Make save async (should we?) - * * - Scan and remove orphan bitmaps (just in case). * * - Backup & restore + * + * - Detect when already registered instances are passed to APIs again, which might break + * internal bitmap handling. */ public class ShortcutService extends IShortcutService.Stub { static final String TAG = "ShortcutService"; @@ -138,7 +137,8 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting static final int DEFAULT_ICON_PERSIST_QUALITY = 100; - private static final int SAVE_DELAY_MS = 5000; // in milliseconds. + @VisibleForTesting + static final int DEFAULT_SAVE_DELAY_MS = 3000; @VisibleForTesting static final String FILENAME_BASE_STATE = "shortcut_service.xml"; @@ -151,36 +151,19 @@ public class ShortcutService extends IShortcutService.Stub { static final String DIRECTORY_BITMAPS = "bitmaps"; - static final String TAG_ROOT = "root"; - static final String TAG_USER = "user"; - static final String TAG_PACKAGE = "package"; - static final String TAG_LAST_RESET_TIME = "last_reset_time"; - static final String TAG_INTENT_EXTRAS = "intent-extras"; - static final String TAG_EXTRAS = "extras"; - static final String TAG_SHORTCUT = "shortcut"; - static final String TAG_LAUNCHER = "launcher"; - static final String TAG_PIN = "pin"; - static final String TAG_LAUNCHER_PINS = "launcher-pins"; - - static final String ATTR_VALUE = "value"; - static final String ATTR_NAME = "name"; - static final String ATTR_DYNAMIC_COUNT = "dynamic-count"; - static final String ATTR_CALL_COUNT = "call-count"; - static final String ATTR_LAST_RESET = "last-reset"; - static final String ATTR_ID = "id"; - static final String ATTR_ACTIVITY = "activity"; - static final String ATTR_TITLE = "title"; - static final String ATTR_INTENT = "intent"; - static final String ATTR_WEIGHT = "weight"; - static final String ATTR_TIMESTAMP = "timestamp"; - static final String ATTR_FLAGS = "flags"; - static final String ATTR_ICON_RES = "icon-res"; - static final String ATTR_BITMAP_PATH = "bitmap-path"; - static final String ATTR_PACKAGE_NAME = "package-name"; + private static final String TAG_ROOT = "root"; + private static final String TAG_LAST_RESET_TIME = "last_reset_time"; + + private static final String ATTR_VALUE = "value"; @VisibleForTesting interface ConfigConstants { /** + * Key name for the save delay, in milliseconds. (int) + */ + String KEY_SAVE_DELAY_MILLIS = "save_delay_ms"; + + /** * Key name for the throttling reset interval, in seconds. (long) */ String KEY_RESET_INTERVAL_SEC = "reset_interval_sec"; @@ -257,12 +240,22 @@ public class ShortcutService extends IShortcutService.Stub { private CompressFormat mIconPersistFormat; private int mIconPersistQuality; + private int mSaveDelayMillis; + private final PackageManagerInternal mPackageManagerInternal; + @GuardedBy("mLock") + private List<Integer> mDirtyUserIds = new ArrayList<>(); + public ShortcutService(Context context) { + this(context, BackgroundThread.get().getLooper()); + } + + @VisibleForTesting + ShortcutService(Context context, Looper looper) { mContext = Preconditions.checkNotNull(context); LocalServices.addService(ShortcutServiceInternal.class, new LocalService()); - mHandler = new Handler(BackgroundThread.get().getLooper()); + mHandler = new Handler(looper); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } @@ -290,7 +283,7 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void onCleanupUser(int userHandle) { synchronized (mService.mLock) { - mService.onCleanupUserInner(userHandle); + mService.onCleanupUserLocked(userHandle); } } @@ -321,7 +314,10 @@ public class ShortcutService extends IShortcutService.Stub { } /** lifecycle event */ - void onCleanupUserInner(int userId) { + void onCleanupUserLocked(int userId) { + // Save all dirty information. + saveDirtyInfo(); + // Unload mUsers.delete(userId); } @@ -367,6 +363,9 @@ public class ShortcutService extends IShortcutService.Stub { result = false; } + mSaveDelayMillis = (int) parser.getLong(ConfigConstants.KEY_SAVE_DELAY_MILLIS, + DEFAULT_SAVE_DELAY_MS); + mResetInterval = parser.getLong( ConfigConstants.KEY_RESET_INTERVAL_SEC, DEFAULT_RESET_INTERVAL_SEC) * 1000L; @@ -508,7 +507,7 @@ public class ShortcutService extends IShortcutService.Stub { void saveBaseStateLocked() { final AtomicFile file = getBaseStateFile(); if (DEBUG) { - Slog.i(TAG, "Saving to " + file.getBaseFile()); + Slog.d(TAG, "Saving to " + file.getBaseFile()); } FileOutputStream outs = null; @@ -541,7 +540,7 @@ public class ShortcutService extends IShortcutService.Stub { final AtomicFile file = getBaseStateFile(); if (DEBUG) { - Slog.i(TAG, "Loading from " + file.getBaseFile()); + Slog.d(TAG, "Loading from " + file.getBaseFile()); } try (FileInputStream in = file.openRead()) { XmlPullParser parser = Xml.newPullParser(); @@ -586,7 +585,7 @@ public class ShortcutService extends IShortcutService.Stub { private void saveUserLocked(@UserIdInt int userId) { final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); if (DEBUG) { - Slog.i(TAG, "Saving to " + path); + Slog.d(TAG, "Saving to " + path); } path.mkdirs(); final AtomicFile file = new AtomicFile(path); @@ -619,7 +618,7 @@ public class ShortcutService extends IShortcutService.Stub { private UserShortcuts loadUserLocked(@UserIdInt int userId) { final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES); if (DEBUG) { - Slog.i(TAG, "Loading from " + path); + Slog.d(TAG, "Loading from " + path); } final AtomicFile file = new AtomicFile(path); @@ -628,7 +627,7 @@ public class ShortcutService extends IShortcutService.Stub { in = file.openRead(); } catch (FileNotFoundException e) { if (DEBUG) { - Slog.i(TAG, "Not found " + path); + Slog.d(TAG, "Not found " + path); } return null; } @@ -649,7 +648,7 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, String.format("depth=%d type=%d name=%s", depth, type, tag)); } - if ((depth == 1) && TAG_USER.equals(tag)) { + if ((depth == 1) && UserShortcuts.TAG_ROOT.equals(tag)) { ret = UserShortcuts.loadFromXml(parser, userId); continue; } @@ -664,29 +663,58 @@ public class ShortcutService extends IShortcutService.Stub { } } - // TODO Actually make it async. private void scheduleSaveBaseState() { + scheduleSave(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state. + } + + void scheduleSaveUser(@UserIdInt int userId) { + scheduleSave(userId); + } + + // In order to re-schedule, we need to reuse the same instance, so keep it in final. + private final Runnable mSaveDirtyInfoRunner = this::saveDirtyInfo; + + private void scheduleSave(@UserIdInt int userId) { + if (DEBUG) { + Slog.d(TAG, "Scheduling to save for " + userId); + } synchronized (mLock) { - saveBaseStateLocked(); + if (!mDirtyUserIds.contains(userId)) { + mDirtyUserIds.add(userId); + } } + // If already scheduled, remove that and re-schedule in N seconds. + mHandler.removeCallbacks(mSaveDirtyInfoRunner); + mHandler.postDelayed(mSaveDirtyInfoRunner, mSaveDelayMillis); } - // TODO Actually make it async. - void scheduleSaveUser(@UserIdInt int userId) { + @VisibleForTesting + void saveDirtyInfo() { + if (DEBUG) { + Slog.d(TAG, "saveDirtyInfo"); + } synchronized (mLock) { - saveUserLocked(userId); + for (int i = mDirtyUserIds.size() - 1; i >= 0; i--) { + final int userId = mDirtyUserIds.get(i); + if (userId == UserHandle.USER_NULL) { // USER_NULL for base state. + saveBaseStateLocked(); + } else { + saveUserLocked(userId); + } + } + mDirtyUserIds.clear(); } } /** Return the last reset time. */ long getLastResetTimeLocked() { - updateTimes(); + updateTimesLocked(); return mRawLastResetTime; } /** Return the next reset time. */ long getNextResetTimeLocked() { - updateTimes(); + updateTimesLocked(); return mRawLastResetTime + mResetInterval; } @@ -697,7 +725,7 @@ public class ShortcutService extends IShortcutService.Stub { /** * Update the last reset time. */ - private void updateTimes() { + private void updateTimesLocked() { final long now = injectCurrentTimeMillis(); @@ -709,13 +737,14 @@ public class ShortcutService extends IShortcutService.Stub { } else if (now < mRawLastResetTime) { // Clock rewound. if (isClockValid(now)) { + Slog.w(TAG, "Clock rewound"); // TODO Randomize?? mRawLastResetTime = now; } } else { - // TODO Do it properly. - while ((mRawLastResetTime + mResetInterval) <= now) { - mRawLastResetTime += mResetInterval; + if ((mRawLastResetTime + mResetInterval) <= now) { + final long offset = mRawLastResetTime % mResetInterval; + mRawLastResetTime = ((now / mResetInterval) * mResetInterval) + offset; } } if (prevLastResetTime != mRawLastResetTime) { @@ -1895,6 +1924,11 @@ public class ShortcutService extends IShortcutService.Stub { class UserShortcuts { private static final String TAG = ShortcutService.TAG; + static final String TAG_ROOT = "user"; + private static final String TAG_LAUNCHER = "launcher"; + + private static final String ATTR_VALUE = "value"; + @UserIdInt final int mUserId; @@ -1935,9 +1969,9 @@ class UserShortcuts { } public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException { - out.startTag(null, ShortcutService.TAG_USER); + out.startTag(null, TAG_ROOT); - ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER, + ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLauncherComponent); final int lsize = mLaunchers.size(); @@ -1950,7 +1984,7 @@ class UserShortcuts { mPackages.valueAt(i).saveToXml(out); } - out.endTag(null, ShortcutService.TAG_USER); + out.endTag(null, TAG_ROOT); } public static UserShortcuts loadFromXml(XmlPullParser parser, int userId) @@ -1967,12 +2001,12 @@ class UserShortcuts { final int depth = parser.getDepth(); final String tag = parser.getName(); switch (tag) { - case ShortcutService.TAG_LAUNCHER: { + case TAG_LAUNCHER: { ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute( - parser, ShortcutService.ATTR_VALUE); + parser, ATTR_VALUE); continue; } - case ShortcutService.TAG_PACKAGE: { + case PackageShortcuts.TAG_ROOT: { final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId); // Don't use addShortcut(), we don't need to save the icon. @@ -1980,7 +2014,7 @@ class UserShortcuts { continue; } - case ShortcutService.TAG_LAUNCHER_PINS: { + case LauncherShortcuts.TAG_ROOT: { final LauncherShortcuts shortcuts = LauncherShortcuts.loadFromXml(parser, userId); @@ -2036,6 +2070,14 @@ class UserShortcuts { class LauncherShortcuts { private static final String TAG = ShortcutService.TAG; + static final String TAG_ROOT = "launcher-pins"; + + private static final String TAG_PACKAGE = "package"; + private static final String TAG_PIN = "pin"; + + private static final String ATTR_VALUE = "value"; + private static final String ATTR_PACKAGE_NAME = "package-name"; + @UserIdInt final int mUserId; @@ -2093,25 +2135,25 @@ class LauncherShortcuts { * Persist. */ public void saveToXml(XmlSerializer out) throws IOException { - out.startTag(null, ShortcutService.TAG_LAUNCHER_PINS); - ShortcutService.writeAttr(out, ShortcutService.ATTR_PACKAGE_NAME, + out.startTag(null, TAG_ROOT); + ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, mPackageName); final int size = mPinnedShortcuts.size(); for (int i = 0; i < size; i++) { - out.startTag(null, ShortcutService.TAG_PACKAGE); - ShortcutService.writeAttr(out, ShortcutService.ATTR_PACKAGE_NAME, + out.startTag(null, TAG_PACKAGE); + ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, mPinnedShortcuts.keyAt(i)); final ArraySet<String> ids = mPinnedShortcuts.valueAt(i); final int idSize = ids.size(); for (int j = 0; j < idSize; j++) { - ShortcutService.writeTagValue(out, ShortcutService.TAG_PIN, ids.valueAt(j)); + ShortcutService.writeTagValue(out, TAG_PIN, ids.valueAt(j)); } - out.endTag(null, ShortcutService.TAG_PACKAGE); + out.endTag(null, TAG_PACKAGE); } - out.endTag(null, ShortcutService.TAG_LAUNCHER_PINS); + out.endTag(null, TAG_ROOT); } /** @@ -2120,7 +2162,7 @@ class LauncherShortcuts { public static LauncherShortcuts loadFromXml(XmlPullParser parser, int userId) throws IOException, XmlPullParserException { final String launcherPackageName = ShortcutService.parseStringAttribute(parser, - ShortcutService.ATTR_PACKAGE_NAME); + ATTR_PACKAGE_NAME); final LauncherShortcuts ret = new LauncherShortcuts(userId, launcherPackageName); @@ -2135,16 +2177,16 @@ class LauncherShortcuts { final int depth = parser.getDepth(); final String tag = parser.getName(); switch (tag) { - case ShortcutService.TAG_PACKAGE: { + case TAG_PACKAGE: { final String packageName = ShortcutService.parseStringAttribute(parser, - ShortcutService.ATTR_PACKAGE_NAME); + ATTR_PACKAGE_NAME); ids = new ArraySet<>(); ret.mPinnedShortcuts.put(packageName, ids); continue; } - case ShortcutService.TAG_PIN: { + case TAG_PIN: { ids.add(ShortcutService.parseStringAttribute(parser, - ShortcutService.ATTR_VALUE)); + ATTR_VALUE)); continue; } } @@ -2189,6 +2231,25 @@ class LauncherShortcuts { class PackageShortcuts { private static final String TAG = ShortcutService.TAG; + static final String TAG_ROOT = "package"; + private static final String TAG_INTENT_EXTRAS = "intent-extras"; + private static final String TAG_EXTRAS = "extras"; + private static final String TAG_SHORTCUT = "shortcut"; + + private static final String ATTR_NAME = "name"; + private static final String ATTR_DYNAMIC_COUNT = "dynamic-count"; + private static final String ATTR_CALL_COUNT = "call-count"; + private static final String ATTR_LAST_RESET = "last-reset"; + private static final String ATTR_ID = "id"; + private static final String ATTR_ACTIVITY = "activity"; + private static final String ATTR_TITLE = "title"; + private static final String ATTR_INTENT = "intent"; + private static final String ATTR_WEIGHT = "weight"; + private static final String ATTR_TIMESTAMP = "timestamp"; + private static final String ATTR_FLAGS = "flags"; + private static final String ATTR_ICON_RES = "icon-res"; + private static final String ATTR_BITMAP_PATH = "bitmap-path"; + @UserIdInt final int mUserId; @@ -2377,12 +2438,19 @@ class PackageShortcuts { final long now = s.injectCurrentTimeMillis(); if (ShortcutService.isClockValid(now) && mLastResetTime > now) { - // Clock rewound. // TODO Test it + Slog.w(TAG, "Clock rewound"); + // Clock rewound. mLastResetTime = now; + mApiCallCount = 0; + return mApiCallCount; } // If not reset yet, then reset. if (mLastResetTime < last) { + if (ShortcutService.DEBUG) { + Slog.d(TAG, String.format("My last reset=%d, now=%d, last=%d: resetting", + mLastResetTime, now, last)); + } mApiCallCount = 0; mLastResetTime = last; } @@ -2501,58 +2569,58 @@ class PackageShortcuts { } public void saveToXml(@NonNull XmlSerializer out) throws IOException, XmlPullParserException { - out.startTag(null, ShortcutService.TAG_PACKAGE); + out.startTag(null, TAG_ROOT); - ShortcutService.writeAttr(out, ShortcutService.ATTR_NAME, mPackageName); - ShortcutService.writeAttr(out, ShortcutService.ATTR_DYNAMIC_COUNT, mDynamicShortcutCount); - ShortcutService.writeAttr(out, ShortcutService.ATTR_CALL_COUNT, mApiCallCount); - ShortcutService.writeAttr(out, ShortcutService.ATTR_LAST_RESET, mLastResetTime); + ShortcutService.writeAttr(out, ATTR_NAME, mPackageName); + ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount); + ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount); + ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); final int size = mShortcuts.size(); for (int j = 0; j < size; j++) { saveShortcut(out, mShortcuts.valueAt(j)); } - out.endTag(null, ShortcutService.TAG_PACKAGE); + out.endTag(null, TAG_ROOT); } private static void saveShortcut(XmlSerializer out, ShortcutInfo si) throws IOException, XmlPullParserException { - out.startTag(null, ShortcutService.TAG_SHORTCUT); - ShortcutService.writeAttr(out, ShortcutService.ATTR_ID, si.getId()); + out.startTag(null, TAG_SHORTCUT); + ShortcutService.writeAttr(out, ATTR_ID, si.getId()); // writeAttr(out, "package", si.getPackageName()); // not needed - ShortcutService.writeAttr(out, ShortcutService.ATTR_ACTIVITY, si.getActivityComponent()); + ShortcutService.writeAttr(out, ATTR_ACTIVITY, si.getActivityComponent()); // writeAttr(out, "icon", si.getIcon()); // We don't save it. - ShortcutService.writeAttr(out, ShortcutService.ATTR_TITLE, si.getTitle()); - ShortcutService.writeAttr(out, ShortcutService.ATTR_INTENT, si.getIntentNoExtras()); - ShortcutService.writeAttr(out, ShortcutService.ATTR_WEIGHT, si.getWeight()); - ShortcutService.writeAttr(out, ShortcutService.ATTR_TIMESTAMP, + ShortcutService.writeAttr(out, ATTR_TITLE, si.getTitle()); + ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras()); + ShortcutService.writeAttr(out, ATTR_WEIGHT, si.getWeight()); + ShortcutService.writeAttr(out, ATTR_TIMESTAMP, si.getLastChangedTimestamp()); - ShortcutService.writeAttr(out, ShortcutService.ATTR_FLAGS, si.getFlags()); - ShortcutService.writeAttr(out, ShortcutService.ATTR_ICON_RES, si.getIconResourceId()); - ShortcutService.writeAttr(out, ShortcutService.ATTR_BITMAP_PATH, si.getBitmapPath()); + ShortcutService.writeAttr(out, ATTR_FLAGS, si.getFlags()); + ShortcutService.writeAttr(out, ATTR_ICON_RES, si.getIconResourceId()); + ShortcutService.writeAttr(out, ATTR_BITMAP_PATH, si.getBitmapPath()); - ShortcutService.writeTagExtra(out, ShortcutService.TAG_INTENT_EXTRAS, + ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS, si.getIntentPersistableExtras()); - ShortcutService.writeTagExtra(out, ShortcutService.TAG_EXTRAS, si.getExtras()); + ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras()); - out.endTag(null, ShortcutService.TAG_SHORTCUT); + out.endTag(null, TAG_SHORTCUT); } public static PackageShortcuts loadFromXml(XmlPullParser parser, int userId) throws IOException, XmlPullParserException { final String packageName = ShortcutService.parseStringAttribute(parser, - ShortcutService.ATTR_NAME); + ATTR_NAME); final PackageShortcuts ret = new PackageShortcuts(userId, packageName); ret.mDynamicShortcutCount = - ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_DYNAMIC_COUNT); + ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT); ret.mApiCallCount = - ShortcutService.parseIntAttribute(parser, ShortcutService.ATTR_CALL_COUNT); + ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); ret.mLastResetTime = - ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_LAST_RESET); + ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); final int outerDepth = parser.getDepth(); int type; @@ -2564,7 +2632,7 @@ class PackageShortcuts { final int depth = parser.getDepth(); final String tag = parser.getName(); switch (tag) { - case ShortcutService.TAG_SHORTCUT: + case TAG_SHORTCUT: final ShortcutInfo si = parseShortcut(parser, packageName); // Don't use addShortcut(), we don't need to save the icon. @@ -2591,17 +2659,17 @@ class PackageShortcuts { int iconRes; String bitmapPath; - id = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_ID); + id = ShortcutService.parseStringAttribute(parser, ATTR_ID); activityComponent = ShortcutService.parseComponentNameAttribute(parser, - ShortcutService.ATTR_ACTIVITY); - title = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_TITLE); - intent = ShortcutService.parseIntentAttribute(parser, ShortcutService.ATTR_INTENT); - weight = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_WEIGHT); + ATTR_ACTIVITY); + title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE); + intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT); + weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT); lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser, - ShortcutService.ATTR_TIMESTAMP); - flags = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_FLAGS); - iconRes = (int) ShortcutService.parseLongAttribute(parser, ShortcutService.ATTR_ICON_RES); - bitmapPath = ShortcutService.parseStringAttribute(parser, ShortcutService.ATTR_BITMAP_PATH); + ATTR_TIMESTAMP); + flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS); + iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES); + bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH); final int outerDepth = parser.getDepth(); int type; @@ -2617,10 +2685,10 @@ class PackageShortcuts { depth, type, tag)); } switch (tag) { - case ShortcutService.TAG_INTENT_EXTRAS: + case TAG_INTENT_EXTRAS: intentPersistableExtras = PersistableBundle.restoreFromXml(parser); continue; - case ShortcutService.TAG_EXTRAS: + case TAG_EXTRAS: extras = PersistableBundle.restoreFromXml(parser); continue; } diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 2db6b5d4bf90..50699f8ccf4a 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -35,14 +35,14 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; import android.os.UserManager; -import android.provider.Settings; import android.provider.Settings.Global; +import android.provider.Settings; import android.util.AndroidRuntimeException; import android.util.Slog; import android.webkit.IWebViewUpdateService; +import android.webkit.WebViewFactory; import android.webkit.WebViewProviderInfo; import android.webkit.WebViewProviderResponse; -import android.webkit.WebViewFactory; import com.android.server.SystemService; @@ -66,8 +66,6 @@ public class WebViewUpdateService extends SystemService { private int mNumRelroCreationsFinished = 0; // Implies that we need to rerun relro creation because we are using an out-of-date package private boolean mWebViewPackageDirty = false; - // Set to true when the current provider is being replaced - private boolean mCurrentProviderBeingReplaced = false; private boolean mAnyWebViewInstalled = false; private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE; @@ -78,9 +76,11 @@ public class WebViewUpdateService extends SystemService { private WebViewProviderInfo[] mCurrentValidWebViewPackages = null; private BroadcastReceiver mWebViewUpdatedReceiver; + private WebViewUtilityInterface mWebViewUtility; public WebViewUpdateService(Context context) { super(context); + mWebViewUtility = new WebViewUtilityImpl(); } @Override @@ -92,19 +92,10 @@ public class WebViewUpdateService extends SystemService { // the removal of the old package and one representing the addition of the // new package. // In the case where we receive an intent to remove the old version of the - // package that is being replaced we set a flag here and early-out so that we - // don't change provider while replacing the current package (we will instead - // change provider when the new version of the package is being installed). + // package that is being replaced we early-out here so that we don't run the + // update-logic twice. if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) { - synchronized(WebViewUpdateService.this) { - if (mCurrentWebViewPackage == null) return; - - String webViewPackage = "package:" + mCurrentWebViewPackage.packageName; - if (webViewPackage.equals(intent.getDataString())) - mCurrentProviderBeingReplaced = true; - } - return; } @@ -125,7 +116,7 @@ public class WebViewUpdateService extends SystemService { updateFallbackState(context, intent); - for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) { + for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) { String webviewPackage = "package:" + provider.packageName; if (webviewPackage.equals(intent.getDataString())) { @@ -164,11 +155,7 @@ public class WebViewUpdateService extends SystemService { // package that was not the previous provider then we must kill // packages dependent on the old package ourselves. The framework // only kills dependents of packages that are being removed. - try { - ActivityManagerNative.getDefault().killPackageDependents( - oldProviderName, UserHandle.USER_ALL); - } catch (RemoteException e) { - } + mWebViewUtility.killPackageDependents(oldProviderName); } return; } @@ -181,7 +168,7 @@ public class WebViewUpdateService extends SystemService { filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addDataScheme("package"); // Make sure we only receive intents for WebView packages from our config file. - for (WebViewProviderInfo provider : WebViewFactory.getWebViewPackages()) { + for (WebViewProviderInfo provider : mWebViewUtility.getWebViewPackages()) { filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL); } getContext().registerReceiver(mWebViewUpdatedReceiver, filter); @@ -221,7 +208,7 @@ public class WebViewUpdateService extends SystemService { void handleNewUser(int userId) { if (!isFallbackLogicEnabled()) return; - WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages(); + WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages(); WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders); if (fallbackProvider == null) return; boolean existsValidNonFallbackProvider = @@ -239,7 +226,7 @@ public class WebViewUpdateService extends SystemService { void updateFallbackState(final Context context, final Intent intent) { if (!isFallbackLogicEnabled()) return; - WebViewProviderInfo[] webviewProviders = WebViewFactory.getWebViewPackages(); + WebViewProviderInfo[] webviewProviders = mWebViewUtility.getWebViewPackages(); if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED) || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) { @@ -330,10 +317,10 @@ public class WebViewUpdateService extends SystemService { return false; } - private static boolean isFallbackPackage(String packageName) { + private boolean isFallbackPackage(String packageName) { if (packageName == null || !isFallbackLogicEnabled()) return false; - WebViewProviderInfo[] webviewPackages = WebViewFactory.getWebViewPackages(); + WebViewProviderInfo[] webviewPackages = mWebViewUtility.getWebViewPackages(); WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages); return (fallbackProvider != null && packageName.equals(fallbackProvider.packageName)); @@ -370,13 +357,13 @@ public class WebViewUpdateService extends SystemService { PackageInfo newPackage = null; synchronized(this) { oldPackage = mCurrentWebViewPackage; - updateUserSetting(newProviderName); + mWebViewUtility.updateUserSetting(getContext(), newProviderName); try { newPackage = findPreferredWebViewPackage(); if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) { // If we don't perform the user change, revert the settings change. - updateUserSetting(newPackage.packageName); + mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName); return newPackage.packageName; } } catch (WebViewFactory.MissingWebViewPackageException e) { @@ -389,12 +376,8 @@ public class WebViewUpdateService extends SystemService { onWebViewProviderChanged(newPackage); } // Kill apps using the old provider - try { - if (oldPackage != null) { - ActivityManagerNative.getDefault().killPackageDependents( - oldPackage.packageName, UserHandle.USER_ALL); - } - } catch (RemoteException e) { + if (oldPackage != null) { + mWebViewUtility.killPackageDependents(oldPackage.packageName); } return newPackage.packageName; } @@ -406,19 +389,16 @@ public class WebViewUpdateService extends SystemService { private void onWebViewProviderChanged(PackageInfo newPackage) { synchronized(this) { mAnyWebViewInstalled = true; - // If we have changed provider then the replacement of the old provider is - // irrelevant - we can only have chosen a new provider if its package is available. - mCurrentProviderBeingReplaced = false; if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) { mCurrentWebViewPackage = newPackage; - updateUserSetting(newPackage.packageName); + mWebViewUtility.updateUserSetting(getContext(), newPackage.packageName); // The relro creations might 'finish' (not start at all) before // WebViewFactory.onWebViewProviderChanged which means we might not know the number // of started creations before they finish. mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN; mNumRelroCreationsFinished = 0; - mNumRelroCreationsStarted = WebViewFactory.onWebViewProviderChanged(newPackage); + mNumRelroCreationsStarted = mWebViewUtility.onWebViewProviderChanged(newPackage); // If the relro creations finish before we know the number of started creations we // will have to do any cleanup/notifying here. checkIfRelrosDoneLocked(); @@ -435,7 +415,7 @@ public class WebViewUpdateService extends SystemService { * */ private void updateValidWebViewPackages() { List<WebViewProviderInfo> webViewProviders = - new ArrayList<WebViewProviderInfo>(Arrays.asList(WebViewFactory.getWebViewPackages())); + new ArrayList<WebViewProviderInfo>(Arrays.asList(mWebViewUtility.getWebViewPackages())); Iterator<WebViewProviderInfo> it = webViewProviders.iterator(); // remove non-valid packages while(it.hasNext()) { @@ -449,17 +429,6 @@ public class WebViewUpdateService extends SystemService { } } - private static String getUserChosenWebViewProvider() { - return Settings.Global.getString(AppGlobals.getInitialApplication().getContentResolver(), - Settings.Global.WEBVIEW_PROVIDER); - } - - private void updateUserSetting(String newProviderName) { - Settings.Global.putString(getContext().getContentResolver(), - Settings.Global.WEBVIEW_PROVIDER, - newProviderName == null ? "" : newProviderName); - } - /** * Returns either the package info of the WebView provider determined in the following way: * If the user has chosen a provider then use that if it is valid, @@ -470,7 +439,7 @@ public class WebViewUpdateService extends SystemService { private PackageInfo findPreferredWebViewPackage() { WebViewProviderInfo[] providers = mCurrentValidWebViewPackages; - String userChosenProvider = getUserChosenWebViewProvider(); + String userChosenProvider = mWebViewUtility.getUserChosenWebViewProvider(getContext()); // If the user has chosen provider, use that for (WebViewProviderInfo provider : providers) { @@ -504,7 +473,6 @@ public class WebViewUpdateService extends SystemService { private boolean webViewIsReadyLocked() { return !mWebViewPackageDirty && (mNumRelroCreationsStarted == mNumRelroCreationsFinished) - && !mCurrentProviderBeingReplaced // The current package might be replaced though we haven't received an intent declaring // this yet, the following flag makes anyone loading WebView to wait in this case. && mAnyWebViewInstalled; @@ -516,13 +484,8 @@ public class WebViewUpdateService extends SystemService { mWebViewPackageDirty = false; // If we have changed provider since we started the relro creation we need to // redo the whole process using the new package instead. - // Though, if the current provider package is being replaced we don't want to change - // provider here since we will perform the change either when the package is added - // again or when we switch to another provider (whichever comes first). - if (!mCurrentProviderBeingReplaced) { - PackageInfo newPackage = findPreferredWebViewPackage(); - onWebViewProviderChanged(newPackage); - } + PackageInfo newPackage = findPreferredWebViewPackage(); + onWebViewProviderChanged(newPackage); } else { this.notifyAll(); } @@ -597,11 +560,6 @@ public class WebViewUpdateService extends SystemService { // Make sure we return the provider that was used to create the relro file webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage; if (webViewReady) { - } else if (mCurrentProviderBeingReplaced) { - // It is important that we check this flag before the one representing WebView - // being installed, otherwise we might think there is no WebView though the - // current one is just being replaced. - webViewStatus = WebViewFactory.LIBLOAD_WEBVIEW_BEING_REPLACED; } else if (!mAnyWebViewInstalled) { webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES; } else { @@ -641,6 +599,11 @@ public class WebViewUpdateService extends SystemService { } @Override // Binder call + public WebViewProviderInfo[] getAllWebViewPackages() { + return WebViewUpdateService.this.mWebViewUtility.getWebViewPackages(); + } + + @Override // Binder call public String getCurrentWebViewPackageName() { synchronized(WebViewUpdateService.this) { if (WebViewUpdateService.this.mCurrentWebViewPackage == null) @@ -651,7 +614,7 @@ public class WebViewUpdateService extends SystemService { @Override // Binder call public boolean isFallbackPackage(String packageName) { - return WebViewUpdateService.isFallbackPackage(packageName); + return WebViewUpdateService.this.isFallbackPackage(packageName); } @Override // Binder call diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java new file mode 100644 index 000000000000..4dbd02d1ede5 --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUtilityImpl.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2016 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.server.webkit; + +import android.app.ActivityManagerNative; +import android.app.AppGlobals; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.res.XmlResourceParser; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.AndroidRuntimeException; +import android.util.Log; +import android.webkit.WebViewFactory; +import android.webkit.WebViewFactory.MissingWebViewPackageException; +import android.webkit.WebViewProviderInfo; + +import com.android.internal.util.XmlUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.xmlpull.v1.XmlPullParserException; + +/** + * Default implementation for the WebView preparation Utility interface. + * @hide + */ +public class WebViewUtilityImpl implements WebViewUtilityInterface { + private static final String TAG = WebViewUtilityImpl.class.getSimpleName(); + private static final String TAG_START = "webviewproviders"; + private static final String TAG_WEBVIEW_PROVIDER = "webviewprovider"; + private static final String TAG_PACKAGE_NAME = "packageName"; + private static final String TAG_DESCRIPTION = "description"; + // Whether or not the provider must be explicitly chosen by the user to be used. + private static final String TAG_AVAILABILITY = "availableByDefault"; + private static final String TAG_SIGNATURE = "signature"; + private static final String TAG_FALLBACK = "isFallback"; + + /** + * Returns all packages declared in the framework resources as potential WebView providers. + * @hide + * */ + @Override + public WebViewProviderInfo[] getWebViewPackages() { + int numFallbackPackages = 0; + XmlResourceParser parser = null; + List<WebViewProviderInfo> webViewProviders = new ArrayList<WebViewProviderInfo>(); + try { + parser = AppGlobals.getInitialApplication().getResources().getXml( + com.android.internal.R.xml.config_webview_packages); + XmlUtils.beginDocument(parser, TAG_START); + while(true) { + XmlUtils.nextElement(parser); + String element = parser.getName(); + if (element == null) { + break; + } + if (element.equals(TAG_WEBVIEW_PROVIDER)) { + String packageName = parser.getAttributeValue(null, TAG_PACKAGE_NAME); + if (packageName == null) { + throw new MissingWebViewPackageException( + "WebView provider in framework resources missing package name"); + } + String description = parser.getAttributeValue(null, TAG_DESCRIPTION); + if (description == null) { + throw new MissingWebViewPackageException( + "WebView provider in framework resources missing description"); + } + boolean availableByDefault = "true".equals( + parser.getAttributeValue(null, TAG_AVAILABILITY)); + boolean isFallback = "true".equals( + parser.getAttributeValue(null, TAG_FALLBACK)); + WebViewProviderInfo currentProvider = + new WebViewProviderInfo(packageName, description, availableByDefault, + isFallback, readSignatures(parser)); + if (currentProvider.isFallbackPackage()) { + numFallbackPackages++; + if (numFallbackPackages > 1) { + throw new AndroidRuntimeException( + "There can be at most one webview fallback package."); + } + } + webViewProviders.add(currentProvider); + } + else { + Log.e(TAG, "Found an element that is not a webview provider"); + } + } + } catch(XmlPullParserException e) { + throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); + } catch(IOException e) { + throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e); + } finally { + if (parser != null) parser.close(); + } + return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]); + } + + /** + * Reads all signatures at the current depth (within the current provider) from the XML parser. + */ + private static String[] readSignatures(XmlResourceParser parser) throws IOException, + XmlPullParserException { + List<String> signatures = new ArrayList<String>(); + int outerDepth = parser.getDepth(); + while(XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals(TAG_SIGNATURE)) { + // Parse the value within the signature tag + String signature = parser.nextText(); + signatures.add(signature); + } else { + Log.e(TAG, "Found an element in a webview provider that is not a signature"); + } + } + return signatures.toArray(new String[signatures.size()]); + } + + @Override + public int onWebViewProviderChanged(PackageInfo packageInfo) { + return WebViewFactory.onWebViewProviderChanged(packageInfo); + } + + @Override + public String getUserChosenWebViewProvider(Context context) { + return Settings.Global.getString(context.getContentResolver(), + Settings.Global.WEBVIEW_PROVIDER); + } + + @Override + public void updateUserSetting(Context context, String newProviderName) { + Settings.Global.putString(context.getContentResolver(), + Settings.Global.WEBVIEW_PROVIDER, + newProviderName == null ? "" : newProviderName); + } + + @Override + public void killPackageDependents(String packageName) { + try { + ActivityManagerNative.getDefault().killPackageDependents(packageName, + UserHandle.USER_ALL); + } catch (RemoteException e) { + } + } +} diff --git a/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java new file mode 100644 index 000000000000..1919f400c29a --- /dev/null +++ b/services/core/java/com/android/server/webkit/WebViewUtilityInterface.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 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.server.webkit; + +import android.webkit.WebViewProviderInfo; +import android.content.Context; +import android.content.pm.PackageInfo; + +/** + * Utility interface for the WebViewUpdateService. + * This interface provides a way to test the WebView preparation mechanism - during normal use this + * interface is implemented using calls to the Android framework, but by providing an alternative + * implementation we can test the WebView preparation logic without reaching other framework code. + * @hide + */ +public interface WebViewUtilityInterface { + public WebViewProviderInfo[] getWebViewPackages(); + public int onWebViewProviderChanged(PackageInfo packageInfo); + + public String getUserChosenWebViewProvider(Context context); + public void updateUserSetting(Context context, String newProviderName); + public void killPackageDependents(String packageName); +} diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java index f2f555b553d5..8ab18788beaf 100644 --- a/services/print/java/com/android/server/print/UserState.java +++ b/services/print/java/com/android/server/print/UserState.java @@ -443,10 +443,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { @Nullable List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); - // No services - nothing to do. - if (mActiveServices.isEmpty()) { - return; - } + // No session - nothing to do. if (mPrinterDiscoverySession == null) { return; @@ -460,10 +457,7 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { public void stopPrinterDiscovery(@NonNull IPrinterDiscoveryObserver observer) { synchronized (mLock) { throwIfDestroyedLocked(); - // No services - nothing to do. - if (mActiveServices.isEmpty()) { - return; - } + // No session - nothing to do. if (mPrinterDiscoverySession == null) { return; diff --git a/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java new file mode 100644 index 000000000000..e440a0d603d2 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/TaskPersisterTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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.server.am; + +import android.content.pm.UserInfo; +import android.os.Environment; +import android.os.UserHandle; +import android.os.UserManager; +import android.test.AndroidTestCase; +import android.util.Log; +import android.util.SparseBooleanArray; + +import com.android.server.am.TaskPersister; + +import java.io.File; +import java.util.Random; + +public class TaskPersisterTest extends AndroidTestCase { + private static final String TEST_USER_NAME = "AM-Test-User"; + + private TaskPersister mTaskPersister; + private int testUserId; + private UserManager mUserManager; + + @Override + public void setUp() throws Exception { + super.setUp(); + mUserManager = UserManager.get(getContext()); + mTaskPersister = new TaskPersister(getContext().getFilesDir()); + testUserId = createUser(TEST_USER_NAME, 0); + } + + @Override + public void tearDown() throws Exception { + super.tearDown(); + mTaskPersister.unloadUserDataFromMemory(testUserId); + removeUser(testUserId); + } + + private int getRandomTaskIdForUser(int userId) { + int taskId = (int) (Math.random() * UserHandle.PER_USER_RANGE); + taskId += UserHandle.PER_USER_RANGE * userId; + return taskId; + } + + public void testTaskIdsPersistence() { + SparseBooleanArray taskIdsOnFile = mTaskPersister.loadPersistedTaskIdsForUser(testUserId); + for (int i = 0; i < 100; i++) { + taskIdsOnFile.put(getRandomTaskIdForUser(testUserId), true); + } + mTaskPersister.maybeWritePersistedTaskIdsForUser(taskIdsOnFile, testUserId); + SparseBooleanArray newTaskIdsOnFile = mTaskPersister + .loadPersistedTaskIdsForUser(testUserId); + assertTrue("TaskIds written differ from TaskIds read back from file", + taskIdsOnFile.equals(newTaskIdsOnFile)); + } + + private int createUser(String name, int flags) { + UserInfo user = mUserManager.createUser(name, flags); + if (user == null) { + fail("Error while creating the test user: " + TEST_USER_NAME); + } + return user.id; + } + + private void removeUser(int userId) { + if (!mUserManager.removeUser(userId)) { + fail("Error while removing the test user: " + TEST_USER_NAME); + } + } +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java index 5c5113901cad..ad86fd08cea9 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java @@ -166,8 +166,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { private final class ShortcutServiceTestable extends ShortcutService { final ServiceContext mContext; - public ShortcutServiceTestable(ServiceContext context) { - super(context); + public ShortcutServiceTestable(ServiceContext context, Looper looper) { + super(context, looper); mContext = context; } @@ -248,7 +248,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase { @Override void postToHandler(Runnable r) { final long token = mContext.injectClearCallingIdentity(); - r.run(); + super.postToHandler(r); + try { + runTestOnUiThread(() -> {}); + } catch (Throwable e) { + fail("runTestOnUiThread failed: " + e); + } mContext.injectRestoreCallingIdentity(token); } @@ -302,7 +307,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { } @Override - void postToPackageMonitor(Runnable r) { + void postToPackageMonitorHandler(Runnable r) { final long token = mContext.injectClearCallingIdentity(); r.run(); mContext.injectRestoreCallingIdentity(token); @@ -368,7 +373,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { private static final int USER_10 = 10; private static final int USER_11 = 11; - private static final long START_TIME = 1234560000000L; + private static final long START_TIME = 1440000000101L; private static final long INTERVAL = 10000; @@ -424,7 +429,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { LocalServices.removeServiceForTest(ShortcutServiceInternal.class); // Instantiate targets. - mService = new ShortcutServiceTestable(mServiceContext); + mService = new ShortcutServiceTestable(mServiceContext, Looper.getMainLooper()); mManager = new ShortcutManagerTestable(mClientContext, mService); mInternal = LocalServices.getService(ShortcutServiceInternal.class); @@ -509,6 +514,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { * For debugging, dump the main state file on logcat. */ private void dumpBaseStateFile() { + mService.saveDirtyInfo(); dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath() + "/system/" + ShortcutService.FILENAME_BASE_STATE); } @@ -517,6 +523,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { * For debugging, dump per-user state file on logcat. */ private void dumpUserFile(int userId) { + mService.saveDirtyInfo(); dumpFileOnLogcat(mInjectedFilePathRoot.getAbsolutePath() + "/user-" + userId + "/" + ShortcutService.FILENAME_USER_PACKAGES); @@ -890,6 +897,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { dumpBaseStateFile(); + mService.saveDirtyInfo(); + // Restore. initService(); @@ -1125,51 +1134,102 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); mInjectedCurrentTimeLillis++; assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(1, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); mInjectedCurrentTimeLillis++; assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(0, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Reached the max mInjectedCurrentTimeLillis++; assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(0, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Still throttled mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1; assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(0, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); // Now it should work. mInjectedCurrentTimeLillis++; assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); mInjectedCurrentTimeLillis++; assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(1, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); mInjectedCurrentTimeLillis++; assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(0, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); mInjectedCurrentTimeLillis++; assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(0, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime()); // 4 days later... mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL; assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime()); + + mInjectedCurrentTimeLillis++; + assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); + assertEquals(1, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime()); - // Make sure getRemainingCallCount() itself gets reset withou calling setDynamicShortcuts(). + // Make sure getRemainingCallCount() itself gets reset without calling setDynamicShortcuts(). mInjectedCurrentTimeLillis = START_TIME + 8 * INTERVAL; assertEquals(3, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime()); + + mInjectedCurrentTimeLillis++; + assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); + assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime()); + } + + public void testThrottling_rewind() { + final ShortcutInfo si1 = makeShortcut("shortcut1"); + + assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); + assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); + + mInjectedCurrentTimeLillis = 12345; // Clock reset! + + // Since the clock looks invalid, the counter shouldn't have reset. + assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); + + // Forward again. Still haven't reset yet. + mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1; + assertEquals(2, mManager.getRemainingCallCount()); + assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); + + // Now rewind -- this will reset the counters. + mInjectedCurrentTimeLillis = START_TIME - 100000; + assertEquals(3, mManager.getRemainingCallCount()); + + assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); + assertEquals(2, mManager.getRemainingCallCount()); + + // Forward again, should be reset now. + mInjectedCurrentTimeLillis += INTERVAL; + assertEquals(3, mManager.getRemainingCallCount()); } public void testThrottling_perPackage() { @@ -1291,6 +1351,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { "none"); // Re-initialize and load from the files. + mService.saveDirtyInfo(); initService(); // Load from launcher. @@ -1943,6 +2004,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { }); // Re-initialize and load from the files. + mService.saveDirtyInfo(); initService(); runWithCaller(LAUNCHER_1, USER_0, () -> { @@ -2270,6 +2332,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { dumpUserFile(0); // Restore. + mService.saveDirtyInfo(); initService(); assertEquals(0, mManager.getDynamicShortcuts().size()); @@ -2366,6 +2429,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService, new ComponentName("pkg1", "class")); // Restore. + mService.saveDirtyInfo(); initService(); // Before the load, the map should be empty. @@ -2414,7 +2478,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertNull(mService.getShortcutsForTest().get(USER_10).getLauncherComponent()); // Try stopping the user - mService.onCleanupUserInner(USER_10); + mService.onCleanupUserLocked(USER_10); // Now it's unloaded. assertEquals(1, mService.getShortcutsForTest().size()); diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java index 2ab0525a33c4..a965342982f5 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -44,6 +44,7 @@ final public class Log { public static final boolean ERROR = isLoggable(android.util.Log.ERROR); private static MessageDigest sMessageDigest; + private static final Object sMessageDigestLock = new Object(); private Log() {} @@ -57,7 +58,9 @@ final public class Log { } catch (NoSuchAlgorithmException e) { md = null; } - sMessageDigest = md; + synchronized (sMessageDigestLock) { + sMessageDigest = md; + } return null; } }.execute(); @@ -187,13 +190,15 @@ final public class Log { } private static String secureHash(byte[] input) { - if (sMessageDigest != null) { - sMessageDigest.reset(); - sMessageDigest.update(input); - byte[] result = sMessageDigest.digest(); - return encodeHex(result); - } else { - return "Uninitialized SHA1"; + synchronized (sMessageDigestLock) { + if (sMessageDigest != null) { + sMessageDigest.reset(); + sMessageDigest.update(input); + byte[] result = sMessageDigest.digest(); + return encodeHex(result); + } else { + return "Uninitialized SHA1"; + } } } diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py index fb172d4fca78..c16de7b3dfc3 100755 --- a/tools/fonts/fontchain_lint.py +++ b/tools/fonts/fontchain_lint.py @@ -164,6 +164,7 @@ def check_emoji_availability(): def check_emoji_defaults(): default_emoji_chars = _emoji_properties['Emoji_Presentation'] + missing_text_chars = _emoji_properties['Emoji'] - default_emoji_chars emoji_font_seen = False for name, scripts, variant, weight, style, font in _fallback_chain: if 'Zsye' in scripts: @@ -176,32 +177,39 @@ def check_emoji_defaults(): if emoji_font_seen and not scripts: continue - if font[1] is None: - emoji_to_skip = set() - else: - # CJK font, skip checking the following characters for now. - # See b/26153752 - emoji_to_skip = ({ - 0x26BD, # SOCCER BALL - 0x26BE, # BASEBALL - 0x1F18E, # NEGATIVE SQUARED AB - 0x1F201, # SQUARED KATAKANA KOKO - 0x1F21A, # SQUARED CJK UNIFIED IDEOGRAPH-7121 - 0x1F22F, # SQUARED CJK UNIFIED IDEOGRAPH-6307 - } | set(xrange(0x1F191, 0x1F19A+1)) - | set(xrange(0x1F232, 0x1F236+1)) - | set(xrange(0x1F238, 0x1F23A+1)) - | set(xrange(0x1F250, 0x1F251+1))) - - assert_font_supports_none_of_chars(font, - sorted(default_emoji_chars - emoji_to_skip)) - - -def parse_ucd(ucd_path): - global _emoji_properties - _emoji_properties = collections.defaultdict(set) - with open(path.join(ucd_path, 'emoji-data.txt')) as emoji_data_txt: - for line in emoji_data_txt: + # Check default emoji-style characters + assert_font_supports_none_of_chars(font, sorted(default_emoji_chars)) + + # Mark default text-style characters appearing in fonts above the emoji + # font as seen + if not emoji_font_seen: + missing_text_chars -= set(get_best_cmap(font)) + + # Noto does not have monochrome symbols for Unicode 7.0 wingdings and + # webdings + missing_text_chars -= _chars_by_age['7.0'] + # TODO: Remove these after b/26113320 is fixed + missing_text_chars -= { + 0x263A, # WHITE SMILING FACE + 0x270C, # VICTORY HAND + 0x2744, # SNOWFLAKE + 0x2764, # HEAVY BLACK HEART + } + assert missing_text_chars == set(), ( + 'Text style version of some emoji characters are missing.') + + +# Setting reverse to true returns a dictionary that maps the values to sets of +# characters, useful for some binary properties. Otherwise, we get a +# dictionary that maps characters to the property values, assuming there's only +# one property in the file. +def parse_unicode_datafile(file_path, reverse=False): + if reverse: + output_dict = collections.defaultdict(set) + else: + output_dict = {} + with open(file_path) as datafile: + for line in datafile: if '#' in line: line = line[:line.index('#')] line = line.strip() @@ -216,7 +224,22 @@ def parse_ucd(ucd_path): char_start = char_end = char_range char_start = int(char_start, 16) char_end = int(char_end, 16) - _emoji_properties[prop].update(xrange(char_start, char_end+1)) + char_range = xrange(char_start, char_end+1) + if reverse: + output_dict[prop].update(char_range) + else: + for char in char_range: + assert char not in output_dict + output_dict[char] = prop + return output_dict + + +def parse_ucd(ucd_path): + global _emoji_properties, _chars_by_age + _emoji_properties = parse_unicode_datafile( + path.join(ucd_path, 'emoji-data.txt'), reverse=True) + _chars_by_age = parse_unicode_datafile( + path.join(ucd_path, 'DerivedAge.txt'), reverse=True) def main(): |