Merge "Fix start/end margin for CarListDialog's dividers." into oc-mr1-jetpack-dev
diff --git a/car/res/layout/car_paged_recycler_view.xml b/car/res/layout/car_paged_recycler_view.xml
index e7b1f61..d1312bf 100644
--- a/car/res/layout/car_paged_recycler_view.xml
+++ b/car/res/layout/car_paged_recycler_view.xml
@@ -30,7 +30,7 @@
android:id="@+id/paged_scroll_view"
android:layout_width="@dimen/car_margin"
android:layout_height="match_parent"
- android:paddingBottom="@dimen/car_scroll_bar_padding"
- android:paddingTop="@dimen/car_scroll_bar_padding"
+ android:paddingBottom="@dimen/car_padding_4"
+ android:paddingTop="@dimen/car_padding_4"
android:visibility="invisible" />
</FrameLayout>
diff --git a/car/res/layout/car_paged_scrollbar_buttons.xml b/car/res/layout/car_paged_scrollbar_buttons.xml
index b32877f..b126b48 100644
--- a/car/res/layout/car_paged_scrollbar_buttons.xml
+++ b/car/res/layout/car_paged_scrollbar_buttons.xml
@@ -36,8 +36,8 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:layout_marginBottom="@dimen/car_scroll_bar_thumb_margin"
- android:layout_marginTop="@dimen/car_scroll_bar_thumb_margin" >
+ android:layout_marginBottom="@dimen/car_padding_2"
+ android:layout_marginTop="@dimen/car_padding_2" >
<View
android:id="@+id/scrollbar_thumb"
diff --git a/car/res/values-h668dp/dimens.xml b/car/res/values-h668dp/dimens.xml
index 4c7aae6..ab0f120 100644
--- a/car/res/values-h668dp/dimens.xml
+++ b/car/res/values-h668dp/dimens.xml
@@ -34,12 +34,6 @@
<!-- Slide Up Menu -->
<dimen name="car_slide_up_menu_initial_height">116dp</dimen>
- <!-- Scroll Bar -->
- <dimen name="car_scroll_bar_padding">@dimen/car_padding_4</dimen>
-
- <!-- Scroll Bar Thumb -->
- <dimen name="car_scroll_bar_thumb_margin">@dimen/car_padding_2</dimen>
-
<!-- Scroll Bar Buttons -->
<dimen name="car_scroll_bar_button_size">76dp</dimen>
diff --git a/car/res/values/dimens.xml b/car/res/values/dimens.xml
index 8312800..4a5010f 100644
--- a/car/res/values/dimens.xml
+++ b/car/res/values/dimens.xml
@@ -122,12 +122,8 @@
<dimen name="car_seekbar_thumb_size">20dp</dimen>
<dimen name="car_seekbar_thumb_stroke">1dp</dimen>
- <!-- Scroll Bar -->
- <dimen name="car_scroll_bar_padding">@dimen/car_padding_2</dimen>
-
<!-- Scroll Bar Thumb -->
<dimen name="car_scroll_bar_thumb_width">6dp</dimen>
- <dimen name="car_scroll_bar_thumb_margin">@dimen/car_padding_1</dimen>
<!-- Scroll Bar and Alpha Jump Buttons -->
<dimen name="car_scroll_bar_button_size">56dp</dimen>
diff --git a/car/src/androidTest/AndroidManifest.xml b/car/src/androidTest/AndroidManifest.xml
index 79ac201..24b98bc 100644
--- a/car/src/androidTest/AndroidManifest.xml
+++ b/car/src/androidTest/AndroidManifest.xml
@@ -21,9 +21,13 @@
<application android:supportsRtl="true">
<activity android:name="androidx.car.drawer.CarDrawerTestActivity"
android:theme="@style/Theme.Car.Light.NoActionBar.Drawer" />
- <activity android:name="androidx.car.widget.ColumnCardViewTestActivity"/>
- <activity android:name="androidx.car.widget.PagedListViewSavedStateActivity"/>
- <activity android:name="androidx.car.widget.PagedListViewTestActivity"/>
- <activity android:name="androidx.car.widget.DividerVisibilityManagerTestActivity"/>
+ <activity android:name="androidx.car.widget.ColumnCardViewTestActivity"
+ android:theme="@style/Theme.Car.Light.NoActionBar" />
+ <activity android:name="androidx.car.widget.PagedListViewSavedStateActivity"
+ android:theme="@style/Theme.Car.Light.NoActionBar" />
+ <activity android:name="androidx.car.widget.PagedListViewTestActivity"
+ android:theme="@style/Theme.Car.Light.NoActionBar" />
+ <activity android:name="androidx.car.widget.DividerVisibilityManagerTestActivity"
+ android:theme="@style/Theme.Car.Light.NoActionBar" />
</application>
</manifest>
diff --git a/car/src/androidTest/java/androidx/car/drawer/CarDrawerTest.java b/car/src/androidTest/java/androidx/car/drawer/CarDrawerTest.java
index 81ef980..711808f 100644
--- a/car/src/androidTest/java/androidx/car/drawer/CarDrawerTest.java
+++ b/car/src/androidTest/java/androidx/car/drawer/CarDrawerTest.java
@@ -25,6 +25,7 @@
import static org.junit.Assume.assumeThat;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.contrib.DrawerActions;
@@ -32,6 +33,7 @@
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
@@ -51,8 +53,14 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
public final class CarDrawerTest {
+ // Note that launchActivity is passed "false" here because we only want to create the
+ // Activity after we checked that the test is being run on an auto device. Otherwise, this will
+ // cause an error due to classes not being found.
@Rule
- public ActivityTestRule<CarDrawerTestActivity> mActivityRule;
+ public ActivityTestRule<CarDrawerTestActivity> mActivityRule = new ActivityTestRule<>(
+ CarDrawerTestActivity.class,
+ false /* initialTouchMode */,
+ false /* launchActivity */);
private CarDrawerTestActivity mActivity;
private PagedListView mDrawerList;
@@ -67,19 +75,21 @@
public void setUp() {
Assume.assumeTrue(isAutoDevice());
- // Inflate the activity here rather than initialization because it needs to happen after
- // the isAutoDevice() check. Otherwise, errors will be thrown about "android.car.Car"
- // classes not being found.
- mActivityRule = new ActivityTestRule<>(CarDrawerTestActivity.class);
-
+ // Retrieve the activity after the isAutoDevice() check because this class depends on
+ // car-related classes (android.car.Car). These classes will not be available on non-auto
+ // devices.
+ mActivityRule.launchActivity(new Intent());
mActivity = mActivityRule.getActivity();
- try {
- mActivityRule.runOnUiThread(() -> {
- mDrawerList = mActivity.findViewById(R.id.drawer_list);
- });
- } catch (Throwable throwable) {
- throwable.printStackTrace();
- throw new RuntimeException(throwable);
+
+ mDrawerList = mActivity.findViewById(R.id.drawer_list);
+ }
+
+ @After
+ public void tearDown() {
+ // The Activity is only launched if the test was run on an auto device. If it's been
+ // launched, then explicitly finish it here since it was also explicitly launched.
+ if (isAutoDevice()) {
+ mActivityRule.finishActivity();
}
}
diff --git a/car/src/main/java/androidx/car/widget/TextListItem.java b/car/src/main/java/androidx/car/widget/TextListItem.java
index d4509a9..8cae7ac 100644
--- a/car/src/main/java/androidx/car/widget/TextListItem.java
+++ b/car/src/main/java/androidx/car/widget/TextListItem.java
@@ -356,8 +356,8 @@
if (mIsBodyPrimary) {
mBinders.add(vh -> {
- vh.getTitle().setTextAppearance(getTitleTextAppearance());
- vh.getBody().setTextAppearance(getBodyTextAppearance());
+ vh.getTitle().setTextAppearance(getBodyTextAppearance());
+ vh.getBody().setTextAppearance(getTitleTextAppearance());
});
} else {
mBinders.add(vh -> {
diff --git a/fragment/src/androidTest/java/android/support/v4/app/LoaderInfoTest.java b/fragment/src/androidTest/java/android/support/v4/app/LoaderInfoTest.java
index 799b6ab..7ea8014 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/LoaderInfoTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/LoaderInfoTest.java
@@ -18,7 +18,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import android.arch.lifecycle.Lifecycle;
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.LifecycleRegistry;
+import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.test.annotation.UiThreadTest;
@@ -29,6 +35,7 @@
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,6 +47,18 @@
@SmallTest
public class LoaderInfoTest {
+ private LifecycleOwner mOwner;
+ private LifecycleRegistry mRegistry;
+
+ @Before
+ public void setup() {
+ mOwner = mock(LifecycleOwner.class);
+ mRegistry = new LifecycleRegistry(mOwner);
+ when(mOwner.getLifecycle()).thenReturn(mRegistry);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ }
+
@Rule
public ActivityTestRule<LoaderActivity> mActivityRule =
new ActivityTestRule<>(LoaderActivity.class);
@@ -136,6 +155,61 @@
@UiThreadTest
@Test
+ public void testMarkForRedelivery() throws Throwable {
+ LoaderTest.DummyLoaderCallbacks loaderCallback =
+ new LoaderTest.DummyLoaderCallbacks(mock(Context.class));
+ Loader<Boolean> loader = loaderCallback.onCreateLoader(0, null);
+ LoaderManagerImpl.LoaderInfo<Boolean> loaderInfo = new LoaderManagerImpl.LoaderInfo<>(
+ 0, null, loader);
+ loaderInfo.setCallback(mOwner, loaderCallback);
+ assertTrue("onLoadFinished should be called after setCallback",
+ loaderCallback.mOnLoadFinished);
+
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+ loaderCallback.mOnLoadFinished = false;
+ loaderInfo.markForRedelivery();
+ assertFalse("onLoadFinished should not be called when stopped after markForRedelivery",
+ loaderCallback.mOnLoadFinished);
+
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ assertTrue("onLoadFinished should be called after markForRedelivery",
+ loaderCallback.mOnLoadFinished);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testMarkForRedelivery_replace() throws Throwable {
+ LoaderTest.DummyLoaderCallbacks initialCallback =
+ new LoaderTest.DummyLoaderCallbacks(mock(Context.class));
+ Loader<Boolean> loader = initialCallback.onCreateLoader(0, null);
+ LoaderManagerImpl.LoaderInfo<Boolean> loaderInfo = new LoaderManagerImpl.LoaderInfo<>(
+ 0, null, loader);
+ loaderInfo.setCallback(mOwner, initialCallback);
+ assertTrue("onLoadFinished for initial should be called after setCallback initial",
+ initialCallback.mOnLoadFinished);
+
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
+ initialCallback.mOnLoadFinished = false;
+ loaderInfo.markForRedelivery();
+ assertFalse("onLoadFinished should not be called when stopped after markForRedelivery",
+ initialCallback.mOnLoadFinished);
+
+ // Replace the callback
+ final LoaderTest.DummyLoaderCallbacks replacementCallback =
+ new LoaderTest.DummyLoaderCallbacks(mock(Context.class));
+ loaderInfo.setCallback(mOwner, replacementCallback);
+
+ mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+ assertFalse("onLoadFinished for initial should not be called "
+ + "after setCallback replacement",
+ initialCallback.mOnLoadFinished);
+ assertTrue("onLoadFinished for replacement should be called "
+ + " after setCallback replacement",
+ replacementCallback.mOnLoadFinished);
+ }
+
+ @UiThreadTest
+ @Test
public void testDestroy() throws Throwable {
final LoaderTest.DummyLoaderCallbacks loaderCallback =
new LoaderTest.DummyLoaderCallbacks(mActivityRule.getActivity());
diff --git a/fragment/src/androidTest/java/android/support/v4/app/ViewModelTest.java b/fragment/src/androidTest/java/android/support/v4/app/ViewModelTest.java
index 3c17e26..7c6e8fb 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/ViewModelTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/ViewModelTest.java
@@ -18,6 +18,7 @@
import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
+
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.notNullValue;
@@ -188,6 +189,39 @@
}
@Test
+ public void testFragmentOnClearedWhenFinished() throws Throwable {
+ final ViewModelActivity activity = mActivityRule.getActivity();
+ final ViewModelFragment fragment = getFragment(activity,
+ ViewModelActivity.FRAGMENT_TAG_1);
+ final CountDownLatch latch = new CountDownLatch(1);
+ final LifecycleObserver observer = new LifecycleObserver() {
+ @SuppressWarnings("unused")
+ @OnLifecycleEvent(ON_DESTROY)
+ void onDestroy() {
+ activity.getWindow().getDecorView().post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ assertThat(fragment.fragmentModel.mCleared, is(true));
+ } finally {
+ latch.countDown();
+ }
+ }
+ });
+ }
+ };
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ activity.getLifecycle().addObserver(observer);
+ }
+ });
+ activity.finish();
+ assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS), is(true));
+ }
+
+ @Test
public void testFragmentOnCleared() throws Throwable {
final ViewModelActivity activity = mActivityRule.getActivity();
final CountDownLatch latch = new CountDownLatch(1);
diff --git a/fragment/src/main/java/android/support/v4/app/Fragment.java b/fragment/src/main/java/android/support/v4/app/Fragment.java
index b15c49f..5d5caab 100644
--- a/fragment/src/main/java/android/support/v4/app/Fragment.java
+++ b/fragment/src/main/java/android/support/v4/app/Fragment.java
@@ -1642,7 +1642,8 @@
@CallSuper
public void onDestroy() {
mCalled = true;
- if (mViewModelStore != null && !mHost.mFragmentManager.isStateSaved()) {
+ // Use mStateSaved instead of isStateSaved() since we're past onStop()
+ if (mViewModelStore != null && !mHost.mFragmentManager.mStateSaved) {
mViewModelStore.clear();
}
}
diff --git a/fragment/src/main/java/android/support/v4/app/FragmentManager.java b/fragment/src/main/java/android/support/v4/app/FragmentManager.java
index 141d402..0ab1497 100644
--- a/fragment/src/main/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/src/main/java/android/support/v4/app/FragmentManager.java
@@ -677,6 +677,7 @@
boolean mNeedMenuInvalidate;
boolean mStateSaved;
+ boolean mStopped;
boolean mDestroyed;
String mNoTransactionsBecause;
boolean mHavePendingDeferredStart;
@@ -1081,6 +1082,7 @@
}
writer.print(prefix); writer.print(" mCurState="); writer.print(mCurState);
writer.print(" mStateSaved="); writer.print(mStateSaved);
+ writer.print(" mStopped="); writer.print(mStopped);
writer.print(" mDestroyed="); writer.println(mDestroyed);
if (mNeedMenuInvalidate) {
writer.print(prefix); writer.print(" mNeedMenuInvalidate=");
@@ -2042,7 +2044,7 @@
}
private void checkStateLoss() {
- if (mStateSaved) {
+ if (isStateSaved()) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
@@ -2054,7 +2056,10 @@
@Override
public boolean isStateSaved() {
- return mStateSaved;
+ // See saveAllState() for the explanation of this. We do this for
+ // all platform versions, to keep our behavior more consistent between
+ // them.
+ return mStateSaved || mStopped;
}
/**
@@ -3168,6 +3173,7 @@
public void noteStateNotSaved() {
mSavedNonConfig = null;
mStateSaved = false;
+ mStopped = false;
final int addedCount = mAdded.size();
for (int i = 0; i < addedCount; i++) {
Fragment fragment = mAdded.get(i);
@@ -3179,21 +3185,25 @@
public void dispatchCreate() {
mStateSaved = false;
+ mStopped = false;
dispatchStateChange(Fragment.CREATED);
}
public void dispatchActivityCreated() {
mStateSaved = false;
+ mStopped = false;
dispatchStateChange(Fragment.ACTIVITY_CREATED);
}
public void dispatchStart() {
mStateSaved = false;
+ mStopped = false;
dispatchStateChange(Fragment.STARTED);
}
public void dispatchResume() {
mStateSaved = false;
+ mStopped = false;
dispatchStateChange(Fragment.RESUMED);
}
@@ -3202,11 +3212,7 @@
}
public void dispatchStop() {
- // See saveAllState() for the explanation of this. We do this for
- // all platform versions, to keep our behavior more consistent between
- // them.
- mStateSaved = true;
-
+ mStopped = true;
dispatchStateChange(Fragment.STOPPED);
}
@@ -4038,7 +4044,7 @@
boolean more = super.getTransformation(currentTime, t);
if (!more) {
mEnded = true;
- mParent.post(this);
+ OneShotPreDrawListener.add(mParent, this);
}
return true;
}
@@ -4052,7 +4058,7 @@
boolean more = super.getTransformation(currentTime, outTransformation, scale);
if (!more) {
mEnded = true;
- mParent.post(this);
+ OneShotPreDrawListener.add(mParent, this);
}
return true;
}
diff --git a/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java b/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java
index 17687ff..3021005 100644
--- a/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java
+++ b/fragment/src/main/java/android/support/v4/app/LoaderManagerImpl.java
@@ -110,7 +110,8 @@
// Removing and re-adding the observer ensures that the
// observer is called again, even if they had already
// received the current data
- removeObserver(observer);
+ // Use super.removeObserver to avoid nulling out mLifecycleOwner & mObserver
+ super.removeObserver(observer);
observe(lifecycleOwner, observer);
}
}