Fixed focus transfer for GridLayoutManager and Leanback picker widgets

GridLayoutManager continued focus search even if the parent blocked the
focus delivery to its children. Also, Picker widget did not have the
right focus logic for the containing view and its child text views in
activated/deactivated modes. This CL fixed these 2 issues.

Bug: 34814581
Test: ./gradlew support-leanback-v17:connectedCheck
-Pandroid.testInstrumentationRunnerArguments.class=android.support.v17.leanback.widget.DatePickerTest
./gradlew support-leanback-v17:connectedCheck
-Pandroid.testInstrumentationRunnerArguments.class=android.support.v17.leanback.app.wizard.GuidedDatePickerTest

Change-Id: I90d4b9f870ae3f16c4591fd0b1e085a568ca4c7d
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index a2f99e2..87f8402 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -2863,6 +2863,10 @@
             return result;
         }
 
+        if (mBaseGridView.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
+            return mBaseGridView.getParent().focusSearch(focused, direction);
+        }
+
         if (DEBUG) Log.v(getTag(), "regular focusSearch failed direction " + direction);
         int movement = getMovement(direction);
         final boolean isScroll = mBaseGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index 9540810..e5ff158 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -963,9 +963,9 @@
 
     void onEditActivatorView(final ViewHolder vh, boolean editing, final boolean withTransition) {
         if (editing) {
+            startExpanded(vh, withTransition);
             vh.itemView.setFocusable(false);
             vh.mActivatorView.requestFocus();
-            startExpanded(vh, withTransition);
             vh.mActivatorView.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java b/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java
index 55ec6de..3055d8a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/picker/Picker.java
@@ -133,6 +133,7 @@
         super(context, attrs, defStyleAttr);
         // Make it enabled and clickable to receive Click event.
         setEnabled(true);
+        setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
 
         mFocusedAlpha = 1f; //getFloat(R.dimen.list_item_selected_title_text_alpha);
         mUnfocusedAlpha = 1f; //getFloat(R.dimen.list_item_unselected_text_alpha);
@@ -147,7 +148,6 @@
         LayoutInflater inflater = LayoutInflater.from(getContext());
         mRootView = (ViewGroup) inflater.inflate(R.layout.lb_picker, this, true);
         mPickerView = (ViewGroup) mRootView.findViewById(R.id.picker);
-
     }
 
     /**
@@ -196,6 +196,7 @@
             columnView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
             // Width is dynamic, so has fixed size is false.
             columnView.setHasFixedSize(false);
+            columnView.setFocusable(isActivated());
             mColumnViews.add(columnView);
 
             // add view to root
@@ -549,13 +550,40 @@
 
     @Override
     public void setActivated(boolean activated) {
-        if (activated != isActivated()) {
+        if (activated == isActivated()) {
             super.setActivated(activated);
-            updateColumnSize();
-            updateItemFocusable();
-        } else {
-            super.setActivated(activated);
+            return;
         }
+        super.setActivated(activated);
+        boolean hadFocus = hasFocus();
+        int column = getSelectedColumn();
+        // To avoid temporary focus loss in both the following cases, we set Picker's flag to
+        // FOCUS_BEFORE_DESCENDANTS first, and then back to FOCUS_AFTER_DESCENDANTS once done with
+        // the focus logic.
+        // 1. When changing from activated to deactivated, the Picker should grab the focus
+        // back if it's focusable. However, calling requestFocus on it will transfer the focus down
+        // to its children if it's flag is FOCUS_AFTER_DESCENDANTS.
+        // 2. When changing from deactivated to activated, while setting focusable flags on each
+        // column VerticalGridView, that column will call requestFocus (regardless of which column
+        // is the selected column) since the currently focused view (Picker) has a flag of
+        // FOCUS_AFTER_DESCENDANTS.
+        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
+        if (!activated && hadFocus && isFocusable()) {
+            // When picker widget that originally had focus is deactivated and it is focusable, we
+            // should not pass the focus down to the children. The Picker itself will capture focus.
+            requestFocus();
+        }
+
+        for (int i = 0; i < getColumnsCount(); i++) {
+            mColumnViews.get(i).setFocusable(activated);
+        }
+
+        updateColumnSize();
+        updateItemFocusable();
+        if (activated && hadFocus && (column >= 0)) {
+            mColumnViews.get(column).requestFocus();
+        }
+        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
     }
 
     @Override
diff --git a/v17/leanback/tests/AndroidManifest.xml b/v17/leanback/tests/AndroidManifest.xml
index 82af3b6..f566245 100644
--- a/v17/leanback/tests/AndroidManifest.xml
+++ b/v17/leanback/tests/AndroidManifest.xml
@@ -33,6 +33,10 @@
         <activity android:name="android.support.v17.leanback.widget.GridActivity"
                   android:exported="true" />
 
+        <activity android:name="android.support.v17.leanback.widget.DatePickerActivity"
+                  android:theme="@style/Theme.Leanback"
+                  android:exported="true" />
+
         <activity
                 android:name="android.support.v17.leanback.app.wizard.GuidedStepAttributesTestActivity"
                 android:theme="@style/Theme.Leanback.GuidedStep"
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/DatePickerActivity.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/DatePickerActivity.java
new file mode 100644
index 0000000..eda702f
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/DatePickerActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.support.v17.leanback.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v17.leanback.test.R;
+
+
+public class DatePickerActivity extends Activity {
+
+    public static final String EXTRA_LAYOUT_RESOURCE_ID = "layoutResourceId";
+
+    int mLayoutId;
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mLayoutId = getIntent().getIntExtra(EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets);
+        setContentView(mLayoutId);
+    }
+}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/DatePickerTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/DatePickerTest.java
new file mode 100644
index 0000000..fc0df10
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/DatePickerTest.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2017 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.support.v17.leanback.widget;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.test.R;
+import android.support.v17.leanback.widget.picker.DatePicker;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class DatePickerTest {
+
+    private static final String TAG = "DatePickerTest";
+    private static final long TRANSITION_LENGTH = 1000;
+
+    Context mContext;
+    View mViewAbove;
+    DatePicker mDatePickerView;
+    ViewGroup mDatePickerInnerView;
+    View mViewBelow;
+
+    @Rule
+    public ActivityTestRule<DatePickerActivity> mActivityTestRule =
+            new ActivityTestRule<>(DatePickerActivity.class, false, false);
+    private DatePickerActivity mActivity;
+
+    public void initActivity(Intent intent) throws Throwable {
+        mActivity = mActivityTestRule.launchActivity(intent);
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mDatePickerView = (DatePicker) mActivity.findViewById(R.id.date_picker);
+        mDatePickerInnerView = (ViewGroup) mDatePickerView.findViewById(R.id.picker);
+        mDatePickerView.setActivatedVisibleItemCount(3);
+        mDatePickerView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mDatePickerView.setActivated(!mDatePickerView.isActivated());
+            }
+        });
+        if (intent.getIntExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets) == R.layout.datepicker_with_other_widgets) {
+            mViewAbove = mActivity.findViewById(R.id.above_picker);
+            mViewBelow = mActivity.findViewById(R.id.below_picker);
+        } else if (intent.getIntExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets) == R.layout.datepicker_alone) {
+            // A layout with only a DatePicker widget that is initially activated.
+            mActivityTestRule.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    mDatePickerView.setActivated(true);
+                }
+            });
+            Thread.sleep(500);
+        }
+    }
+
+    @Test
+    public void testFocusTravel() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets);
+        initActivity(intent);
+
+        assertThat("TextView above should have focus initially", mViewAbove.hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("DatePicker should have focus now", mDatePickerView.isFocused(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The first column of DatePicker should hold focus",
+                mDatePickerInnerView.getChildAt(0).hasFocus(), is(true));
+
+        // skipping the separator in the child indices
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The second column of DatePicker should hold focus",
+                mDatePickerInnerView.getChildAt(2).hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The third column of DatePicker should hold focus",
+                mDatePickerInnerView.getChildAt(4).hasFocus(), is(true));
+    }
+
+    @Test
+    public void testFocusRetainedForASelectedColumn()
+            throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets);
+        initActivity(intent);
+        mDatePickerView.setFocusable(true);
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+
+        assertThat("DatePicker should have focus when it's focusable",
+                mDatePickerView.isFocused(), is(true));
+
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("After the first activation, the first column of DatePicker should hold focus",
+                mDatePickerInnerView.getChildAt(0).hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The third column of DatePicker should hold focus",
+                mDatePickerInnerView.getChildAt(4).hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("After the first deactivation, the DatePicker itself should hold focus",
+                mDatePickerView.isFocused(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("After the second activation, the last selected column (3rd) should hold focus",
+                mDatePickerInnerView.getChildAt(4).hasFocus(), is(true));
+    }
+
+    @Test
+    public void testFocusSkippedWhenDatePickerUnFocusable()
+            throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets);
+        initActivity(intent);
+
+        mDatePickerView.setFocusable(false);
+        assertThat("TextView above should have focus initially.", mViewAbove.hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+
+        assertThat("DatePicker should be skipped and TextView below should have focus.",
+                mViewBelow.hasFocus(), is(true));
+    }
+
+    @Test
+    public void testTemporaryFocusLossWhenDeactivated()
+            throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets);
+        initActivity(intent);
+
+        final int[] currentFocusChangeCountForViewAbove = {0};
+        mDatePickerView.setFocusable(true);
+        Log.d(TAG, "view above: " + mViewAbove);
+        mViewAbove.setOnFocusChangeListener(new View.OnFocusChangeListener(){
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                currentFocusChangeCountForViewAbove[0]++;
+            }
+        });
+        assertThat("TextView above should have focus initially.", mViewAbove.hasFocus(), is(true));
+
+        // Traverse to the third column of date picker
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Click once to activate
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Traverse to the third column
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Click to deactivate. Before that we remember the focus change count for the view above.
+        // This view should NOT receive temporary focus when DatePicker is deactivated, and
+        // DatePicker itself should capture the focus.
+        int[] lastFocusChangeCountForViewAbove = {currentFocusChangeCountForViewAbove[0]};
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("DatePicker should have focus now since it's focusable",
+                mDatePickerView.isFocused(), is(true));
+        assertThat("Focus change count of view above should not be changed after last click.",
+                currentFocusChangeCountForViewAbove[0], is(lastFocusChangeCountForViewAbove[0]));
+    }
+
+    @Test
+    public void testTemporaryFocusLossWhenActivated() throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_with_other_widgets);
+        initActivity(intent);
+        final int[] currentFocusChangeCountForColumns = {0, 0, 0};
+        mDatePickerView.setFocusable(true);
+        mDatePickerInnerView.getChildAt(0).setOnFocusChangeListener(
+                new View.OnFocusChangeListener() {
+                    @Override
+                    public void onFocusChange(View v, boolean hasFocus) {
+                        currentFocusChangeCountForColumns[0]++;
+                    }
+                });
+
+        mDatePickerInnerView.getChildAt(2).setOnFocusChangeListener(
+                new View.OnFocusChangeListener() {
+                    @Override
+                    public void onFocusChange(View v, boolean hasFocus) {
+                        currentFocusChangeCountForColumns[1]++;
+                    }
+                });
+
+        mDatePickerInnerView.getChildAt(4).setOnFocusChangeListener(
+                new View.OnFocusChangeListener() {
+                    @Override
+                    public void onFocusChange(View v, boolean hasFocus) {
+                        currentFocusChangeCountForColumns[2]++;
+                    }
+                });
+
+        // Traverse to the third column of date picker
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Click once to activate
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Traverse to the third column
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Click to deactivate
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        // Click again. The focus should NOT be temporarily moved to the other columns and the third
+        // column should receive focus.
+        // Before that we will remember the last focus change count to compare it against after the
+        // click.
+        int[] lastFocusChangeCountForColumns = {currentFocusChangeCountForColumns[0],
+                currentFocusChangeCountForColumns[1], currentFocusChangeCountForColumns[2]};
+        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("Focus change count of column 0 should not be changed after last click.",
+                currentFocusChangeCountForColumns[0], is(lastFocusChangeCountForColumns[0]));
+        assertThat("Focus change count of column 1 should not be changed after last click.",
+                currentFocusChangeCountForColumns[1], is(lastFocusChangeCountForColumns[1]));
+        assertThat("Focus change count of column 2 should not be changed after last click.",
+                currentFocusChangeCountForColumns[2], is(lastFocusChangeCountForColumns[2]));
+    }
+
+    @Test
+    public void testInitiallyActiveDatePicker()
+            throws Throwable {
+        Intent intent = new Intent();
+        intent.putExtra(DatePickerActivity.EXTRA_LAYOUT_RESOURCE_ID,
+                R.layout.datepicker_alone);
+        initActivity(intent);
+
+        assertThat("The first column of DatePicker should initially hold focus",
+                mDatePickerInnerView.getChildAt(0).hasFocus(), is(true));
+
+        // focus on first column
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The first column of DatePicker should still hold focus after scrolling down",
+                mDatePickerInnerView.getChildAt(0).hasFocus(), is(true));
+
+        // focus on second column
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The second column of DatePicker should hold focus after scrolling right",
+                mDatePickerInnerView.getChildAt(2).hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The second column of DatePicker should still hold focus after scrolling down",
+                mDatePickerInnerView.getChildAt(2).hasFocus(), is(true));
+
+        // focus on third column
+        sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The third column of DatePicker should hold focus after scrolling right",
+                mDatePickerInnerView.getChildAt(4).hasFocus(), is(true));
+
+        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
+        Thread.sleep(TRANSITION_LENGTH);
+        assertThat("The third column of DatePicker should still hold focus after scrolling down",
+                mDatePickerInnerView.getChildAt(4).hasFocus(), is(true));
+    }
+
+    private void sendKeys(int ...keys) {
+        for (int i = 0; i < keys.length; i++) {
+            InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]);
+        }
+    }
+}
diff --git a/v17/leanback/tests/res/layout/datepicker_alone.xml b/v17/leanback/tests/res/layout/datepicker_alone.xml
new file mode 100644
index 0000000..79a20b1
--- /dev/null
+++ b/v17/leanback/tests/res/layout/datepicker_alone.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <android.support.v17.leanback.widget.picker.DatePicker
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/date_picker"
+        android:importantForAccessibility="yes"
+        android:focusable="true"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/v17/leanback/tests/res/layout/datepicker_with_other_widgets.xml b/v17/leanback/tests/res/layout/datepicker_with_other_widgets.xml
new file mode 100644
index 0000000..b5dc630
--- /dev/null
+++ b/v17/leanback/tests/res/layout/datepicker_with_other_widgets.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <RelativeLayout android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:gravity="center">
+        <TextView
+            android:id="@+id/above_picker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Above Picker Some Text"
+            android:textAlignment="center"
+            android:focusable="true"
+            android:background="?android:attr/selectableItemBackground"
+        />
+        <android.support.v17.leanback.widget.picker.DatePicker
+            android:id="@+id/date_picker"
+            android:importantForAccessibility="yes"
+            android:focusable="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/above_picker"
+            android:gravity="center" />
+        <TextView
+            android:id="@+id/below_picker"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Below Picker Some Text"
+            android:textAlignment="center"
+            android:layout_below="@id/date_picker"
+            android:focusable="true"
+            android:background="?android:attr/selectableItemBackground"/>
+    </RelativeLayout>
+</RelativeLayout>