1/* 2 * Copyright (C) 2015 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 17package android.support.design.testutils; 18 19import android.support.annotation.LayoutRes; 20import android.support.annotation.Nullable; 21import android.support.annotation.StringRes; 22import android.support.design.widget.CollapsingToolbarLayout; 23import android.support.design.widget.TabLayout; 24import android.support.test.espresso.UiController; 25import android.support.test.espresso.ViewAction; 26import android.support.v4.view.ViewCompat; 27import android.support.v4.view.ViewPager; 28import android.support.v7.widget.Toolbar; 29import android.view.LayoutInflater; 30import android.view.View; 31import android.view.ViewGroup; 32import android.widget.TextView; 33import org.hamcrest.Matcher; 34 35import static android.support.test.espresso.matcher.ViewMatchers.*; 36 37public class TestUtilsActions { 38 /** 39 * Replaces an existing {@link TabLayout} with a new one inflated from the specified 40 * layout resource. 41 */ 42 public static ViewAction replaceTabLayout(final @LayoutRes int tabLayoutResId) { 43 return new ViewAction() { 44 @Override 45 public Matcher<View> getConstraints() { 46 return isDisplayingAtLeast(90); 47 } 48 49 @Override 50 public String getDescription() { 51 return "Replace TabLayout"; 52 } 53 54 @Override 55 public void perform(UiController uiController, View view) { 56 uiController.loopMainThreadUntilIdle(); 57 58 final ViewGroup viewGroup = (ViewGroup) view; 59 final int childCount = viewGroup.getChildCount(); 60 // Iterate over children and find TabLayout 61 for (int i = 0; i < childCount; i++) { 62 View child = viewGroup.getChildAt(i); 63 if (child instanceof TabLayout) { 64 // Remove the existing TabLayout 65 viewGroup.removeView(child); 66 // Create a new one 67 final LayoutInflater layoutInflater = 68 LayoutInflater.from(view.getContext()); 69 final TabLayout newTabLayout = (TabLayout) layoutInflater.inflate( 70 tabLayoutResId, viewGroup, false); 71 // Make sure we're adding the new TabLayout at the same index 72 viewGroup.addView(newTabLayout, i); 73 break; 74 } 75 } 76 77 uiController.loopMainThreadUntilIdle(); 78 } 79 }; 80 } 81 82 /** 83 * Sets layout direction on the view. 84 */ 85 public static ViewAction setLayoutDirection(final int layoutDirection) { 86 return new ViewAction() { 87 @Override 88 public Matcher<View> getConstraints() { 89 return isDisplayed(); 90 } 91 92 @Override 93 public String getDescription() { 94 return "set layout direction"; 95 } 96 97 @Override 98 public void perform(UiController uiController, View view) { 99 uiController.loopMainThreadUntilIdle(); 100 101 ViewCompat.setLayoutDirection(view, layoutDirection); 102 103 uiController.loopMainThreadUntilIdle(); 104 } 105 }; 106 } 107 108 /** 109 * Sets title on the {@link CollapsingToolbarLayout}. 110 */ 111 public static ViewAction setTitle(final CharSequence title) { 112 return new ViewAction() { 113 @Override 114 public Matcher<View> getConstraints() { 115 return isAssignableFrom(CollapsingToolbarLayout.class); 116 } 117 118 @Override 119 public String getDescription() { 120 return "set toolbar title"; 121 } 122 123 @Override 124 public void perform(UiController uiController, View view) { 125 uiController.loopMainThreadUntilIdle(); 126 127 CollapsingToolbarLayout collapsingToolbarLayout = 128 (CollapsingToolbarLayout) view; 129 collapsingToolbarLayout.setTitle(title); 130 131 uiController.loopMainThreadUntilIdle(); 132 } 133 }; 134 } 135 136 /** 137 * Sets text content on {@link TextView} 138 */ 139 public static ViewAction setText(final @Nullable CharSequence text) { 140 return new ViewAction() { 141 @Override 142 public Matcher<View> getConstraints() { 143 return isAssignableFrom(TextView.class); 144 } 145 146 @Override 147 public String getDescription() { 148 return "TextView set text"; 149 } 150 151 @Override 152 public void perform(UiController uiController, View view) { 153 uiController.loopMainThreadUntilIdle(); 154 155 TextView textView = (TextView) view; 156 textView.setText(text); 157 158 uiController.loopMainThreadUntilIdle(); 159 } 160 }; 161 } 162 163 /** 164 * Adds tabs to {@link TabLayout} 165 */ 166 public static ViewAction addTabs(final String... tabs) { 167 return new ViewAction() { 168 @Override 169 public Matcher<View> getConstraints() { 170 return isAssignableFrom(TabLayout.class); 171 } 172 173 @Override 174 public String getDescription() { 175 return "TabLayout add tabs"; 176 } 177 178 @Override 179 public void perform(UiController uiController, View view) { 180 uiController.loopMainThreadUntilIdle(); 181 182 TabLayout tabLayout = (TabLayout) view; 183 for (int i = 0; i < tabs.length; i++) { 184 tabLayout.addTab(tabLayout.newTab().setText(tabs[i])); 185 } 186 187 uiController.loopMainThreadUntilIdle(); 188 } 189 }; 190 } 191 192 /** 193 * Dummy Espresso action that waits until the UI thread is idle. This action can be performed 194 * on the root view to wait for an ongoing animation to be completed. 195 */ 196 public static ViewAction waitUntilIdle() { 197 return new ViewAction() { 198 @Override 199 public Matcher<View> getConstraints() { 200 return isRoot(); 201 } 202 203 @Override 204 public String getDescription() { 205 return "wait for idle"; 206 } 207 208 @Override 209 public void perform(UiController uiController, View view) { 210 uiController.loopMainThreadUntilIdle(); 211 } 212 }; 213 } 214} 215