call: allow to drag video preview

Change-Id: I49413a061fcd5bef2056656d8276b48477aa522f
diff --git a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
index 97b804b..e35cf8c 100644
--- a/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/fragments/CallFragment.java
@@ -21,6 +21,7 @@
 package cx.ring.fragments;
 
 import android.Manifest;
+import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.PendingIntent;
@@ -32,6 +33,7 @@
 import android.content.pm.PackageManager;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
+import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.SurfaceTexture;
@@ -48,6 +50,7 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -55,6 +58,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.animation.DecelerateInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.RelativeLayout;
 import android.widget.Toast;
@@ -145,6 +149,10 @@
     private ConfParticipantAdapter confAdapter = null;
     private boolean mConferenceMode = false;
 
+    private PointF previewDrag = null;
+    private final ValueAnimator previewSnapAnimation = new ValueAnimator();
+    private final int[] previewMargins = new int[4];
+
     @Inject
     DeviceRuntimeService mDeviceRuntimeService;
 
@@ -322,6 +330,7 @@
         }
     };
 
+    @SuppressLint("ClickableViewAccessibility")
     @Override
     public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
         setHasOptionsMenu(true);
@@ -400,6 +409,89 @@
         binding.previewSurface.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
                 configureTransform(mPreviewSurfaceWidth, mPreviewSurfaceHeight));
 
+        float margin = getResources().getDimension(R.dimen.call_preview_margin);
+        previewSnapAnimation.setDuration(250);
+        previewSnapAnimation.setFloatValues(0.f, 1.f);
+        previewSnapAnimation.setInterpolator(new DecelerateInterpolator());
+        previewSnapAnimation.addUpdateListener(animation -> {
+            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) binding.previewContainer.getLayoutParams();
+            float f = margin * animation.getAnimatedFraction();
+            float r = 1.f - animation.getAnimatedFraction();
+            params.setMargins(
+                    (int)(previewMargins[0] * r + f),
+                    (int)(previewMargins[1] * r + f),
+                    (int)(previewMargins[2] * r + f),
+                    (int)(previewMargins[3] * r + f));
+            binding.previewContainer.setLayoutParams(params);
+        });
+
+        binding.previewContainer.setOnTouchListener((v, event) -> {
+            int action = event.getActionMasked();
+            if (action == MotionEvent.ACTION_DOWN) {
+                previewSnapAnimation.cancel();
+                previewDrag = new PointF(event.getX(), event.getY());
+                v.setElevation(v.getContext().getResources().getDimension(R.dimen.call_preview_elevation_dragged));
+                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
+                params.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+                params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+                params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+                params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+                params.setMargins((int)v.getX(), (int)v.getY(), 0, 0);
+                v.setLayoutParams(params);
+                return true;
+            } else if (action == MotionEvent.ACTION_MOVE) {
+                if (previewDrag != null) {
+                    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
+                    RelativeLayout parent = (RelativeLayout) v.getParent();
+                    params.setMargins(
+                            Math.min(params.leftMargin + (int) (event.getX() - previewDrag.x), parent.getWidth() - v.getWidth() - (int)margin),
+                            Math.min(params.topMargin + (int) (event.getY() - previewDrag.y), parent.getHeight() - v.getHeight() - (int)margin),
+                            0, 0);
+                    v.setLayoutParams(params);
+                    return true;
+                }
+                return false;
+            } else if (action == MotionEvent.ACTION_UP) {
+                if (previewDrag != null) {
+                    previewSnapAnimation.cancel();
+                    previewDrag = null;
+                    v.setElevation(v.getContext().getResources().getDimension(R.dimen.call_preview_elevation));
+                    RelativeLayout parent = (RelativeLayout) v.getParent();
+                    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) v.getLayoutParams();
+                    int ml = 0, mr = 0, mt = 0, mb = 0;
+                    if (params.leftMargin + (v.getWidth() / 2) > parent.getWidth() / 2) {
+                        params.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
+                        params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+                        mr = (int) (parent.getWidth() - v.getWidth() - v.getX());
+                    } else {
+                        params.removeRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+                        params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+                        ml = (int) v.getX();
+                    }
+                    if (params.topMargin + (v.getHeight() / 2) > parent.getHeight() / 2) {
+                        params.removeRule(RelativeLayout.ALIGN_PARENT_TOP);
+                        params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+                        mb = (int) (parent.getHeight() - v.getHeight() - v.getY());
+                    } else {
+                        params.removeRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+                        params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
+                        mt = (int) v.getY();
+                    }
+                    previewMargins[0] = ml;
+                    previewMargins[1] = mt;
+                    previewMargins[2] = mr;
+                    previewMargins[3] = mb;
+                    params.setMargins(ml, mt, mr, mb);
+                    v.setLayoutParams(params);
+                    previewSnapAnimation.start();
+                    return true;
+                }
+                return false;
+            } else {
+                return false;
+            }
+        });
+
         binding.dialpadEditText.addTextChangedListener(new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence s, int start, int count, int after) {
diff --git a/ring-android/app/src/main/res/layout/frag_call.xml b/ring-android/app/src/main/res/layout/frag_call.xml
index b8e3a95..6789060 100644
--- a/ring-android/app/src/main/res/layout/frag_call.xml
+++ b/ring-android/app/src/main/res/layout/frag_call.xml
@@ -51,12 +51,12 @@
                 android:id="@+id/preview_container"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
-                android:layout_alignParentEnd="true"
+                android:layout_alignParentRight="true"
                 android:layout_alignParentBottom="true"
-                android:layout_marginStart="12dp"
-                android:layout_marginTop="12dp"
-                android:layout_marginEnd="12dp"
-                android:layout_marginBottom="12dp"
+                android:layout_marginLeft="@dimen/call_preview_margin"
+                android:layout_marginTop="@dimen/call_preview_margin"
+                android:layout_marginBottom="@dimen/call_preview_margin"
+                android:layout_marginRight="@dimen/call_preview_margin"
                 android:visibility="gone"
                 app:cardCornerRadius="16dp"
                 app:cardPreventCornerOverlap="false"
diff --git a/ring-android/app/src/main/res/values/dimens.xml b/ring-android/app/src/main/res/values/dimens.xml
index 74d5110..b48f676 100644
--- a/ring-android/app/src/main/res/values/dimens.xml
+++ b/ring-android/app/src/main/res/values/dimens.xml
@@ -69,4 +69,8 @@
 
     <dimen name="navigation_bottom_height">60dp</dimen>
 
+    <dimen name="call_preview_elevation">8dp</dimen>
+    <dimen name="call_preview_elevation_dragged">16dp</dimen>
+    <dimen name="call_preview_margin">12dp</dimen>
+
 </resources>
\ No newline at end of file