Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.car.widget;
     18 
     19 import static android.support.test.espresso.Espresso.onView;
     20 import static android.support.test.espresso.action.ViewActions.click;
     21 import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription;
     22 import static android.support.test.espresso.matcher.ViewMatchers.withId;
     23 
     24 import static org.junit.Assert.assertEquals;
     25 import static org.junit.Assert.assertNotNull;
     26 import static org.junit.Assert.assertNull;
     27 
     28 import android.content.Context;
     29 import android.support.test.filters.MediumTest;
     30 import android.support.test.rule.ActivityTestRule;
     31 import android.support.test.runner.AndroidJUnit4;
     32 import androidx.core.util.Preconditions;
     33 import android.view.View;
     34 import android.widget.ImageButton;
     35 import android.widget.LinearLayout;
     36 import android.widget.Space;
     37 import android.widget.ToggleButton;
     38 
     39 import org.junit.Before;
     40 import org.junit.Rule;
     41 import org.junit.Test;
     42 import org.junit.runner.RunWith;
     43 
     44 import androidx.car.R;
     45 
     46 /** Unit tests for {@link ActionBar}. */
     47 @RunWith(AndroidJUnit4.class)
     48 @MediumTest
     49 public class ActionBarTest {
     50     @Rule
     51     public ActivityTestRule<ActionBarTestActivity> mActivityRule =
     52             new ActivityTestRule<>(ActionBarTestActivity.class);
     53     private ActionBarTestActivity mActivity;
     54     private ActionBar mActionBar;
     55     private LinearLayout mRowsContainer;
     56     private View[] mItems;
     57 
     58     private static final int TOP_ROW_IDX = 0;
     59     private static final int BOTTOM_ROW_IDX = 1;
     60     private static final int NUM_COLS = 5;
     61 
     62     @Before
     63     public void setUp() {
     64         mActivity = mActivityRule.getActivity();
     65         mActionBar = mActivity.findViewById(androidx.car.test.R.id.action_bar);
     66         mRowsContainer = mActionBar.findViewById(R.id.rows_container);
     67     }
     68 
     69     private void setUpActionBarItems(int numItems) {
     70         mItems = new View[numItems];
     71         for (int i = 0; i < numItems; i++) {
     72             mItems[i] = createButton(mActivity);
     73         }
     74         mActivity.runOnUiThread(() ->  mActionBar.setViews(mItems));
     75     }
     76 
     77     private ImageButton createButton(Context context) {
     78         ImageButton button = new ImageButton(context, null, R.style.Widget_Car_Button_ActionBar);
     79         button.setImageDrawable(context.getDrawable(androidx.car.test.R.drawable.ic_overflow));
     80         return button;
     81     }
     82 
     83     /**
     84      * Asserts that only the first 'numItems' slots are used.
     85      */
     86     private void assertLeftItemsNotEmpty(int rowIdx, int numItems) {
     87         for (int colIdx = 0; colIdx < NUM_COLS - 1; colIdx++) {
     88             if (colIdx < numItems) {
     89                 assertNotNull(String.format("Slot (%d, %d) should be taken", rowIdx, colIdx),
     90                         mActionBar.getViewAt(rowIdx, colIdx));
     91             } else {
     92                 assertNull(String.format("Slot (%d, %d) should be empty", rowIdx, colIdx),
     93                         mActionBar.getViewAt(rowIdx, colIdx));
     94             }
     95         }
     96     }
     97 
     98     /**
     99      * Tests that the bar with no children views is displayed correctly
    100      */
    101     @Test
    102     public void testEmptyState() {
    103         setUpActionBarItems(0);
    104         onView(withId(androidx.car.test.R.id.action_bar)).check((view, noViewFoundException) -> {
    105             Preconditions.checkNotNull(view);
    106             // All slots should be empty.
    107             assertLeftItemsNotEmpty(TOP_ROW_IDX, 0);
    108             assertLeftItemsNotEmpty(BOTTOM_ROW_IDX, 0);
    109         });
    110     }
    111 
    112     /**
    113      * Tests that slots are used from left to right and from bottom to top
    114      */
    115     @Test
    116     public void testNormalSlotUsage() {
    117         for (int items = 1; items < NUM_COLS - 1; items++) {
    118             setUpActionBarItems(items);
    119             final int numItems = items;
    120             onView(withId(androidx.car.test.R.id.action_bar))
    121                     .check((view, noViewFoundException) -> {
    122                         Preconditions.checkNotNull(view);
    123                         // Top row should be empty
    124                         assertLeftItemsNotEmpty(TOP_ROW_IDX, 0);
    125                         // Expand/collapse slot should be empty
    126                         assertNull("Expand/collapse should be empty" ,
    127                                 mActionBar.getViewAt(BOTTOM_ROW_IDX, NUM_COLS - 1));
    128                         // Slots on the bottom left should be taken while the rest should be empty.
    129                         assertLeftItemsNotEmpty(BOTTOM_ROW_IDX, numItems);
    130                     });
    131         }
    132     }
    133 
    134     private void assertRowVisibility(int rowIdx, int visibility) {
    135         assertEquals(visibility, mRowsContainer.getChildAt(rowIdx).getVisibility());
    136     }
    137 
    138     /**
    139      * Tests that the expand/collapse button is added if enough views are set
    140      */
    141     @Test
    142     public void testExpandCollapseEnabled() {
    143         setUpActionBarItems(NUM_COLS + 1);
    144 
    145         // Top row should have 2 slot taken (as expand/collapse takes one slot on the bottom row)
    146         onView(withContentDescription(R.string.action_bar_expand_collapse_button))
    147                 .check((view, noViewFoundException) -> {
    148                     Preconditions.checkNotNull(view);
    149                     assertLeftItemsNotEmpty(TOP_ROW_IDX, 2);
    150                     assertLeftItemsNotEmpty(BOTTOM_ROW_IDX, NUM_COLS);
    151                     assertRowVisibility(TOP_ROW_IDX, View.GONE);
    152                 })
    153                 // Check that expand/collapse works
    154                 .perform(click())
    155                 .check((view, noViewFoundException) -> {
    156                     assertRowVisibility(TOP_ROW_IDX, View.VISIBLE);
    157                 })
    158                 .perform(click())
    159                 .check((view, noViewFoundException) -> {
    160                     assertRowVisibility(TOP_ROW_IDX, View.GONE);
    161                 });
    162     }
    163 
    164     private void setViewInPosition(View view, @ActionBar.SlotPosition int position) {
    165         mActivity.runOnUiThread(() -> {
    166             mActionBar.setView(view, position);
    167         });
    168     }
    169 
    170     /**
    171      * Tests that reserved slots are not used by normal views.
    172      */
    173     @Test
    174     public void testReservingNamedSlots() {
    175         View mainView = createButton(mActivity);
    176         setViewInPosition(mainView, ActionBar.SLOT_MAIN);
    177         View leftView = new Space(mActivity);
    178         setViewInPosition(leftView, ActionBar.SLOT_LEFT);
    179         setUpActionBarItems(NUM_COLS + 1);
    180 
    181         // Expand/collapse plus two other slots should be taken in the bottom row.
    182         onView(withContentDescription(R.string.action_bar_expand_collapse_button))
    183                 .check((view, noViewFoundException) -> {
    184                     // Only 2 items fit in the bottom row. The remaining 4 should be on the top
    185                     Preconditions.checkNotNull(view);
    186                     assertLeftItemsNotEmpty(TOP_ROW_IDX, 4);
    187                     assertLeftItemsNotEmpty(BOTTOM_ROW_IDX, NUM_COLS);
    188                     assertRowVisibility(TOP_ROW_IDX, View.GONE);
    189                     assertEquals(mainView, mActionBar.getViewAt(BOTTOM_ROW_IDX, 2));
    190                     assertEquals(leftView, mActionBar.getViewAt(BOTTOM_ROW_IDX, 1));
    191                 })
    192                 .perform(click())
    193                 .check((view, noViewFoundException) -> {
    194                     assertRowVisibility(TOP_ROW_IDX, View.VISIBLE);
    195                 });
    196     }
    197 
    198     private void setExpandCollapseCustomView(View view) {
    199         mActivity.runOnUiThread(() -> {
    200             mActionBar.setExpandCollapseView(view);
    201         });
    202     }
    203 
    204     /**
    205      * Tests setting custom expand/collapse views.
    206      */
    207     @Test
    208     public void testCustomExpandCollapseView() {
    209         View customExpandCollapse = new ToggleButton(mActivity);
    210         customExpandCollapse.setContentDescription(mActivity.getString(
    211                 R.string.action_bar_expand_collapse_button));
    212         setExpandCollapseCustomView(customExpandCollapse);
    213         setUpActionBarItems(NUM_COLS + 1);
    214 
    215         onView(withContentDescription(R.string.action_bar_expand_collapse_button))
    216                 .check((view, noViewFoundException) -> {
    217                     Preconditions.checkNotNull(view);
    218                     assertEquals(customExpandCollapse, mActionBar.getViewAt(BOTTOM_ROW_IDX,
    219                             NUM_COLS - 1));
    220                 });
    221     }
    222 }
    223