• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package android.support.v7.widget;
17 
18 import static android.support.v7.widget.StaggeredGridLayoutManager.HORIZONTAL;
19 
20 import static org.hamcrest.CoreMatchers.is;
21 import static org.hamcrest.MatcherAssert.assertThat;
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertNull;
24 import static org.junit.Assert.assertTrue;
25 
26 import android.app.Activity;
27 import android.content.Context;
28 import android.graphics.Color;
29 import android.graphics.Rect;
30 import android.support.annotation.Nullable;
31 import android.support.v4.util.LongSparseArray;
32 import android.support.v7.widget.TestedFrameLayout.FullControlLayoutParams;
33 import android.util.Log;
34 import android.view.Gravity;
35 import android.view.View;
36 import android.view.ViewGroup;
37 import android.widget.TextView;
38 
39 import org.hamcrest.CoreMatchers;
40 import org.junit.Test;
41 
42 import org.hamcrest.CoreMatchers;
43 import org.hamcrest.MatcherAssert;
44 
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.concurrent.CountDownLatch;
49 import java.util.concurrent.TimeUnit;
50 
51 /**
52  * Class to test any generic wrap content behavior.
53  * It does so by running the same view scenario twice. Once with match parent setup to record all
54  * dimensions and once with wrap_content setup. Then compares all child locations & ids +
55  * RecyclerView size.
56  */
57 abstract public class BaseWrapContentTest extends BaseRecyclerViewInstrumentationTest {
58 
59     static final boolean DEBUG = false;
60     static final String TAG = "WrapContentTest";
61     RecyclerView.LayoutManager mLayoutManager;
62 
63     TestAdapter mTestAdapter;
64 
65     LoggingItemAnimator mLoggingItemAnimator;
66 
67     boolean mIsWrapContent;
68 
69     protected final WrapContentConfig mWrapContentConfig;
70 
BaseWrapContentTest(WrapContentConfig config)71     public BaseWrapContentTest(WrapContentConfig config) {
72         mWrapContentConfig = config;
73     }
74 
createLayoutManager()75     abstract RecyclerView.LayoutManager createLayoutManager();
76 
unspecifiedWithHintTest(boolean horizontal)77     void unspecifiedWithHintTest(boolean horizontal) throws Throwable {
78         final int itemHeight = 20;
79         final int itemWidth = 15;
80         RecyclerView.LayoutManager layoutManager = createLayoutManager();
81         WrappedRecyclerView rv = createRecyclerView(getActivity());
82         TestAdapter testAdapter = new TestAdapter(20) {
83             @Override
84             public void onBindViewHolder(TestViewHolder holder,
85                     int position) {
86                 super.onBindViewHolder(holder, position);
87                 holder.itemView.setLayoutParams(new ViewGroup.LayoutParams(itemWidth, itemHeight));
88             }
89         };
90         rv.setLayoutManager(layoutManager);
91         rv.setAdapter(testAdapter);
92         TestedFrameLayout.FullControlLayoutParams lp =
93                 new TestedFrameLayout.FullControlLayoutParams(0, 0);
94         if (horizontal) {
95             lp.wSpec = View.MeasureSpec.makeMeasureSpec(25, View.MeasureSpec.UNSPECIFIED);
96             lp.hSpec = View.MeasureSpec.makeMeasureSpec(50, View.MeasureSpec.AT_MOST);
97         } else {
98             lp.hSpec = View.MeasureSpec.makeMeasureSpec(25, View.MeasureSpec.UNSPECIFIED);
99             lp.wSpec = View.MeasureSpec.makeMeasureSpec(50, View.MeasureSpec.AT_MOST);
100         }
101         rv.setLayoutParams(lp);
102         setRecyclerView(rv);
103         rv.waitUntilLayout();
104 
105         // we don't assert against the given size hint because LM will still ask for more if it
106         // lays out more children. This is the correct behavior because the spec is not AT_MOST,
107         // it is UNSPECIFIED.
108         if (horizontal) {
109             int expectedWidth = rv.getPaddingLeft() + rv.getPaddingRight() + itemWidth;
110             while (expectedWidth < 25) {
111                 expectedWidth += itemWidth;
112             }
113             assertThat(rv.getWidth(), CoreMatchers.is(expectedWidth));
114         } else {
115             int expectedHeight = rv.getPaddingTop() + rv.getPaddingBottom() + itemHeight;
116             while (expectedHeight < 25) {
117                 expectedHeight += itemHeight;
118             }
119             assertThat(rv.getHeight(), CoreMatchers.is(expectedHeight));
120         }
121     }
122 
testScenerio(Scenario scenario)123     protected void testScenerio(Scenario scenario) throws Throwable {
124         FullControlLayoutParams matchParent = new FullControlLayoutParams(
125                 ViewGroup.LayoutParams.MATCH_PARENT,
126                 ViewGroup.LayoutParams.MATCH_PARENT);
127         FullControlLayoutParams wrapContent = new FullControlLayoutParams(
128                 ViewGroup.LayoutParams.WRAP_CONTENT,
129                 ViewGroup.LayoutParams.WRAP_CONTENT);
130         if (mWrapContentConfig.isUnlimitedHeight()) {
131             wrapContent.hSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
132         }
133         if (mWrapContentConfig.isUnlimitedWidth()) {
134             wrapContent.wSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
135         }
136 
137         mIsWrapContent = false;
138         List<Snapshot> s1 = runScenario(scenario, matchParent, null);
139         mIsWrapContent = true;
140 
141         List<Snapshot> s2 = runScenario(scenario, wrapContent, s1);
142         assertEquals("test sanity", s1.size(), s2.size());
143 
144         for (int i = 0; i < s1.size(); i++) {
145             Snapshot step1 = s1.get(i);
146             Snapshot step2 = s2.get(i);
147             step1.assertSame(step2, i);
148         }
149     }
150 
runScenario(Scenario scenario, ViewGroup.LayoutParams lp, @Nullable List<Snapshot> compareWith)151     public List<Snapshot> runScenario(Scenario scenario, ViewGroup.LayoutParams lp,
152             @Nullable List<Snapshot> compareWith)
153             throws Throwable {
154         removeRecyclerView();
155         Item.idCounter.set(0);
156         List<Snapshot> result = new ArrayList<>();
157         RecyclerView.LayoutManager layoutManager = scenario.createLayoutManager();
158         WrappedRecyclerView recyclerView = new WrappedRecyclerView(getActivity());
159         recyclerView.setBackgroundColor(Color.rgb(0, 0, 255));
160         recyclerView.setLayoutManager(layoutManager);
161         recyclerView.setLayoutParams(lp);
162         mLayoutManager = layoutManager;
163         mTestAdapter = new TestAdapter(scenario.getSeedAdapterSize());
164         recyclerView.setAdapter(mTestAdapter);
165         mLoggingItemAnimator = new LoggingItemAnimator();
166         recyclerView.setItemAnimator(mLoggingItemAnimator);
167         setRecyclerView(recyclerView);
168         recyclerView.waitUntilLayout();
169         int stepIndex = 0;
170         for (Step step : scenario.mStepList) {
171             mLoggingItemAnimator.reset();
172             step.onRun();
173             recyclerView.waitUntilLayout();
174             recyclerView.waitUntilAnimations();
175             Snapshot snapshot = takeSnapshot();
176             if (mIsWrapContent) {
177                 snapshot.assertRvSize();
178             }
179             result.add(snapshot);
180             if (compareWith != null) {
181                 compareWith.get(stepIndex).assertSame(snapshot, stepIndex);
182             }
183             stepIndex++;
184         }
185         recyclerView.waitUntilLayout();
186         recyclerView.waitUntilAnimations();
187         Snapshot snapshot = takeSnapshot();
188         if (mIsWrapContent) {
189             snapshot.assertRvSize();
190         }
191         result.add(snapshot);
192         if (compareWith != null) {
193             compareWith.get(stepIndex).assertSame(snapshot, stepIndex);
194         }
195         return result;
196     }
197 
createRecyclerView(Activity activity)198     protected WrappedRecyclerView createRecyclerView(Activity activity) {
199         return new WrappedRecyclerView(getActivity());
200     }
201 
layoutAndCheck(TestedFrameLayout.FullControlLayoutParams lp, BaseWrapContentWithAspectRatioTest.WrapContentAdapter adapter, Rect[] expected, int width, int height)202     void layoutAndCheck(TestedFrameLayout.FullControlLayoutParams lp,
203             BaseWrapContentWithAspectRatioTest.WrapContentAdapter adapter, Rect[] expected,
204             int width, int height) throws Throwable {
205         WrappedRecyclerView recyclerView = createRecyclerView(getActivity());
206         recyclerView.setBackgroundColor(Color.rgb(0, 0, 255));
207         recyclerView.setLayoutManager(createLayoutManager());
208         recyclerView.setAdapter(adapter);
209         recyclerView.setLayoutParams(lp);
210         Rect padding = mWrapContentConfig.padding;
211         recyclerView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
212         setRecyclerView(recyclerView);
213         recyclerView.waitUntilLayout();
214         Snapshot snapshot = takeSnapshot();
215         int index = 0;
216         Rect tmp = new Rect();
217         for (BaseWrapContentWithAspectRatioTest.MeasureBehavior behavior : adapter.behaviors) {
218             tmp.set(expected[index]);
219             tmp.offset(padding.left, padding.top);
220             assertThat("behavior " + index, snapshot.mChildCoordinates.get(behavior.getId()),
221                     is(tmp));
222             index ++;
223         }
224         Rect boundingBox = new Rect(0, 0, 0, 0);
225         for (Rect rect : expected) {
226             boundingBox.union(rect);
227         }
228         assertThat(recyclerView.getWidth(), is(width + padding.left + padding.right));
229         assertThat(recyclerView.getHeight(), is(height + padding.top + padding.bottom));
230     }
231 
232 
getVerticalGravity(RecyclerView.LayoutManager layoutManager)233     abstract protected int getVerticalGravity(RecyclerView.LayoutManager layoutManager);
234 
getHorizontalGravity(RecyclerView.LayoutManager layoutManager)235     abstract protected int getHorizontalGravity(RecyclerView.LayoutManager layoutManager);
236 
takeSnapshot()237     protected Snapshot takeSnapshot() throws Throwable {
238         Snapshot snapshot = new Snapshot(mRecyclerView, mLoggingItemAnimator,
239                 getHorizontalGravity(mLayoutManager), getVerticalGravity(mLayoutManager));
240         return snapshot;
241     }
242 
243     abstract class Scenario {
244 
245         ArrayList<Step> mStepList = new ArrayList<>();
246 
Scenario(Step... steps)247         public Scenario(Step... steps) {
248             Collections.addAll(mStepList, steps);
249         }
250 
getSeedAdapterSize()251         public int getSeedAdapterSize() {
252             return 10;
253         }
254 
createLayoutManager()255         public RecyclerView.LayoutManager createLayoutManager() {
256             return BaseWrapContentTest.this.createLayoutManager();
257         }
258     }
259 
260     abstract static class Step {
261 
onRun()262         abstract void onRun() throws Throwable;
263     }
264 
265     class Snapshot {
266 
267         Rect mRawChildrenBox = new Rect();
268 
269         Rect mRvSize = new Rect();
270 
271         Rect mRvPadding = new Rect();
272 
273         Rect mRvParentSize = new Rect();
274 
275         LongSparseArray<Rect> mChildCoordinates = new LongSparseArray<>();
276 
277         LongSparseArray<String> mAppear = new LongSparseArray<>();
278 
279         LongSparseArray<String> mDisappear = new LongSparseArray<>();
280 
281         LongSparseArray<String> mPersistent = new LongSparseArray<>();
282 
283         LongSparseArray<String> mChanged = new LongSparseArray<>();
284 
285         int mVerticalGravity;
286 
287         int mHorizontalGravity;
288 
289         int mOffsetX, mOffsetY;// how much we should offset children
290 
Snapshot(RecyclerView recyclerView, LoggingItemAnimator loggingItemAnimator, int horizontalGravity, int verticalGravity)291         public Snapshot(RecyclerView recyclerView, LoggingItemAnimator loggingItemAnimator,
292                 int horizontalGravity, int verticalGravity)
293                 throws Throwable {
294             mRvSize = getViewBounds(recyclerView);
295             mRvParentSize = getViewBounds((View) recyclerView.getParent());
296             mRvPadding = new Rect(recyclerView.getPaddingLeft(), recyclerView.getPaddingTop(),
297                     recyclerView.getPaddingRight(), recyclerView.getPaddingBottom());
298             mVerticalGravity = verticalGravity;
299             mHorizontalGravity = horizontalGravity;
300             if (mVerticalGravity == Gravity.TOP) {
301                 mOffsetY = 0;
302             } else {
303                 mOffsetY = mRvParentSize.bottom - mRvSize.bottom;
304             }
305 
306             if (mHorizontalGravity == Gravity.LEFT) {
307                 mOffsetX = 0;
308             } else {
309                 mOffsetX = mRvParentSize.right - mRvSize.right;
310             }
311             collectChildCoordinates(recyclerView);
312             if (loggingItemAnimator != null) {
313                 collectInto(mAppear, loggingItemAnimator.mAnimateAppearanceList);
314                 collectInto(mDisappear, loggingItemAnimator.mAnimateDisappearanceList);
315                 collectInto(mPersistent, loggingItemAnimator.mAnimatePersistenceList);
316                 collectInto(mChanged, loggingItemAnimator.mAnimateChangeList);
317             }
318         }
319 
doesChildrenFitVertically()320         public boolean doesChildrenFitVertically() {
321             return mRawChildrenBox.top >= mRvPadding.top
322                     && mRawChildrenBox.bottom <= mRvSize.bottom - mRvPadding.bottom;
323         }
324 
doesChildrenFitHorizontally()325         public boolean doesChildrenFitHorizontally() {
326             return mRawChildrenBox.left >= mRvPadding.left
327                     && mRawChildrenBox.right <= mRvSize.right - mRvPadding.right;
328         }
329 
assertSame(Snapshot other, int step)330         public void assertSame(Snapshot other, int step) {
331             if (mWrapContentConfig.isUnlimitedHeight() &&
332                     (!doesChildrenFitVertically() || !other.doesChildrenFitVertically())) {
333                 if (DEBUG) {
334                     Log.d(TAG, "cannot assert coordinates because it does not fit vertically");
335                 }
336                 return;
337             }
338             if (mWrapContentConfig.isUnlimitedWidth() &&
339                     (!doesChildrenFitHorizontally() || !other.doesChildrenFitHorizontally())) {
340                 if (DEBUG) {
341                     Log.d(TAG, "cannot assert coordinates because it does not fit horizontally");
342                 }
343                 return;
344             }
345             assertMap("child coordinates. step:" + step, mChildCoordinates,
346                     other.mChildCoordinates);
347             if (mWrapContentConfig.isUnlimitedHeight() || mWrapContentConfig.isUnlimitedWidth()) {
348                 return;//cannot assert animatinos in unlimited size
349             }
350             assertMap("appearing step:" + step, mAppear, other.mAppear);
351             assertMap("disappearing step:" + step, mDisappear, other.mDisappear);
352             assertMap("persistent step:" + step, mPersistent, other.mPersistent);
353             assertMap("changed step:" + step, mChanged, other.mChanged);
354         }
355 
assertMap(String prefix, LongSparseArray<?> map1, LongSparseArray<?> map2)356         private void assertMap(String prefix, LongSparseArray<?> map1, LongSparseArray<?> map2) {
357             StringBuilder logBuilder = new StringBuilder();
358             logBuilder.append(prefix).append("\n");
359             logBuilder.append("map1").append("\n");
360             logInto(map1, logBuilder);
361             logBuilder.append("map2").append("\n");
362             logInto(map2, logBuilder);
363             final String log = logBuilder.toString();
364             assertEquals(log + " same size", map1.size(), map2.size());
365             for (int i = 0; i < map1.size(); i++) {
366                 assertAtIndex(log, map1, map2, i);
367             }
368         }
369 
assertAtIndex(String prefix, LongSparseArray<?> map1, LongSparseArray<?> map2, int index)370         private void assertAtIndex(String prefix, LongSparseArray<?> map1, LongSparseArray<?> map2,
371                 int index) {
372             long key1 = map1.keyAt(index);
373             long key2 = map2.keyAt(index);
374             assertEquals(prefix + "key mismatch at index " + index, key1, key2);
375             Object value1 = map1.valueAt(index);
376             Object value2 = map2.valueAt(index);
377             assertEquals(prefix + " value mismatch at index " + index, value1, value2);
378         }
379 
logInto(LongSparseArray<?> map, StringBuilder sb)380         private void logInto(LongSparseArray<?> map, StringBuilder sb) {
381             for (int i = 0; i < map.size(); i++) {
382                 long key = map.keyAt(i);
383                 Object value = map.valueAt(i);
384                 sb.append(key).append(" : ").append(value).append("\n");
385             }
386         }
387 
388         @Override
toString()389         public String toString() {
390             StringBuilder sb = new StringBuilder("Snapshot{\n");
391             sb.append("child coordinates:\n");
392             logInto(mChildCoordinates, sb);
393             sb.append("appear animations:\n");
394             logInto(mAppear, sb);
395             sb.append("disappear animations:\n");
396             logInto(mDisappear, sb);
397             sb.append("change animations:\n");
398             logInto(mChanged, sb);
399             sb.append("persistent animations:\n");
400             logInto(mPersistent, sb);
401             sb.append("}");
402             return sb.toString();
403         }
404 
405         @Override
hashCode()406         public int hashCode() {
407             int result = mChildCoordinates.hashCode();
408             result = 31 * result + mAppear.hashCode();
409             result = 31 * result + mDisappear.hashCode();
410             result = 31 * result + mPersistent.hashCode();
411             result = 31 * result + mChanged.hashCode();
412             return result;
413         }
414 
collectInto( LongSparseArray<String> target, List<? extends BaseRecyclerViewAnimationsTest.AnimateLogBase> list)415         private void collectInto(
416                 LongSparseArray<String> target,
417                 List<? extends BaseRecyclerViewAnimationsTest.AnimateLogBase> list) {
418             for (BaseRecyclerViewAnimationsTest.AnimateLogBase base : list) {
419                 long id = getItemId(base.viewHolder);
420                 assertNull(target.get(id));
421                 target.put(id, log(base));
422             }
423         }
424 
log(BaseRecyclerViewAnimationsTest.AnimateLogBase base)425         private String log(BaseRecyclerViewAnimationsTest.AnimateLogBase base) {
426             return base.getClass().getSimpleName() +
427                     ((TextView) base.viewHolder.itemView).getText() + ": " +
428                     "[pre:" + log(base.postInfo) +
429                     ", post:" + log(base.postInfo) + "]";
430         }
431 
log(BaseRecyclerViewAnimationsTest.LoggingInfo postInfo)432         private String log(BaseRecyclerViewAnimationsTest.LoggingInfo postInfo) {
433             if (postInfo == null) {
434                 return "?";
435             }
436             return "PI[flags: " + postInfo.changeFlags
437                     + ",l:" + (postInfo.left + mOffsetX)
438                     + ",t:" + (postInfo.top + mOffsetY)
439                     + ",r:" + (postInfo.right + mOffsetX)
440                     + ",b:" + (postInfo.bottom + mOffsetY) + "]";
441         }
442 
collectChildCoordinates(RecyclerView recyclerView)443         void collectChildCoordinates(RecyclerView recyclerView) throws Throwable {
444             mRawChildrenBox = new Rect(0, 0, 0, 0);
445             final int childCount = recyclerView.getChildCount();
446             for (int i = 0; i < childCount; i++) {
447                 View child = recyclerView.getChildAt(i);
448                 Rect childBounds = getChildBounds(recyclerView, child, true);
449                 mRawChildrenBox.union(getChildBounds(recyclerView, child, false));
450                 RecyclerView.ViewHolder childViewHolder = recyclerView.getChildViewHolder(child);
451                 mChildCoordinates.put(getItemId(childViewHolder), childBounds);
452             }
453         }
454 
getViewBounds(View view)455         private Rect getViewBounds(View view) {
456             return new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
457         }
458 
getChildBounds(RecyclerView recyclerView, View child, boolean offset)459         private Rect getChildBounds(RecyclerView recyclerView, View child, boolean offset) {
460             RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
461             RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
462             Rect rect = new Rect(layoutManager.getDecoratedLeft(child) - lp.leftMargin,
463                     layoutManager.getDecoratedTop(child) - lp.topMargin,
464                     layoutManager.getDecoratedRight(child) + lp.rightMargin,
465                     layoutManager.getDecoratedBottom(child) + lp.bottomMargin);
466             if (offset) {
467                 rect.offset(mOffsetX, mOffsetY);
468             }
469             return rect;
470         }
471 
getItemId(RecyclerView.ViewHolder vh)472         private long getItemId(RecyclerView.ViewHolder vh) {
473             if (vh instanceof TestViewHolder) {
474                 return ((TestViewHolder) vh).mBoundItem.mId;
475             } else if (vh instanceof BaseWrapContentWithAspectRatioTest.WrapContentViewHolder) {
476                 BaseWrapContentWithAspectRatioTest.WrapContentViewHolder casted =
477                         (BaseWrapContentWithAspectRatioTest.WrapContentViewHolder) vh;
478                 return casted.mView.mBehavior.getId();
479             } else {
480                 throw new IllegalArgumentException("i don't support any VH");
481             }
482         }
483 
assertRvSize()484         public void assertRvSize() {
485             if (shouldWrapContentHorizontally()) {
486                 int expectedW = mRawChildrenBox.width() + mRvPadding.left + mRvPadding.right;
487                 assertTrue(mRvSize.width() + " <= " + expectedW, mRvSize.width() <= expectedW);
488             }
489             if (shouldWrapContentVertically()) {
490                 int expectedH = mRawChildrenBox.height() + mRvPadding.top + mRvPadding.bottom;
491                 assertTrue(mRvSize.height() + "<=" + expectedH, mRvSize.height() <= expectedH);
492             }
493         }
494     }
495 
shouldWrapContentHorizontally()496     protected boolean shouldWrapContentHorizontally() {
497         return true;
498     }
499 
shouldWrapContentVertically()500     protected boolean shouldWrapContentVertically() {
501         return true;
502     }
503 
504     static class WrappedRecyclerView extends RecyclerView {
505 
WrappedRecyclerView(Context context)506         public WrappedRecyclerView(Context context) {
507             super(context);
508         }
509 
waitUntilLayout()510         public void waitUntilLayout() {
511             while (isLayoutRequested()) {
512                 try {
513                     Thread.sleep(100);
514                 } catch (InterruptedException e) {
515                     e.printStackTrace();
516                 }
517             }
518         }
519 
waitUntilAnimations()520         public void waitUntilAnimations() throws InterruptedException {
521             final CountDownLatch latch = new CountDownLatch(1);
522             if (mItemAnimator == null || !mItemAnimator.isRunning(
523                     new ItemAnimator.ItemAnimatorFinishedListener() {
524                         @Override
525                         public void onAnimationsFinished() {
526                             latch.countDown();
527                         }
528                     })) {
529                 latch.countDown();
530             }
531             MatcherAssert.assertThat("waiting too long for animations",
532                     latch.await(60, TimeUnit.SECONDS), CoreMatchers.is(true));
533         }
534 
535         @Override
onLayout(boolean changed, int l, int t, int r, int b)536         protected void onLayout(boolean changed, int l, int t, int r, int b) {
537             super.onLayout(changed, l, t, r, b);
538         }
539     }
540 
541     static class WrapContentConfig {
542 
543         public boolean unlimitedWidth;
544         public boolean unlimitedHeight;
545         public Rect padding = new Rect(0, 0, 0, 0);
546 
WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight)547         public WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight) {
548             this.unlimitedWidth = unlimitedWidth;
549             this.unlimitedHeight = unlimitedHeight;
550         }
551 
WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight, Rect padding)552         public WrapContentConfig(boolean unlimitedWidth, boolean unlimitedHeight, Rect padding) {
553             this.unlimitedWidth = unlimitedWidth;
554             this.unlimitedHeight = unlimitedHeight;
555             this.padding.set(padding);
556         }
557 
isUnlimitedWidth()558         public boolean isUnlimitedWidth() {
559             return unlimitedWidth;
560         }
561 
setUnlimitedWidth(boolean unlimitedWidth)562         public WrapContentConfig setUnlimitedWidth(boolean unlimitedWidth) {
563             this.unlimitedWidth = unlimitedWidth;
564             return this;
565         }
566 
isUnlimitedHeight()567         public boolean isUnlimitedHeight() {
568             return unlimitedHeight;
569         }
570 
setUnlimitedHeight(boolean unlimitedHeight)571         public WrapContentConfig setUnlimitedHeight(boolean unlimitedHeight) {
572             this.unlimitedHeight = unlimitedHeight;
573             return this;
574         }
575 
576         @Override
toString()577         public String toString() {
578             return "WrapContentConfig{"
579                     + "unlimitedWidth=" + unlimitedWidth
580                     + ", unlimitedHeight=" + unlimitedHeight
581                     + ", padding=" + padding
582                     + '}';
583         }
584 
toLayoutParams(int wDim, int hDim)585         public TestedFrameLayout.FullControlLayoutParams toLayoutParams(int wDim, int hDim) {
586             TestedFrameLayout.FullControlLayoutParams
587                     lp = new TestedFrameLayout.FullControlLayoutParams(
588                     wDim, hDim);
589             if (unlimitedWidth) {
590                 lp.wSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
591             }
592             if (unlimitedHeight) {
593                 lp.hSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
594             }
595             return lp;
596         }
597     }
598 }
599