Accessibility fix for Dialer search results

In portrait mode, resize the listview to fit above the dialpad,
so that accessibility services can correctly traverse the
view hierarchy.

Bug: 20172384

Change-Id: I35fb49be7bea5d13d951c4d954ddaf9d8d12780e
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 653ed8b..b8a1d4d 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -1281,11 +1281,20 @@
         return mActionBarController;
     }
 
+    @Override
     public boolean isDialpadShown() {
         return mIsDialpadShown;
     }
 
     @Override
+    public int getDialpadHeight() {
+        if (mDialpadFragment != null) {
+            return mDialpadFragment.getDialpadHeight();
+        }
+        return 0;
+    }
+
+    @Override
     public int getActionBarHideOffset() {
         return getActionBar().getHideOffset();
     }
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 5b1e211..b18069f 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -1669,6 +1669,13 @@
         ((DialpadSlidingRelativeLayout) getView()).setYFraction(yFraction);
     }
 
+    public int getDialpadHeight() {
+        if (mDialpadView == null) {
+            return 0;
+        }
+        return mDialpadView.getHeight();
+    }
+
     public void process_quote_emergency_unquote(String query) {
         if (PseudoEmergencyAnimator.PSEUDO_EMERGENCY_NUMBER.equals(query)) {
             if (mPseudoEmergencyAnimator == null) {
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
index 78c3ad3..1f33d5b 100644
--- a/src/com/android/dialer/list/SearchFragment.java
+++ b/src/com/android/dialer/list/SearchFragment.java
@@ -21,17 +21,23 @@
 import android.app.Activity;
 import android.app.DialogFragment;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.ContactsContract;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.Interpolator;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
+import android.widget.LinearLayout;
 import android.widget.ListView;
+import android.widget.Space;
 
 import com.android.contacts.common.list.ContactEntryListAdapter;
 import com.android.contacts.common.list.ContactListItemView;
@@ -63,11 +69,20 @@
     private int mShowDialpadDuration;
     private int mHideDialpadDuration;
 
+    /**
+     * Used to resize the list view containing search results so that it fits the available space
+     * above the dialpad. Does not have a user-visible effect in regular touch usage (since the
+     * dialpad hides that portion of the ListView anyway), but improves usability in accessibility
+     * mode.
+     */
+    private Space mSpacer;
+
     private HostInterface mActivity;
 
     public interface HostInterface {
         public boolean isActionBarShowing();
         public boolean isDialpadShown();
+        public int getDialpadHeight();
         public int getActionBarHideOffset();
         public int getActionBarHeight();
     }
@@ -270,17 +285,35 @@
                     mActivity.isDialpadShown() ? 0 : mActionBarHeight -mShadowHeight;
         }
         if (animate) {
-            Interpolator interpolator =
-                    mActivity.isDialpadShown() ? AnimUtils.EASE_IN : AnimUtils.EASE_OUT ;
-            int duration =
-                    mActivity.isDialpadShown() ? mShowDialpadDuration : mHideDialpadDuration;
+            // If the dialpad will be shown, then this animation involves sliding the list up.
+            final boolean slideUp = mActivity.isDialpadShown();
+
+            Interpolator interpolator = slideUp ? AnimUtils.EASE_IN : AnimUtils.EASE_OUT ;
+            int duration = slideUp ? mShowDialpadDuration : mHideDialpadDuration;
             getView().setTranslationY(startTranslationValue);
             getView().animate()
                     .translationY(endTranslationValue)
                     .setInterpolator(interpolator)
-                    .setDuration(duration);
+                    .setDuration(duration)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationStart(Animator animation) {
+                            if (!slideUp) {
+                                resizeListView();
+                            }
+                        }
+
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            if (slideUp) {
+                                resizeListView();
+                            }
+                        }
+                    });
+
         } else {
             getView().setTranslationY(endTranslationValue);
+            resizeListView();
         }
 
         // There is padding which should only be applied when the dialpad is not shown.
@@ -293,6 +326,19 @@
                 listView.getPaddingBottom());
     }
 
+    public void resizeListView() {
+        if (mSpacer == null) {
+            return;
+        }
+        int spacerHeight = mActivity.isDialpadShown() ? mActivity.getDialpadHeight() : 0;
+        if (spacerHeight != mSpacer.getHeight()) {
+            final LinearLayout.LayoutParams lp =
+                    (LinearLayout.LayoutParams) mSpacer.getLayoutParams();
+            lp.height = spacerHeight;
+            mSpacer.setLayoutParams(lp);
+        }
+    }
+
     @Override
     protected void startLoading() {
         if (PermissionsUtil.hasContactsPermissions(getActivity())) {
@@ -306,4 +352,16 @@
     public void setOnTouchListener(View.OnTouchListener onTouchListener) {
         mActivityOnTouchListener = onTouchListener;
     }
+
+    @Override
+    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+        final LinearLayout parent = (LinearLayout) super.inflateView(inflater, container);
+        final int orientation = getResources().getConfiguration().orientation;
+        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+            mSpacer = new Space(getActivity());
+            parent.addView(mSpacer,
+                    new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0));
+        }
+        return parent;
+    }
 }