Reverse horizontal translation set on preview in RTL layouts

When correcting or applying a scale type to the preview in PreviewView, a horizontal translation might be applied to it, which works fine in left-to-right layouts. In the context of a right-to-left layout, the horizontal tarnslation should be reversed, otherwise it may cause the preview is be offset.

Bug: 154944939
Test: ./gradlew camera:camera-view:test
Change-Id: I7d59750304bb24c58b5c7e8a4a086aaf6248830e
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
index 01a939f..3b9890a 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/PreviewView.java
@@ -261,28 +261,38 @@
     public enum ScaleType {
         /**
          * Scale the preview, maintaining the source aspect ratio, so it fills the entire
-         * {@link PreviewView}, and align it to the top left corner of the view.
+         * {@link PreviewView}, and align it to the start of the view, which is the top left
+         * corner in a left-to-right (LTR) layout, or the top right corner in a right-to-left
+         * (RTL) layout.
+         * <p>
          * This may cause the preview to be cropped if the camera preview aspect ratio does not
          * match that of its container {@link PreviewView}.
          */
         FILL_START(0),
         /**
          * Scale the preview, maintaining the source aspect ratio, so it fills the entire
-         * {@link PreviewView}, and center it inside the view.
+         * {@link PreviewView}, and center it in the view.
+         * <p>
          * This may cause the preview to be cropped if the camera preview aspect ratio does not
          * match that of its container {@link PreviewView}.
          */
         FILL_CENTER(1),
         /**
          * Scale the preview, maintaining the source aspect ratio, so it fills the entire
-         * {@link PreviewView}, and align it to the bottom right corner of the view.
+         * {@link PreviewView}, and align it to the end of the view, which is the bottom right
+         * corner in a left-to-right (LTR) layout, or the bottom left corner in a right-to-left
+         * (RTL) layout.
+         * <p>
          * This may cause the preview to be cropped if the camera preview aspect ratio does not
          * match that of its container {@link PreviewView}.
          */
         FILL_END(2),
         /**
          * Scale the preview, maintaining the source aspect ratio, so it is entirely contained
-         * within the {@link PreviewView}, and align it to the top left corner of the view.
+         * within the {@link PreviewView}, and align it to the start of the view, which is the
+         * top left corner in a left-to-right (LTR) layout, or the top right corner in a
+         * right-to-left (RTL) layout.
+         * <p>
          * Both dimensions of the preview will be equal or less than the corresponding dimensions
          * of its container {@link PreviewView}.
          */
@@ -290,13 +300,17 @@
         /**
          * Scale the preview, maintaining the source aspect ratio, so it is entirely contained
          * within the {@link PreviewView}, and center it inside the view.
+         * <p>
          * Both dimensions of the preview will be equal or less than the corresponding dimensions
          * of its container {@link PreviewView}.
          */
         FIT_CENTER(4),
         /**
          * Scale the preview, maintaining the source aspect ratio, so it is entirely contained
-         * within the {@link PreviewView}, and align it to the bottom right corner of the view.
+         * within the {@link PreviewView}, and align it to the end of the view, which is the
+         * bottom right corner in a left-to-right (LTR) layout, or the bottom left corner in a
+         * right-to-left (RTL) layout.
+         * <p>
          * Both dimensions of the preview will be equal or less than the corresponding dimensions
          * of its container {@link PreviewView}.
          */
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java
index b7a3788..2809dc2 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/preview/transform/TranslationTransform.java
@@ -23,8 +23,14 @@
 import androidx.camera.view.preview.transform.transformation.TranslationTransformation;
 
 /**
- * Computes the x and y coordinates of the top left corner of the preview in order to position it
- * at the start (top left), center or end (bottom right) of its parent.
+ * Computes the horizontal and vertical translations by which the preview needs to be translated
+ * to position it at the start, center or end of its parent.
+ * <p>
+ * The start represents the top left corner in a left-to-right (LTR) layout, or the top right
+ * corner in a right-to-left (RTL) layout.
+ * <p>
+ * The end represents the bottom right corner in a left-to-right (LTR) layout, or the bottom left
+ * corner in a right-to-left (RTL) layout.
  */
 final class TranslationTransform {
 
@@ -32,8 +38,11 @@
     }
 
     /**
-     * Computes the x and y coordinates of the top left corner of {@code view} so that it's
-     * aligned to the top left corner of its parent {@code container}.
+     * Computes the horizontal and vertical translations to set on {@code view} to align it to the
+     * start of its parent {@code container}.
+     * <p>
+     * The start represents the top left corner in a left-to-right (LTR) layout, or the top right
+     * corner in a right-to-left (RTL) layout.
      */
     static TranslationTransformation start(@NonNull final View view,
             @NonNull final Pair<Float, Float> scaleXY) {
@@ -63,14 +72,14 @@
         final int currentCenterX = view.getWidth() / 2;
         final int currentCenterY = view.getHeight() / 2;
 
-        final int transX = targetCenterX - currentCenterX;
+        final int transX = reverseIfRTLLayout(view, targetCenterX - currentCenterX);
         final int transY = targetCenterY - currentCenterY;
         return new TranslationTransformation(transX, transY);
     }
 
     /**
-     * Computes the x and y coordinates of the top left corner of {@code view} so that it's
-     * centered in its parent {@code container}.
+     * Computes the horizontal and vertical translations to set on {@code view} to center it in its
+     * parent {@code container}.
      */
     static TranslationTransformation center(@NonNull final View container,
             @NonNull final View view) {
@@ -86,14 +95,17 @@
         final int currentCenterX = view.getWidth() / 2;
         final int currentCenterY = view.getHeight() / 2;
 
-        final int transX = targetCenterX - currentCenterX;
+        final int transX = reverseIfRTLLayout(view, targetCenterX - currentCenterX);
         final int transY = targetCenterY - currentCenterY;
         return new TranslationTransformation(transX, transY);
     }
 
     /**
-     * Computes the x and y coordinates of the top left corner of {@code view} so that it's
-     * aligned to the bottom right corner of its parent {@code container}.
+     * Computes the horizontal and vertical translations to set on {@code view} to align it to the
+     * end of its parent {@code container}.
+     * <p>
+     * The end represents the bottom right corner in a left-to-right (LTR) layout, or the bottom
+     * left corner in a right-to-left (RTL) layout.
      */
     static TranslationTransformation end(@NonNull final View container, @NonNull final View view,
             @NonNull final Pair<Float, Float> scaleXY) {
@@ -127,8 +139,19 @@
         final int currentCenterX = view.getWidth() / 2;
         final int currentCenterY = view.getHeight() / 2;
 
-        final int transX = targetCenterX - currentCenterX;
+        final int transX = reverseIfRTLLayout(view, targetCenterX - currentCenterX);
         final int transY = targetCenterY - currentCenterY;
         return new TranslationTransformation(transX, transY);
     }
+
+    /**
+     * Reverses a horizontal translation if the {@code view} is in a right-to-left (RTL) layout.
+     *
+     * @return The passed in horizontal translation if the layout is left-to-right (LTR), or its
+     * reverse if the layout is right-to-left (RTL).
+     */
+    private static int reverseIfRTLLayout(@NonNull final View view, int transX) {
+        final boolean isRTLDirection = view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        return isRTLDirection ? -transX : transX;
+    }
 }
