API Review: split out BidirectionalTypeConverter.

Bug 14997858

Change-Id: I1f2ccf7c4e60320c8df759702a8f2fa24fd3acd2
diff --git a/api/current.txt b/api/current.txt
index 6386ff2..0ce7207 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2831,6 +2831,12 @@
     method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
   }
 
+  public abstract class BidirectionalTypeConverter extends android.animation.TypeConverter {
+    ctor public BidirectionalTypeConverter(java.lang.Class<T>, java.lang.Class<V>);
+    method public abstract T convertBack(V);
+    method public android.animation.BidirectionalTypeConverter<V, T> invert();
+  }
+
   public class FloatArrayEvaluator implements android.animation.TypeEvaluator {
     ctor public FloatArrayEvaluator();
     ctor public FloatArrayEvaluator(float[]);
@@ -3009,7 +3015,6 @@
   public abstract class TypeConverter {
     ctor public TypeConverter(java.lang.Class<T>, java.lang.Class<V>);
     method public abstract V convert(T);
-    method public T convertBack(V);
   }
 
   public abstract interface TypeEvaluator {
diff --git a/core/java/android/animation/BidirectionalTypeConverter.java b/core/java/android/animation/BidirectionalTypeConverter.java
new file mode 100644
index 0000000..960650e
--- /dev/null
+++ b/core/java/android/animation/BidirectionalTypeConverter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.animation;
+
+/**
+ * Abstract base class used convert type T to another type V and back again. This
+ * is necessary when the value types of in animation are different from the property
+ * type. BidirectionalTypeConverter is needed when only the final value for the
+ * animation is supplied to animators.
+ * @see PropertyValuesHolder#setConverter(TypeConverter)
+ */
+public abstract class BidirectionalTypeConverter<T, V> extends TypeConverter<T, V> {
+    private BidirectionalTypeConverter mInvertedConverter;
+
+    public BidirectionalTypeConverter(Class<T> fromClass, Class<V> toClass) {
+        super(fromClass, toClass);
+    }
+
+    /**
+     * Does a conversion from the target type back to the source type. The subclass
+     * must implement this when a TypeConverter is used in animations and current
+     * values will need to be read for an animation.
+     * @param value The Object to convert.
+     * @return A value of type T, converted from <code>value</code>.
+     */
+    public abstract T convertBack(V value);
+
+    /**
+     * Returns the inverse of this converter, where the from and to classes are reversed.
+     * The inverted converter uses this convert to call {@link #convertBack(Object)} for
+     * {@link #convert(Object)} calls and {@link #convert(Object)} for
+     * {@link #convertBack(Object)} calls.
+     * @return The inverse of this converter, where the from and to classes are reversed.
+     */
+    public BidirectionalTypeConverter<V, T> invert() {
+        if (mInvertedConverter == null) {
+            mInvertedConverter = new InvertedConverter(this);
+        }
+        return mInvertedConverter;
+    }
+
+    private static class InvertedConverter<From, To> extends BidirectionalTypeConverter<From, To> {
+        private BidirectionalTypeConverter<To, From> mConverter;
+
+        public InvertedConverter(BidirectionalTypeConverter<To, From> converter) {
+            super(converter.getTargetType(), converter.getSourceType());
+            mConverter = converter;
+        }
+
+        @Override
+        public From convertBack(To value) {
+            return mConverter.convert(value);
+        }
+
+        @Override
+        public To convert(From value) {
+            return mConverter.convertBack(value);
+        }
+    }
+}
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index c0ce795..130754e 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -610,8 +610,8 @@
      * along the way, and an ending value (these values will be distributed evenly across
      * the duration of the animation). This variant supplies a <code>TypeConverter</code> to
      * convert from the animated values to the type of the property. If only one value is
-     * supplied, the <code>TypeConverter</code> must implement
-     * {@link TypeConverter#convertBack(Object)} to retrieve the current value.
+     * supplied, the <code>TypeConverter</code> must be a
+     * {@link android.animation.BidirectionalTypeConverter} to retrieve the current value.
      *
      * @param target The object whose property is to be animated.
      * @param property The property being animated.
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 8fce80a..bf2924c 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -456,7 +456,7 @@
      * cannot automatically interpolate between objects of unknown type. This variant also
      * takes a <code>TypeConverter</code> to convert from animated values to the type
      * of the property. If only one value is supplied, the <code>TypeConverter</code>
-     * must implement {@link TypeConverter#convertBack(Object)} to retrieve the current
+     * must be a {@link android.animation.BidirectionalTypeConverter} to retrieve the current
      * value.
      *
      * @param property The property being animated. Should not be null.
@@ -635,6 +635,8 @@
 
     /**
      * Sets the converter to convert from the values type to the setter's parameter type.
+     * If only one value is supplied, <var>converter</var> must be a
+     * {@link android.animation.BidirectionalTypeConverter}.
      * @param converter The converter to use to convert values.
      */
     public void setConverter(TypeConverter converter) {
@@ -816,12 +818,12 @@
 
     private Object convertBack(Object value) {
         if (mConverter != null) {
-            value = mConverter.convertBack(value);
-            if (value == null) {
+            if (!(mConverter instanceof BidirectionalTypeConverter)) {
                 throw new IllegalArgumentException("Converter "
                         + mConverter.getClass().getName()
-                        + " must implement convertBack and not return null.");
+                        + " must be a BidirectionalTypeConverter");
             }
+            value = ((BidirectionalTypeConverter) mConverter).convertBack(value);
         }
         return value;
     }
diff --git a/core/java/android/animation/TypeConverter.java b/core/java/android/animation/TypeConverter.java
index 03b3eb5..9ead2ad 100644
--- a/core/java/android/animation/TypeConverter.java
+++ b/core/java/android/animation/TypeConverter.java
@@ -53,16 +53,4 @@
      * @return A value of type V, converted from <code>value</code>.
      */
     public abstract V convert(T value);
-
-    /**
-     * Does a conversion from the target type back to the source type. The subclass
-     * must implement this when a TypeConverter is used in animations and current
-     * values will need to be read for an animation. By default, this will return null,
-     * indicating that back-conversion is not supported.
-     * @param value The Object to convert.
-     * @return A value of type T, converted from <code>value</code>.
-     */
-    public T convertBack(V value) {
-        return null;
-    }
 }