diff --git a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java b/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java
index 699f6db..05ba62f 100644
--- a/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java
+++ b/camera/camera-view/src/test/java/androidx/camera/view/preview/transform/TranslationTransformTest.java
@@ -16,6 +16,9 @@
 
 package androidx.camera.view.preview.transform;
 
+import static android.view.View.LAYOUT_DIRECTION_LTR;
+import static android.view.View.LAYOUT_DIRECTION_RTL;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.mock;
@@ -89,6 +92,16 @@
     }
 
     @Test
+    public void start_rtlLayout() {
+        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
+        final Pair<Float, Float> scaleXY = new Pair<>(2F, 0.5F);
+        final TranslationTransformation transformation = TranslationTransform.start(view, scaleXY);
+
+        assertThat(transformation.getTransX()).isEqualTo(-100);
+        assertThat(transformation.getTransY()).isEqualTo(-75);
+    }
+
+    @Test
     public void center_viewNotScaled_rotation0() {
         final View container = setUpContainer();
         final View view = setUpView(Surface.ROTATION_0);
@@ -137,6 +150,18 @@
     }
 
     @Test
+    public void center_rtlLayout() {
+        final View container = setUpContainer();
+        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
+        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
+        final TranslationTransformation transformation = TranslationTransform.center(container,
+                view);
+
+        assertThat(transformation.getTransX()).isEqualTo(-150);
+        assertThat(transformation.getTransY()).isEqualTo(250);
+    }
+
+    @Test
     public void end_viewNotScaled_rotation0() {
         final View container = setUpContainer();
         final View view = setUpView(Surface.ROTATION_0);
@@ -184,11 +209,29 @@
         assertThat(transformation.getTransY()).isEqualTo(450);
     }
 
+    @Test
+    public void end_rtlLayout() {
+        final View container = setUpContainer();
+        final View view = setUpView(Surface.ROTATION_0, LAYOUT_DIRECTION_RTL);
+        final Pair<Float, Float> scaleXY = new Pair<>(1F, 1F);
+        final TranslationTransformation transformation = TranslationTransform.end(container, view,
+                scaleXY);
+
+        assertThat(transformation.getTransX()).isEqualTo(-300);
+        assertThat(transformation.getTransY()).isEqualTo(500);
+    }
+
     @NonNull
     private View setUpView(final int rotation) {
+        return setUpView(rotation, LAYOUT_DIRECTION_LTR);
+    }
+
+    @NonNull
+    private View setUpView(final int rotation, final int layoutDirection) {
         final View view = mock(View.class);
         when(view.getWidth()).thenReturn(VIEW_WIDTH);
         when(view.getHeight()).thenReturn(VIEW_HEIGHT);
+        when(view.getLayoutDirection()).thenReturn(layoutDirection);
 
         final Display display = mock(Display.class);
         when(view.getDisplay()).thenReturn(display);
diff --git a/camera/integration-tests/viewtestapp/src/main/AndroidManifest.xml b/camera/integration-tests/viewtestapp/src/main/AndroidManifest.xml
index aed04bf..62ab73b 100644
--- a/camera/integration-tests/viewtestapp/src/main/AndroidManifest.xml
+++ b/camera/integration-tests/viewtestapp/src/main/AndroidManifest.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2019 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.
@@ -24,6 +23,7 @@
     <application
         android:label="@string/app_name"
         android:largeHeap="true"
+        android:supportsRtl="true"
         android:theme="@style/AppTheme">
 
         <activity