summaryrefslogtreecommitdiff
path: root/android/support
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-11-17 16:38:15 -0500
committerJustin Klaassen <justinklaassen@google.com>2017-11-17 16:38:15 -0500
commit6a65f2da209bff03cb0eb6da309710ac6ee5026d (patch)
tree48e2090e716d4178378cb0599fc5d9cffbcf3f63 /android/support
parent46c77c203439b3b37c99d09e326df4b1fe08c10b (diff)
downloadandroid-28-6a65f2da209bff03cb0eb6da309710ac6ee5026d.tar.gz
Import Android SDK Platform P [4456821]
/google/data/ro/projects/android/fetch_artifact \ --bid 4456821 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4456821.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: I2d206b200d7952f899a5d1647ab532638cc8dd43
Diffstat (limited to 'android/support')
-rw-r--r--android/support/LibraryGroups.java30
-rw-r--r--android/support/LibraryVersions.java2
-rw-r--r--android/support/SourceJarTaskHelper.java60
-rw-r--r--android/support/car/drawer/CarDrawerActivity.java2
-rw-r--r--android/support/car/drawer/CarDrawerController.java55
-rw-r--r--android/support/car/utils/ColumnCalculator.java2
-rw-r--r--android/support/car/widget/CarItemAnimator.java4
-rw-r--r--android/support/car/widget/CarRecyclerView.java10
-rw-r--r--android/support/car/widget/PagedLayoutManager.java (renamed from android/support/car/widget/CarLayoutManager.java)65
-rw-r--r--android/support/car/widget/PagedListView.java267
-rw-r--r--android/support/car/widget/PagedScrollBarView.java13
-rw-r--r--android/support/mediacompat/testlib/IntentConstants.java34
-rw-r--r--android/support/mediacompat/testlib/MediaSessionConstants.java1
-rw-r--r--android/support/mediacompat/testlib/VersionConstants.java25
-rw-r--r--android/support/mediacompat/testlib/util/IntentUtil.java131
-rw-r--r--android/support/mediacompat/testlib/util/PollingCheck.java98
-rw-r--r--android/support/mediacompat/testlib/util/TestUtil.java41
-rw-r--r--android/support/text/emoji/widget/EmojiAppCompatEditText.java8
-rw-r--r--android/support/text/emoji/widget/EmojiEditText.java8
-rw-r--r--android/support/text/emoji/widget/EmojiExtractEditText.java8
-rw-r--r--android/support/transition/Transition.java4
-rw-r--r--android/support/v17/leanback/app/BaseFragment.java2
-rw-r--r--android/support/v17/leanback/app/BaseRowFragment.java14
-rw-r--r--android/support/v17/leanback/app/BaseRowSupportFragment.java12
-rw-r--r--android/support/v17/leanback/app/BrandedFragment.java2
-rw-r--r--android/support/v17/leanback/app/BrowseFragment.java153
-rw-r--r--android/support/v17/leanback/app/BrowseSupportFragment.java133
-rw-r--r--android/support/v17/leanback/app/DetailsFragment.java2
-rw-r--r--android/support/v17/leanback/app/DetailsFragmentBackgroundController.java2
-rw-r--r--android/support/v17/leanback/app/ErrorFragment.java2
-rw-r--r--android/support/v17/leanback/app/GuidedStepFragment.java17
-rw-r--r--android/support/v17/leanback/app/GuidedStepSupportFragment.java15
-rw-r--r--android/support/v17/leanback/app/HeadersFragment.java6
-rw-r--r--android/support/v17/leanback/app/ListRowDataAdapter.java16
-rw-r--r--android/support/v17/leanback/app/OnboardingFragment.java2
-rw-r--r--android/support/v17/leanback/app/PlaybackFragment.java4
-rw-r--r--android/support/v17/leanback/app/PlaybackFragmentGlueHost.java2
-rw-r--r--android/support/v17/leanback/app/RowsFragment.java4
-rw-r--r--android/support/v17/leanback/app/SearchFragment.java2
-rw-r--r--android/support/v17/leanback/app/VerticalGridFragment.java2
-rw-r--r--android/support/v17/leanback/app/VideoFragment.java2
-rw-r--r--android/support/v17/leanback/app/VideoFragmentGlueHost.java2
-rw-r--r--android/support/v17/leanback/widget/ArrayObjectAdapter.java60
-rw-r--r--android/support/v17/leanback/widget/BaseGridView.java4
-rw-r--r--android/support/v17/leanback/widget/GridLayoutManager.java422
-rw-r--r--android/support/v17/leanback/widget/GuidedActionAdapter.java71
-rw-r--r--android/support/v17/leanback/widget/GuidedActionDiffCallback.java65
-rw-r--r--android/support/v17/leanback/widget/ObjectAdapter.java15
-rw-r--r--android/support/v4/app/FragmentActivity.java22
-rw-r--r--android/support/v4/graphics/TypefaceCompat.java5
-rw-r--r--android/support/v4/graphics/TypefaceCompatApi26Impl.java156
-rw-r--r--android/support/v4/graphics/TypefaceCompatApi28Impl.java68
-rw-r--r--android/support/v4/media/MediaBrowserCompat.java30
-rw-r--r--android/support/v4/media/MediaBrowserServiceCompat.java12
-rw-r--r--android/support/v4/media/MediaMetadataCompat.java2
-rw-r--r--android/support/v4/view/ViewCompat.java4
-rw-r--r--android/support/v7/app/AppCompatDelegateImplV9.java21
-rw-r--r--android/support/v7/app/AppCompatViewInflater.java138
-rw-r--r--android/support/v7/util/SortedList.java44
-rw-r--r--android/support/v7/util/SortedListBatchedCallbackTest.java10
-rw-r--r--android/support/v7/util/SortedListTest.java124
-rw-r--r--android/support/v7/widget/AppCompatTextHelper.java6
-rw-r--r--android/support/v7/widget/TooltipCompatHandler.java39
-rw-r--r--android/support/v7/widget/util/SortedListAdapterCallback.java5
-rw-r--r--android/support/wear/ambient/AmbientDelegate.java42
-rw-r--r--android/support/wear/ambient/AmbientMode.java15
-rw-r--r--android/support/wear/ambient/SharedLibraryVersion.java4
-rw-r--r--android/support/wear/ambient/WearableControllerProvider.java2
-rw-r--r--android/support/wear/internal/widget/ResourcesUtil.java2
-rw-r--r--android/support/wear/internal/widget/drawer/MultiPagePresenter.java2
-rw-r--r--android/support/wear/internal/widget/drawer/MultiPageUi.java27
-rw-r--r--android/support/wear/internal/widget/drawer/SinglePagePresenter.java2
-rw-r--r--android/support/wear/internal/widget/drawer/SinglePageUi.java7
-rw-r--r--android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java2
-rw-r--r--android/support/wear/utils/MetadataConstants.java3
-rw-r--r--android/support/wear/widget/BezierSCurveInterpolator.java5
-rw-r--r--android/support/wear/widget/BoxInsetLayout.java30
-rw-r--r--android/support/wear/widget/CircledImageView.java15
-rw-r--r--android/support/wear/widget/CurvingLayoutCallback.java2
-rw-r--r--android/support/wear/widget/ProgressDrawable.java5
-rw-r--r--android/support/wear/widget/RoundedDrawable.java3
-rw-r--r--android/support/wear/widget/ScrollManager.java8
-rw-r--r--android/support/wear/widget/SimpleAnimatorListener.java2
-rw-r--r--android/support/wear/widget/SwipeDismissLayout.java5
-rw-r--r--android/support/wear/widget/WearableRecyclerView.java3
-rw-r--r--android/support/wear/widget/drawer/AbsListViewFlingWatcher.java2
-rw-r--r--android/support/wear/widget/drawer/FlingWatcherFactory.java2
-rw-r--r--android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java2
-rw-r--r--android/support/wear/widget/drawer/PageIndicatorView.java2
-rw-r--r--android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java2
-rw-r--r--android/support/wear/widget/drawer/ScrollViewFlingWatcher.java2
-rw-r--r--android/support/wear/widget/drawer/WearableActionDrawerMenu.java3
-rw-r--r--android/support/wear/widget/drawer/WearableActionDrawerView.java23
-rw-r--r--android/support/wear/widget/drawer/WearableDrawerLayout.java43
-rw-r--r--android/support/wear/widget/drawer/WearableDrawerView.java9
-rw-r--r--android/support/wear/widget/drawer/WearableNavigationDrawerView.java7
-rw-r--r--android/support/wearable/watchface/decomposition/package-info.java2
97 files changed, 2110 insertions, 762 deletions
diff --git a/android/support/LibraryGroups.java b/android/support/LibraryGroups.java
new file mode 100644
index 00000000..feaefbc6
--- /dev/null
+++ b/android/support/LibraryGroups.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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;
+
+/**
+ * The list of maven group names of all the libraries in this project.
+ */
+public class LibraryGroups {
+ public static final String SUPPORT = "com.android.support";
+ public static final String ROOM = "android.arch.persistence.room";
+ public static final String PERSISTENCE = "android.arch.persistence";
+ public static final String LIFECYCLE = "android.arch.lifecycle";
+ public static final String ARCH_CORE = "android.arch.core";
+ public static final String PAGING = "android.arch.paging";
+ public static final String NAVIGATION = "android.arch.navigation";
+}
diff --git a/android/support/LibraryVersions.java b/android/support/LibraryVersions.java
index 2f5730a2..efa0cbae 100644
--- a/android/support/LibraryVersions.java
+++ b/android/support/LibraryVersions.java
@@ -28,7 +28,7 @@ public class LibraryVersions {
/**
* Version code for flatfoot 1.0 projects (room, lifecycles)
*/
- private static final Version FLATFOOT_1_0_BATCH = new Version("1.0.0-rc1");
+ private static final Version FLATFOOT_1_0_BATCH = new Version("1.0.0");
/**
* Version code for Room
diff --git a/android/support/SourceJarTaskHelper.java b/android/support/SourceJarTaskHelper.java
new file mode 100644
index 00000000..9fbd1dba
--- /dev/null
+++ b/android/support/SourceJarTaskHelper.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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;
+
+import com.android.build.gradle.LibraryExtension;
+import com.android.builder.core.BuilderConstants;
+
+import org.gradle.api.Project;
+import org.gradle.api.plugins.JavaPluginConvention;
+import org.gradle.api.tasks.bundling.Jar;
+
+/**
+ * Helper class to handle creation of source jars.
+ */
+public class SourceJarTaskHelper {
+ /**
+ * Sets up a source jar task for an Android library project.
+ */
+ public static void setUpAndroidProject(Project project, LibraryExtension extension) {
+ // Create sources jar for release builds
+ extension.getLibraryVariants().all(libraryVariant -> {
+ if (!libraryVariant.getBuildType().getName().equals(BuilderConstants.RELEASE)) {
+ return; // Skip non-release builds.
+ }
+
+ Jar sourceJar = project.getTasks().create("sourceJarRelease", Jar.class);
+ sourceJar.setPreserveFileTimestamps(false);
+ sourceJar.setClassifier("sources");
+ sourceJar.from(extension.getSourceSets().findByName("main").getJava().getSrcDirs());
+ project.getArtifacts().add("archives", sourceJar);
+ });
+ }
+
+ /**
+ * Sets up a source jar task for a Java library project.
+ */
+ public static void setUpJavaProject(Project project) {
+ Jar sourceJar = project.getTasks().create("sourceJar", Jar.class);
+ sourceJar.setPreserveFileTimestamps(false);
+ sourceJar.setClassifier("sources");
+ JavaPluginConvention convention =
+ project.getConvention().getPlugin(JavaPluginConvention.class);
+ sourceJar.from(convention.getSourceSets().findByName("main").getAllSource().getSrcDirs());
+ project.getArtifacts().add("archives", sourceJar);
+ }
+}
diff --git a/android/support/car/drawer/CarDrawerActivity.java b/android/support/car/drawer/CarDrawerActivity.java
index 7100218a..f46c652b 100644
--- a/android/support/car/drawer/CarDrawerActivity.java
+++ b/android/support/car/drawer/CarDrawerActivity.java
@@ -46,7 +46,7 @@ import android.view.ViewGroup;
*
* <p>The rootAdapter can implement nested-navigation, in its click-handling, by passing the
* CarDrawerAdapter for the next level to
- * {@link CarDrawerController#switchToAdapter(CarDrawerAdapter)}.
+ * {@link CarDrawerController#pushAdapter(CarDrawerAdapter)}.
*
* <p>Any Activity's based on this class need to set their theme to CarDrawerActivityTheme or a
* derivative.
diff --git a/android/support/car/drawer/CarDrawerController.java b/android/support/car/drawer/CarDrawerController.java
index 4d9f4e99..7b23714c 100644
--- a/android/support/car/drawer/CarDrawerController.java
+++ b/android/support/car/drawer/CarDrawerController.java
@@ -19,16 +19,19 @@ package android.support.car.drawer;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.support.annotation.AnimRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.car.R;
import android.support.car.widget.PagedListView;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Gravity;
import android.view.MenuItem;
import android.view.View;
+import android.view.animation.AnimationUtils;
import android.widget.ProgressBar;
import java.util.Stack;
@@ -39,13 +42,21 @@ import java.util.Stack;
* navigation.
*/
public class CarDrawerController {
+ /** An animation for when a user navigates into a submenu. */
+ @AnimRes
+ private static final int DRILL_DOWN_ANIM = R.anim.fade_in_trans_right_layout_anim;
+
+ /** An animation for when a user navigates up (when the back button is pressed). */
+ @AnimRes
+ private static final int NAVIGATE_UP_ANIM = R.anim.fade_in_trans_left_layout_anim;
+
/** The amount that the drawer has been opened before its color should be switched. */
private static final float COLOR_SWITCH_SLIDE_OFFSET = 0.25f;
/**
* A representation of the hierarchy of navigation being displayed in the list. The ordering of
* this stack is the order that the user has visited each level. When the user navigates up,
- * the adapters are poopped from this list.
+ * the adapters are popped from this list.
*/
private final Stack<CarDrawerAdapter> mAdapterStack = new Stack<>();
@@ -78,16 +89,14 @@ public class CarDrawerController {
ActionBarDrawerToggle drawerToggle) {
mToolbar = toolbar;
mContext = drawerLayout.getContext();
-
+ mDrawerToggle = drawerToggle;
mDrawerLayout = drawerLayout;
mDrawerContent = drawerLayout.findViewById(R.id.drawer_content);
mDrawerList = drawerLayout.findViewById(R.id.drawer_list);
mDrawerList.setMaxPages(PagedListView.ItemCap.UNLIMITED);
-
mProgressBar = drawerLayout.findViewById(R.id.drawer_progress);
- mDrawerToggle = drawerToggle;
setupDrawerToggling();
}
@@ -104,7 +113,13 @@ public class CarDrawerController {
return;
}
- mAdapterStack.push(rootAdapter);
+ // The root adapter is always the last item in the stack.
+ if (mAdapterStack.size() > 0) {
+ mAdapterStack.set(0, rootAdapter);
+ } else {
+ mAdapterStack.push(rootAdapter);
+ }
+
setToolbarTitleFrom(rootAdapter);
mDrawerList.setAdapter(rootAdapter);
}
@@ -120,10 +135,11 @@ public class CarDrawerController {
*
* @param adapter Adapter for next level of content in the drawer.
*/
- public final void switchToAdapter(CarDrawerAdapter adapter) {
+ public final void pushAdapter(CarDrawerAdapter adapter) {
mAdapterStack.peek().setTitleChangeListener(null);
mAdapterStack.push(adapter);
- switchToAdapterInternal(adapter);
+ setDisplayAdapter(adapter);
+ runLayoutAnimation(DRILL_DOWN_ANIM);
}
/** Close the drawer. */
@@ -264,15 +280,15 @@ public class CarDrawerController {
}
/**
- * Sets the navigation drawer's title to be the one supplied by the given adapter and updates
- * the navigation drawer list with the adapter's contents.
+ * Sets the given adapter as the one displaying the current contents of the drawer.
+ *
+ * <p>The drawer's title will also be derived from the given adapter.
*/
- private void switchToAdapterInternal(CarDrawerAdapter adapter) {
+ private void setDisplayAdapter(CarDrawerAdapter adapter) {
setToolbarTitleFrom(adapter);
// NOTE: We don't use swapAdapter() since different levels in the Drawer may switch between
// car_drawer_list_item_normal, car_drawer_list_item_small and car_list_empty layouts.
mDrawerList.getRecyclerView().setAdapter(adapter);
- scrollToPosition(0);
}
/**
@@ -290,7 +306,8 @@ public class CarDrawerController {
CarDrawerAdapter adapter = mAdapterStack.pop();
adapter.setTitleChangeListener(null);
adapter.cleanup();
- switchToAdapterInternal(mAdapterStack.peek());
+ setDisplayAdapter(mAdapterStack.peek());
+ runLayoutAnimation(NAVIGATE_UP_ANIM);
return true;
}
@@ -301,6 +318,18 @@ public class CarDrawerController {
adapter.setTitleChangeListener(null);
adapter.cleanup();
}
- switchToAdapterInternal(mAdapterStack.peek());
+ setDisplayAdapter(mAdapterStack.peek());
+ runLayoutAnimation(NAVIGATE_UP_ANIM);
+ }
+
+ /**
+ * Runs the given layout animation on the PagedListView. Running this animation will also
+ * refresh the contents of the list.
+ */
+ private void runLayoutAnimation(@AnimRes int animation) {
+ RecyclerView recyclerView = mDrawerList.getRecyclerView();
+ recyclerView.setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, animation));
+ recyclerView.getAdapter().notifyDataSetChanged();
+ recyclerView.scheduleLayoutAnimation();
}
}
diff --git a/android/support/car/utils/ColumnCalculator.java b/android/support/car/utils/ColumnCalculator.java
index 96e081b9..fa5dd432 100644
--- a/android/support/car/utils/ColumnCalculator.java
+++ b/android/support/car/utils/ColumnCalculator.java
@@ -65,7 +65,7 @@ public class ColumnCalculator {
private ColumnCalculator(Context context) {
Resources res = context.getResources();
- int marginSize = res.getDimensionPixelSize(R.dimen.car_screen_margin_size);
+ int marginSize = res.getDimensionPixelSize(R.dimen.car_margin);
mGutterSize = res.getDimensionPixelSize(R.dimen.car_screen_gutter_size);
mNumOfColumns = res.getInteger(R.integer.car_screen_num_of_columns);
diff --git a/android/support/car/widget/CarItemAnimator.java b/android/support/car/widget/CarItemAnimator.java
index 4dd32127..ef22c484 100644
--- a/android/support/car/widget/CarItemAnimator.java
+++ b/android/support/car/widget/CarItemAnimator.java
@@ -22,9 +22,9 @@ import android.support.v7.widget.RecyclerView;
/** {@link DefaultItemAnimator} with a few minor changes where it had undesired behavior. */
public class CarItemAnimator extends DefaultItemAnimator {
- private final CarLayoutManager mLayoutManager;
+ private final PagedLayoutManager mLayoutManager;
- public CarItemAnimator(CarLayoutManager layoutManager) {
+ public CarItemAnimator(PagedLayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
diff --git a/android/support/car/widget/CarRecyclerView.java b/android/support/car/widget/CarRecyclerView.java
index 2684c58a..bb9cb71a 100644
--- a/android/support/car/widget/CarRecyclerView.java
+++ b/android/support/car/widget/CarRecyclerView.java
@@ -26,7 +26,7 @@ import android.view.View;
import android.view.ViewGroup;
/**
- * Custom {@link RecyclerView} that helps {@link CarLayoutManager} properly fling and paginate.
+ * Custom {@link RecyclerView} that helps {@link PagedLayoutManager} properly fling and paginate.
*
* <p>It also has the ability to fade children as they scroll off screen that can be set with {@link
* #setFadeLastItem(boolean)}.
@@ -57,7 +57,7 @@ public class CarRecyclerView extends RecyclerView {
@Override
public boolean fling(int velocityX, int velocityY) {
mWasFlingCalledForGesture = true;
- return ((CarLayoutManager) getLayoutManager()).settleScrollForFling(this, velocityY);
+ return ((PagedLayoutManager) getLayoutManager()).settleScrollForFling(this, velocityY);
}
@Override
@@ -69,7 +69,7 @@ public class CarRecyclerView extends RecyclerView {
int action = e.getActionMasked();
if (action == MotionEvent.ACTION_UP) {
if (!mWasFlingCalledForGesture) {
- ((CarLayoutManager) getLayoutManager()).settleScrollForFling(this, 0);
+ ((PagedLayoutManager) getLayoutManager()).settleScrollForFling(this, 0);
}
mWasFlingCalledForGesture = false;
}
@@ -102,7 +102,7 @@ public class CarRecyclerView extends RecyclerView {
* number of items that fit completely on the screen.
*/
public void pageUp() {
- CarLayoutManager lm = (CarLayoutManager) getLayoutManager();
+ PagedLayoutManager lm = (PagedLayoutManager) getLayoutManager();
int pageUpPosition = lm.getPageUpPosition();
if (pageUpPosition == -1) {
return;
@@ -116,7 +116,7 @@ public class CarRecyclerView extends RecyclerView {
* number of items that fit completely on the screen.
*/
public void pageDown() {
- CarLayoutManager lm = (CarLayoutManager) getLayoutManager();
+ PagedLayoutManager lm = (PagedLayoutManager) getLayoutManager();
int pageDownPosition = lm.getPageDownPosition();
if (pageDownPosition == -1) {
return;
diff --git a/android/support/car/widget/CarLayoutManager.java b/android/support/car/widget/PagedLayoutManager.java
index d0d3a9e1..c4f469a3 100644
--- a/android/support/car/widget/CarLayoutManager.java
+++ b/android/support/car/widget/PagedLayoutManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 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.
@@ -18,6 +18,8 @@ package android.support.car.widget;
import android.content.Context;
import android.graphics.PointF;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
@@ -46,9 +48,9 @@ import java.util.ArrayList;
*
* <ol>
* <li>In a normal ListView, when views reach the top of the list, they are clipped. In
- * CarLayoutManager, views have the option of flying off of the top of the screen as the next
- * row settles in to place. This functionality can be enabled or disabled with {@link
- * #setOffsetRows(boolean)}.
+ * PagedLayoutManager, views have the option of flying off of the top of the screen as the
+ * next row settles in to place. This functionality can be enabled or disabled with
+ * {@link #setOffsetRows(boolean)}.
* <li>Standard list physics is disabled. Instead, when the user scrolls, it will settle on the
* next page.
* <li>Items can scroll past the bottom edge of the screen. This helps with pagination so that the
@@ -57,8 +59,8 @@ import java.util.ArrayList;
*
* This LayoutManger should be used with {@link CarRecyclerView}.
*/
-public class CarLayoutManager extends RecyclerView.LayoutManager {
- private static final String TAG = "CarLayoutManager";
+public class PagedLayoutManager extends RecyclerView.LayoutManager {
+ private static final String TAG = "PagedLayoutManager";
/**
* Any fling below the threshold will just scroll to the top fully visible row. The units is
@@ -166,7 +168,7 @@ public class CarLayoutManager extends RecyclerView.LayoutManager {
/** Set the anchor to the following position on the next layout pass. */
private int mPendingScrollPosition = -1;
- public CarLayoutManager(Context context) {
+ public PagedLayoutManager(Context context) {
mContext = context;
}
@@ -919,6 +921,55 @@ public class CarLayoutManager extends RecyclerView.LayoutManager {
return mLowerPageBreakPosition;
}
+ @Override
+ public Parcelable onSaveInstanceState() {
+ SavedState savedState = new SavedState();
+ savedState.mFirstChildPosition = getFirstFullyVisibleChildPosition();
+ return savedState;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ if (state instanceof SavedState) {
+ scrollToPosition(((SavedState) state).mFirstChildPosition);
+ }
+ }
+
+ /** The state that will be saved across configuration changes. */
+ static class SavedState implements Parcelable {
+ /** The position of the first visible child view in the list. */
+ int mFirstChildPosition;
+
+ SavedState() {}
+
+ private SavedState(Parcel in) {
+ mFirstChildPosition = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mFirstChildPosition);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
/**
* Layout the anchor row. The anchor row is the first fully visible row.
*
diff --git a/android/support/car/widget/PagedListView.java b/android/support/car/widget/PagedListView.java
index 46527001..4695c45c 100644
--- a/android/support/car/widget/PagedListView.java
+++ b/android/support/car/widget/PagedListView.java
@@ -23,7 +23,11 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -33,6 +37,7 @@ import android.support.car.R;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -41,13 +46,19 @@ import android.widget.FrameLayout;
/**
* Custom {@link android.support.v7.widget.RecyclerView} that displays a list of items that
* resembles a {@link android.widget.ListView} but also has page up and page down arrows on the
- * right side.
+ * left side.
*/
public class PagedListView extends FrameLayout {
/** Default maximum number of clicks allowed on a list */
public static final int DEFAULT_MAX_CLICKS = 6;
/**
+ * Value to pass to {@link #setMaxPages(int)} to indicate there is no restriction on the
+ * maximum number of pages to show.
+ */
+ public static final int UNLIMITED_PAGES = -1;
+
+ /**
* The amount of time after settling to wait before autoscrolling to the next page when the user
* holds down a pagination button.
*/
@@ -57,7 +68,7 @@ public class PagedListView extends FrameLayout {
private static final int INVALID_RESOURCE_ID = -1;
protected final CarRecyclerView mRecyclerView;
- protected final CarLayoutManager mLayoutManager;
+ protected final PagedLayoutManager mLayoutManager;
protected final Handler mHandler = new Handler();
private final boolean mScrollBarEnabled;
private final PagedScrollBarView mScrollBarView;
@@ -65,8 +76,8 @@ public class PagedListView extends FrameLayout {
private int mRowsPerPage = -1;
protected RecyclerView.Adapter<? extends RecyclerView.ViewHolder> mAdapter;
- /** Maximum number of pages to show. Values < 0 show all pages. */
- private int mMaxPages = -1;
+ /** Maximum number of pages to show. */
+ private int mMaxPages;
protected OnScrollListener mOnScrollListener;
@@ -115,8 +126,6 @@ public class PagedListView extends FrameLayout {
* the item in position 20 instead, for position 1 it will show the item in position 21 instead
* and so on.
*/
- // TODO(b/28003781): ItemPositionOffset and ItemCap interfaces should be merged once
- // we enable AlphaJump outside drawer.
public interface ItemPositionOffset {
/** Sets the position offset for the adapter. */
void setPositionOffset(int positionOffset);
@@ -151,7 +160,7 @@ public class PagedListView extends FrameLayout {
mMaxPages = getDefaultMaxPages();
- mLayoutManager = new CarLayoutManager(context);
+ mLayoutManager = new PagedLayoutManager(context);
mLayoutManager.setOffsetRows(offsetRows);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setOnScrollListener(mRecyclerViewOnScrollListener);
@@ -162,7 +171,7 @@ public class PagedListView extends FrameLayout {
if (offsetScrollBar) {
MarginLayoutParams params = (MarginLayoutParams) mRecyclerView.getLayoutParams();
params.setMarginStart(getResources().getDimensionPixelSize(
- R.dimen.car_screen_margin_size));
+ R.dimen.car_margin));
params.setMarginEnd(
a.getDimensionPixelSize(R.styleable.PagedListView_listEndMargin, 0));
mRecyclerView.setLayoutParams(params);
@@ -180,6 +189,11 @@ public class PagedListView extends FrameLayout {
dividerStartId, dividerEndId));
}
+ int itemSpacing = a.getDimensionPixelSize(R.styleable.PagedListView_itemSpacing, 0);
+ if (itemSpacing > 0) {
+ mRecyclerView.addItemDecoration(new ItemSpacingDecoration(itemSpacing));
+ }
+
// Set this to true so that this view consumes clicks events and views underneath
// don't receive this click event. Without this it's possible to click places in the
// view that don't capture the event, and as a result, elements visually hidden consume
@@ -212,6 +226,16 @@ public class PagedListView extends FrameLayout {
}
});
+ Drawable upButtonIcon = a.getDrawable(R.styleable.PagedListView_upButtonIcon);
+ if (upButtonIcon != null) {
+ setUpButtonIcon(upButtonIcon);
+ }
+
+ Drawable downButtonIcon = a.getDrawable(R.styleable.PagedListView_downButtonIcon);
+ if (downButtonIcon != null) {
+ setDownButtonIcon(downButtonIcon);
+ }
+
mScrollBarView.setVisibility(mScrollBarEnabled ? VISIBLE : GONE);
// Modify the layout the Scroll Bar is not visible.
@@ -236,7 +260,7 @@ public class PagedListView extends FrameLayout {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
// The user has interacted with the list using touch. All movements will now paginate
// the list.
- mLayoutManager.setRowOffsetMode(CarLayoutManager.ROW_OFFSET_MODE_PAGE);
+ mLayoutManager.setRowOffsetMode(PagedLayoutManager.ROW_OFFSET_MODE_PAGE);
}
return super.onInterceptTouchEvent(e);
}
@@ -246,7 +270,7 @@ public class PagedListView extends FrameLayout {
super.requestChildFocus(child, focused);
// The user has interacted with the list using the controller. Movements through the list
// will now be one row at a time.
- mLayoutManager.setRowOffsetMode(CarLayoutManager.ROW_OFFSET_MODE_INDIVIDUAL);
+ mLayoutManager.setRowOffsetMode(PagedLayoutManager.ROW_OFFSET_MODE_INDIVIDUAL);
}
/**
@@ -312,19 +336,25 @@ public class PagedListView extends FrameLayout {
mHandler.post(mUpdatePaginationRunnable);
}
+ /** Sets the icon to be used for the up button. */
+ public void setUpButtonIcon(Drawable icon) {
+ mScrollBarView.setUpButtonIcon(icon);
+ }
+
+ /** Sets the icon to be used for the down button. */
+ public void setDownButtonIcon(Drawable icon) {
+ mScrollBarView.setDownButtonIcon(icon);
+ }
+
/**
* Sets the adapter for the list.
*
- * <p>It <em>must</em> implement {@link ItemCap}, otherwise, will throw an {@link
- * IllegalArgumentException}.
+ * <p>The given Adapter can implement {@link ItemCap} if it wishes to control the behavior of
+ * a max number of items. Otherwise, methods in the PagedListView to limit the content, such as
+ * {@link #setMaxPages(int)}, will do nothing.
*/
public void setAdapter(
@NonNull RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
- if (!(adapter instanceof ItemCap)) {
- throw new IllegalArgumentException("ERROR: adapter ["
- + adapter.getClass().getCanonicalName() + "] MUST implement ItemCap");
- }
-
mAdapter = adapter;
mRecyclerView.setAdapter(adapter);
updateMaxItems();
@@ -333,7 +363,7 @@ public class PagedListView extends FrameLayout {
/** @hide */
@RestrictTo(LIBRARY_GROUP)
@NonNull
- public CarLayoutManager getLayoutManager() {
+ public PagedLayoutManager getLayoutManager() {
return mLayoutManager;
}
@@ -345,15 +375,19 @@ public class PagedListView extends FrameLayout {
/**
* Sets the maximum number of the pages that can be shown in the PagedListView. The size of a
- * page is defined as the number of items that fit completely on the screen at once.
+ * page is defined as the number of items that fit completely on the screen at once.
+ *
+ * <p>Passing {@link #UNLIMITED_PAGES} will remove any restrictions on a maximum number
+ * of pages.
+ *
+ * <p>Note that for any restriction on maximum pages to work, the adapter passed to this
+ * PagedListView needs to implement {@link ItemCap}.
*
- * @param maxPages The maximum number of pages that fit on the screen. Should be positive.
+ * @param maxPages The maximum number of pages that fit on the screen. Should be positive or
+ * {@link #UNLIMITED_PAGES}.
*/
public void setMaxPages(int maxPages) {
- if (maxPages < 0) {
- return;
- }
- mMaxPages = maxPages;
+ mMaxPages = Math.max(UNLIMITED_PAGES, maxPages);
updateMaxItems();
}
@@ -362,7 +396,8 @@ public class PagedListView extends FrameLayout {
* {@link #setMaxPages(int)}. If that method has not been called, then this value should match
* the default value.
*
- * @return The maximum number of pages to be shown.
+ * @return The maximum number of pages to be shown or {@link #UNLIMITED_PAGES} if there is
+ * no limit.
*/
public int getMaxPages() {
return mMaxPages;
@@ -370,7 +405,7 @@ public class PagedListView extends FrameLayout {
/**
* Gets the number of rows per page. Default value of mRowsPerPage is -1. If the first child of
- * CarLayoutManager is null or the height of the first child is 0, it will return 1.
+ * PagedLayoutManager is null or the height of the first child is 0, it will return 1.
*/
public int getRowsPerPage() {
return mRowsPerPage;
@@ -422,6 +457,32 @@ public class PagedListView extends FrameLayout {
}
/**
+ * Sets spacing between each item in the list. The spacing will not be added before the first
+ * item and after the last.
+ *
+ * @param itemSpacing the spacing between each item.
+ */
+ public void setItemSpacing(int itemSpacing) {
+ ItemSpacingDecoration existing = null;
+ for (int i = 0, count = mRecyclerView.getItemDecorationCount(); i < count; i++) {
+ RecyclerView.ItemDecoration itemDecoration = mRecyclerView.getItemDecorationAt(i);
+ if (itemDecoration instanceof ItemSpacingDecoration) {
+ existing = (ItemSpacingDecoration) itemDecoration;
+ break;
+ }
+ }
+
+ if (itemSpacing == 0 && existing != null) {
+ mRecyclerView.removeItemDecoration(existing);
+ } else if (existing == null) {
+ mRecyclerView.addItemDecoration(new ItemSpacingDecoration(itemSpacing));
+ } else {
+ existing.setItemSpacing(itemSpacing);
+ }
+ mRecyclerView.invalidateItemDecorations();
+ }
+
+ /**
* Adds an {@link android.support.v7.widget.RecyclerView.OnItemTouchListener} to this
* PagedListView.
*
@@ -520,6 +581,7 @@ public class PagedListView extends FrameLayout {
return;
}
mDefaultMaxPages = newDefault;
+ resetMaxPages();
}
/** Returns the default number of pages the list should have */
@@ -646,8 +708,15 @@ public class PagedListView extends FrameLayout {
return;
}
- final int originalCount = mAdapter.getItemCount();
+ // Ensure mRowsPerPage regardless of if the adapter implements ItemCap.
updateRowsPerPage();
+
+ // If the adapter does not implement ItemCap, then the max items on it cannot be updated.
+ if (!(mAdapter instanceof ItemCap)) {
+ return;
+ }
+
+ final int originalCount = mAdapter.getItemCount();
((ItemCap) mAdapter).setMaxItems(calculateMaxItemCount());
final int newCount = mAdapter.getItemCount();
if (newCount == originalCount) {
@@ -683,6 +752,78 @@ public class PagedListView extends FrameLayout {
}
}
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ SavedState savedState = new SavedState(super.onSaveInstanceState());
+ savedState.mLayoutManagerState = mLayoutManager.onSaveInstanceState();
+ return savedState;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState savedState = (SavedState) state;
+ mLayoutManager.onRestoreInstanceState(savedState.mLayoutManagerState);
+ super.onRestoreInstanceState(savedState.getSuperState());
+ }
+
+ @Override
+ protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+ // There is the possibility of multiple PagedListViews on a page. This means that the ids
+ // of the child Views of PagedListView are no longer unique, and onSaveInstanceState()
+ // cannot be used. As a result, PagedListViews needs to manually dispatch the instance
+ // states. Call dispatchFreezeSelfOnly() so that no child views have onSaveInstanceState()
+ // called by the system.
+ dispatchFreezeSelfOnly(container);
+ }
+
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+ // Prevent onRestoreInstanceState() from being called on child Views. Instead, PagedListView
+ // will manually handle passing the state. See the comment in dispatchSaveInstanceState()
+ // for more information.
+ dispatchThawSelfOnly(container);
+ }
+
+ /** The state that will be saved across configuration changes. */
+ private static class SavedState extends BaseSavedState {
+ /** The state of the {@link #mLayoutManager} of this PagedListView. */
+ Parcelable mLayoutManagerState;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ mLayoutManagerState =
+ in.readParcelable(PagedLayoutManager.SavedState.class.getClassLoader());
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeParcelable(mLayoutManagerState, flags);
+ }
+
+ public static final ClassLoaderCreator<SavedState> CREATOR =
+ new ClassLoaderCreator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel source, ClassLoader loader) {
+ return new SavedState(source);
+ }
+
+ @Override
+ public SavedState createFromParcel(Parcel source) {
+ return createFromParcel(source, null /* loader */);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
private final RecyclerView.OnScrollListener mRecyclerViewOnScrollListener =
new RecyclerView.OnScrollListener() {
@Override
@@ -766,16 +907,50 @@ public class PagedListView extends FrameLayout {
}
/**
+ * A {@link android.support.v7.widget.RecyclerView.ItemDecoration} that will add spacing
+ * between each item in the RecyclerView that it is added to.
+ */
+ private static class ItemSpacingDecoration extends RecyclerView.ItemDecoration {
+
+ private int mHalfItemSpacing;
+
+ private ItemSpacingDecoration(int itemSpacing) {
+ mHalfItemSpacing = itemSpacing / 2;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ // Skip top offset for first item and bottom offset for last.
+ int position = parent.getChildAdapterPosition(view);
+ if (position > 0) {
+ outRect.top = mHalfItemSpacing;
+ }
+ if (position < state.getItemCount() - 1) {
+ outRect.bottom = mHalfItemSpacing;
+ }
+ }
+
+ /**
+ * @param itemSpacing sets spacing between each item.
+ */
+ public void setItemSpacing(int itemSpacing) {
+ mHalfItemSpacing = itemSpacing / 2;
+ }
+ }
+
+ /**
* A {@link android.support.v7.widget.RecyclerView.ItemDecoration} that will draw a dividing
* line between each item in the RecyclerView that it is added to.
*/
- public static class DividerDecoration extends RecyclerView.ItemDecoration {
+ private static class DividerDecoration extends RecyclerView.ItemDecoration {
private final Context mContext;
private final Paint mPaint;
private final int mDividerHeight;
private final int mDividerStartMargin;
@IdRes private final int mDividerStartId;
- @IdRes private final int mDvidierEndId;
+ @IdRes private final int mDividerEndId;
/**
* @param dividerStartMargin The start offset of the dividing line. This offset will be
@@ -792,7 +967,7 @@ public class PagedListView extends FrameLayout {
mContext = context;
mDividerStartMargin = dividerStartMargin;
mDividerStartId = dividerStartId;
- mDvidierEndId = dividerEndId;
+ mDividerEndId = dividerEndId;
Resources res = context.getResources();
mPaint = new Paint();
@@ -807,16 +982,20 @@ public class PagedListView extends FrameLayout {
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
- for (int i = 0, childCount = parent.getChildCount(); i < childCount; i++) {
+ // Draw a divider line between each item. No need to draw the line for the last item.
+ for (int i = 0, childCount = parent.getChildCount(); i < childCount - 1; i++) {
View container = parent.getChildAt(i);
+ View nextContainer = parent.getChildAt(i + 1);
+ int spacing = nextContainer.getTop() - container.getBottom();
+
View startChild =
mDividerStartId != INVALID_RESOURCE_ID
? container.findViewById(mDividerStartId)
: container;
View endChild =
- mDvidierEndId != INVALID_RESOURCE_ID
- ? container.findViewById(mDvidierEndId)
+ mDividerEndId != INVALID_RESOURCE_ID
+ ? container.findViewById(mDividerEndId)
: container;
if (startChild == null || endChild == null) {
@@ -825,14 +1004,24 @@ public class PagedListView extends FrameLayout {
int left = mDividerStartMargin + startChild.getLeft();
int right = endChild.getRight();
- int bottom = container.getBottom();
+ int bottom = container.getBottom() + spacing / 2 + mDividerHeight / 2;
int top = bottom - mDividerHeight;
- // Draw a divider line between each item. No need to draw the line for the last
- // item.
- if (i != childCount - 1) {
- c.drawRect(left, top, right, bottom, mPaint);
- }
+ c.drawRect(left, top, right, bottom, mPaint);
+ }
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ super.getItemOffsets(outRect, view, parent, state);
+ // Skip top offset for first item and bottom offset for last.
+ int position = parent.getChildAdapterPosition(view);
+ if (position > 0) {
+ outRect.top = mDividerHeight / 2;
+ }
+ if (position < state.getItemCount() - 1) {
+ outRect.bottom = mDividerHeight / 2;
}
}
}
diff --git a/android/support/car/widget/PagedScrollBarView.java b/android/support/car/widget/PagedScrollBarView.java
index 125b354c..1c46b5d4 100644
--- a/android/support/car/widget/PagedScrollBarView.java
+++ b/android/support/car/widget/PagedScrollBarView.java
@@ -18,6 +18,7 @@ package android.support.car.widget;
import android.content.Context;
import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
import android.support.car.R;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
@@ -98,6 +99,16 @@ public class PagedScrollBarView extends FrameLayout
return true;
}
+ /** Sets the icon to be used for the up button. */
+ public void setUpButtonIcon(Drawable icon) {
+ mUpButton.setImageDrawable(icon);
+ }
+
+ /** Sets the icon to be used for the down button. */
+ public void setDownButtonIcon(Drawable icon) {
+ mDownButton.setImageDrawable(icon);
+ }
+
/**
* Sets the listener that will be notified when the up and down buttons have been pressed.
*
@@ -119,7 +130,7 @@ public class PagedScrollBarView extends FrameLayout
/** Sets the range, offset and extent of the scroll bar. See {@link View}. */
public void setParameters(int range, int offset, int extent, boolean animate) {
- // This method is where we take the computed parameters from the CarLayoutManager and
+ // This method is where we take the computed parameters from the PagedLayoutManager and
// render it within the specified constraints ({@link #mMaxThumbLength} and
// {@link #mMinThumbLength}).
final int size = mFiller.getHeight() - mFiller.getPaddingTop() - mFiller.getPaddingBottom();
diff --git a/android/support/mediacompat/testlib/IntentConstants.java b/android/support/mediacompat/testlib/IntentConstants.java
deleted file mode 100644
index a18bcf32..00000000
--- a/android/support/mediacompat/testlib/IntentConstants.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 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.mediacompat.testlib;
-
-/**
- * Constants used for sending intent between client and service apps.
- */
-public class IntentConstants {
- public static final String ACTION_CALL_MEDIA_BROWSER_SERVICE_METHOD =
- "android.support.mediacompat.service.action.CALL_MEDIA_BROWSER_SERVICE_METHOD";
- public static final String ACTION_CALL_MEDIA_SESSION_METHOD =
- "android.support.mediacompat.service.action.CALL_MEDIA_SESSION_METHOD";
- public static final String ACTION_CALL_MEDIA_CONTROLLER_METHOD =
- "android.support.mediacompat.client.action.CALL_MEDIA_CONTROLLER_METHOD";
- public static final String ACTION_CALL_TRANSPORT_CONTROLS_METHOD =
- "android.support.mediacompat.client.action.CALL_TRANSPORT_CONTROLS_METHOD";
- public static final String KEY_METHOD_ID = "method_id";
- public static final String KEY_ARGUMENT = "argument";
- public static final String KEY_SESSION_TOKEN = "session_token";
-}
diff --git a/android/support/mediacompat/testlib/MediaSessionConstants.java b/android/support/mediacompat/testlib/MediaSessionConstants.java
index 95be1621..cbdccc1b 100644
--- a/android/support/mediacompat/testlib/MediaSessionConstants.java
+++ b/android/support/mediacompat/testlib/MediaSessionConstants.java
@@ -40,7 +40,6 @@ public class MediaSessionConstants {
public static final int SET_RATING_TYPE = 117;
public static final String TEST_SESSION_TAG = "test-session-tag";
- public static final String SERVICE_PACKAGE_NAME = "android.support.mediacompat.service.test";
public static final String TEST_KEY = "test-key";
public static final String TEST_VALUE = "test-val";
public static final String TEST_SESSION_EVENT = "test-session-event";
diff --git a/android/support/mediacompat/testlib/VersionConstants.java b/android/support/mediacompat/testlib/VersionConstants.java
new file mode 100644
index 00000000..6533ee17
--- /dev/null
+++ b/android/support/mediacompat/testlib/VersionConstants.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 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.mediacompat.testlib;
+
+/**
+ * Constants for getting support library version information.
+ */
+public class VersionConstants {
+ public static final String KEY_CLIENT_VERSION = "client_version";
+ public static final String KEY_SERVICE_VERSION = "service_version";
+}
diff --git a/android/support/mediacompat/testlib/util/IntentUtil.java b/android/support/mediacompat/testlib/util/IntentUtil.java
new file mode 100644
index 00000000..bbf97524
--- /dev/null
+++ b/android/support/mediacompat/testlib/util/IntentUtil.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 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.mediacompat.testlib.util;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+
+/**
+ * Methods and constants used for sending intent between client and service apps.
+ */
+public class IntentUtil {
+
+ public static final ComponentName SERVICE_RECEIVER_COMPONENT_NAME = new ComponentName(
+ "android.support.mediacompat.service.test",
+ "android.support.mediacompat.service.ServiceBroadcastReceiver");
+ public static final ComponentName CLIENT_RECEIVER_COMPONENT_NAME = new ComponentName(
+ "android.support.mediacompat.client.test",
+ "android.support.mediacompat.client.ClientBroadcastReceiver");
+
+ public static final String ACTION_CALL_MEDIA_BROWSER_SERVICE_METHOD =
+ "android.support.mediacompat.service.action.CALL_MEDIA_BROWSER_SERVICE_METHOD";
+ public static final String ACTION_CALL_MEDIA_SESSION_METHOD =
+ "android.support.mediacompat.service.action.CALL_MEDIA_SESSION_METHOD";
+ public static final String ACTION_CALL_MEDIA_CONTROLLER_METHOD =
+ "android.support.mediacompat.client.action.CALL_MEDIA_CONTROLLER_METHOD";
+ public static final String ACTION_CALL_TRANSPORT_CONTROLS_METHOD =
+ "android.support.mediacompat.client.action.CALL_TRANSPORT_CONTROLS_METHOD";
+
+ public static final String KEY_METHOD_ID = "method_id";
+ public static final String KEY_ARGUMENT = "argument";
+ public static final String KEY_SESSION_TOKEN = "session_token";
+
+ /**
+ * Calls a method of MediaBrowserService. Used by client app.
+ */
+ public static void callMediaBrowserServiceMethod(int methodId, Object arg, Context context) {
+ Intent intent = createIntent(SERVICE_RECEIVER_COMPONENT_NAME, methodId, arg);
+ intent.setAction(ACTION_CALL_MEDIA_BROWSER_SERVICE_METHOD);
+ if (Build.VERSION.SDK_INT >= 16) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ }
+ context.sendBroadcast(intent);
+ }
+
+ /**
+ * Calls a method of MediaSession. Used by client app.
+ */
+ public static void callMediaSessionMethod(int methodId, Object arg, Context context) {
+ Intent intent = createIntent(SERVICE_RECEIVER_COMPONENT_NAME, methodId, arg);
+ intent.setAction(ACTION_CALL_MEDIA_SESSION_METHOD);
+ if (Build.VERSION.SDK_INT >= 16) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ }
+ context.sendBroadcast(intent);
+ }
+
+ /**
+ * Calls a method of MediaController. Used by service app.
+ */
+ public static void callMediaControllerMethod(
+ int methodId, Object arg, Context context, Parcelable token) {
+ Intent intent = createIntent(CLIENT_RECEIVER_COMPONENT_NAME, methodId, arg);
+ intent.setAction(ACTION_CALL_MEDIA_CONTROLLER_METHOD);
+ intent.putExtra(KEY_SESSION_TOKEN, token);
+ if (Build.VERSION.SDK_INT >= 16) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ }
+ context.sendBroadcast(intent);
+ }
+
+ /**
+ * Calls a method of TransportControls. Used by service app.
+ */
+ public static void callTransportControlsMethod(
+ int methodId, Object arg, Context context, Parcelable token) {
+ Intent intent = createIntent(CLIENT_RECEIVER_COMPONENT_NAME, methodId, arg);
+ intent.setAction(ACTION_CALL_TRANSPORT_CONTROLS_METHOD);
+ intent.putExtra(KEY_SESSION_TOKEN, token);
+ if (Build.VERSION.SDK_INT >= 16) {
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ }
+ context.sendBroadcast(intent);
+ }
+
+ private static Intent createIntent(ComponentName componentName, int methodId, Object arg) {
+ Intent intent = new Intent();
+ intent.setComponent(componentName);
+ intent.putExtra(KEY_METHOD_ID, methodId);
+
+ if (arg instanceof String) {
+ intent.putExtra(KEY_ARGUMENT, (String) arg);
+ } else if (arg instanceof Integer) {
+ intent.putExtra(KEY_ARGUMENT, (int) arg);
+ } else if (arg instanceof Long) {
+ intent.putExtra(KEY_ARGUMENT, (long) arg);
+ } else if (arg instanceof Boolean) {
+ intent.putExtra(KEY_ARGUMENT, (boolean) arg);
+ } else if (arg instanceof Parcelable) {
+ intent.putExtra(KEY_ARGUMENT, (Parcelable) arg);
+ } else if (arg instanceof ArrayList<?>) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(KEY_ARGUMENT, (ArrayList<? extends Parcelable>) arg);
+ intent.putExtras(bundle);
+ } else if (arg instanceof Bundle) {
+ Bundle bundle = new Bundle();
+ bundle.putBundle(KEY_ARGUMENT, (Bundle) arg);
+ intent.putExtras(bundle);
+ }
+ return intent;
+ }
+}
diff --git a/android/support/mediacompat/testlib/util/PollingCheck.java b/android/support/mediacompat/testlib/util/PollingCheck.java
new file mode 100644
index 00000000..3412da02
--- /dev/null
+++ b/android/support/mediacompat/testlib/util/PollingCheck.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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.mediacompat.testlib.util;
+
+import junit.framework.Assert;
+
+/**
+ * Utility used for testing that allows to poll for a certain condition to happen within a timeout.
+ * (Copied from testutils/src/main/java/android/support/testutils/PollingCheck.java.)
+ */
+public abstract class PollingCheck {
+ private static final long DEFAULT_TIMEOUT = 3000;
+ private static final long TIME_SLICE = 50;
+ private final long mTimeout;
+
+ /**
+ * The condition that the PollingCheck should use to proceed successfully.
+ */
+ public interface PollingCheckCondition {
+ /**
+ * @return Whether the polling condition has been met.
+ */
+ boolean canProceed();
+ }
+
+ public PollingCheck(long timeout) {
+ mTimeout = timeout;
+ }
+
+ protected abstract boolean check();
+
+ /**
+ * Start running the polling check.
+ */
+ public void run() {
+ if (check()) {
+ return;
+ }
+
+ long timeout = mTimeout;
+ while (timeout > 0) {
+ try {
+ Thread.sleep(TIME_SLICE);
+ } catch (InterruptedException e) {
+ Assert.fail("unexpected InterruptedException");
+ }
+
+ if (check()) {
+ return;
+ }
+
+ timeout -= TIME_SLICE;
+ }
+
+ Assert.fail("unexpected timeout");
+ }
+
+ /**
+ * Instantiate and start polling for a given condition with a default 3000ms timeout.
+ * @param condition The condition to check for success.
+ */
+ public static void waitFor(final PollingCheckCondition condition) {
+ new PollingCheck(DEFAULT_TIMEOUT) {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
+
+ /**
+ * Instantiate and start polling for a given condition.
+ * @param timeout Time out in ms
+ * @param condition The condition to check for success.
+ */
+ public static void waitFor(long timeout, final PollingCheckCondition condition) {
+ new PollingCheck(timeout) {
+ @Override
+ protected boolean check() {
+ return condition.canProceed();
+ }
+ }.run();
+ }
+}
diff --git a/android/support/mediacompat/testlib/util/TestUtil.java b/android/support/mediacompat/testlib/util/TestUtil.java
new file mode 100644
index 00000000..d105510c
--- /dev/null
+++ b/android/support/mediacompat/testlib/util/TestUtil.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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.mediacompat.testlib.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+
+import android.os.Bundle;
+
+/**
+ * Utility methods used for testing.
+ */
+public final class TestUtil {
+
+ /**
+ * Asserts that two Bundles are equal.
+ */
+ public static void assertBundleEquals(Bundle expected, Bundle observed) {
+ if (expected == null || observed == null) {
+ assertSame(expected, observed);
+ }
+ assertEquals(expected.size(), observed.size());
+ for (String key : expected.keySet()) {
+ assertEquals(expected.get(key), observed.get(key));
+ }
+ }
+}
diff --git a/android/support/text/emoji/widget/EmojiAppCompatEditText.java b/android/support/text/emoji/widget/EmojiAppCompatEditText.java
index 87c17c20..0ae4ea04 100644
--- a/android/support/text/emoji/widget/EmojiAppCompatEditText.java
+++ b/android/support/text/emoji/widget/EmojiAppCompatEditText.java
@@ -21,6 +21,7 @@ import android.support.annotation.IntRange;
import android.support.annotation.Nullable;
import android.support.text.emoji.EmojiCompat;
import android.support.v7.widget.AppCompatEditText;
+import android.text.method.KeyListener;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -67,8 +68,11 @@ public class EmojiAppCompatEditText extends AppCompatEditText {
}
@Override
- public void setKeyListener(android.text.method.KeyListener input) {
- super.setKeyListener(getEmojiEditTextHelper().getKeyListener(input));
+ public void setKeyListener(@Nullable KeyListener keyListener) {
+ if (keyListener != null) {
+ keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
+ }
+ super.setKeyListener(keyListener);
}
@Override
diff --git a/android/support/text/emoji/widget/EmojiEditText.java b/android/support/text/emoji/widget/EmojiEditText.java
index a0e8a69e..70ca7a66 100644
--- a/android/support/text/emoji/widget/EmojiEditText.java
+++ b/android/support/text/emoji/widget/EmojiEditText.java
@@ -21,6 +21,7 @@ import android.os.Build;
import android.support.annotation.IntRange;
import android.support.annotation.Nullable;
import android.support.text.emoji.EmojiCompat;
+import android.text.method.KeyListener;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -73,8 +74,11 @@ public class EmojiEditText extends EditText {
}
@Override
- public void setKeyListener(android.text.method.KeyListener keyListener) {
- super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
+ public void setKeyListener(@Nullable KeyListener keyListener) {
+ if (keyListener != null) {
+ keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
+ }
+ super.setKeyListener(keyListener);
}
@Override
diff --git a/android/support/text/emoji/widget/EmojiExtractEditText.java b/android/support/text/emoji/widget/EmojiExtractEditText.java
index ca1868e2..2e4d3caa 100644
--- a/android/support/text/emoji/widget/EmojiExtractEditText.java
+++ b/android/support/text/emoji/widget/EmojiExtractEditText.java
@@ -27,6 +27,7 @@ import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.support.text.emoji.EmojiCompat;
import android.support.text.emoji.EmojiSpan;
+import android.text.method.KeyListener;
import android.util.AttributeSet;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
@@ -81,8 +82,11 @@ public class EmojiExtractEditText extends ExtractEditText {
}
@Override
- public void setKeyListener(android.text.method.KeyListener keyListener) {
- super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener));
+ public void setKeyListener(@Nullable KeyListener keyListener) {
+ if (keyListener != null) {
+ keyListener = getEmojiEditTextHelper().getKeyListener(keyListener);
+ }
+ super.setKeyListener(keyListener);
}
@Override
diff --git a/android/support/transition/Transition.java b/android/support/transition/Transition.java
index 04cc57bd..9c198a94 100644
--- a/android/support/transition/Transition.java
+++ b/android/support/transition/Transition.java
@@ -1017,7 +1017,7 @@ public abstract class Transition implements Cloneable {
*/
@NonNull
public Transition addTarget(@IdRes int targetId) {
- if (targetId > 0) {
+ if (targetId != 0) {
mTargetIds.add(targetId);
}
return this;
@@ -1107,7 +1107,7 @@ public abstract class Transition implements Cloneable {
*/
@NonNull
public Transition removeTarget(@IdRes int targetId) {
- if (targetId > 0) {
+ if (targetId != 0) {
mTargetIds.remove((Integer) targetId);
}
return this;
diff --git a/android/support/v17/leanback/app/BaseFragment.java b/android/support/v17/leanback/app/BaseFragment.java
index bdb213f2..ea460111 100644
--- a/android/support/v17/leanback/app/BaseFragment.java
+++ b/android/support/v17/leanback/app/BaseFragment.java
@@ -31,7 +31,9 @@ import android.view.ViewTreeObserver;
/**
* Base class for leanback Fragments. This class is not intended to be subclassed by apps.
+ * @deprecated use {@link BaseSupportFragment}
*/
+@Deprecated
@SuppressWarnings("FragmentNotInstantiable")
public class BaseFragment extends BrandedFragment {
diff --git a/android/support/v17/leanback/app/BaseRowFragment.java b/android/support/v17/leanback/app/BaseRowFragment.java
index 2d79f3e1..97a5b848 100644
--- a/android/support/v17/leanback/app/BaseRowFragment.java
+++ b/android/support/v17/leanback/app/BaseRowFragment.java
@@ -34,7 +34,9 @@ import android.view.ViewGroup;
/**
* An internal base class for a fragment containing a list of rows.
+ * @deprecated use {@link BaseRowSupportFragment}
*/
+@Deprecated
abstract class BaseRowFragment extends Fragment {
private static final String CURRENT_SELECTED_POSITION = "currentSelectedPosition";
private ObjectAdapter mAdapter;
@@ -164,8 +166,10 @@ abstract class BaseRowFragment extends Fragment {
* Set the presenter selector used to create and bind views.
*/
public final void setPresenterSelector(PresenterSelector presenterSelector) {
- mPresenterSelector = presenterSelector;
- updateAdapter();
+ if (mPresenterSelector != presenterSelector) {
+ mPresenterSelector = presenterSelector;
+ updateAdapter();
+ }
}
/**
@@ -180,8 +184,10 @@ abstract class BaseRowFragment extends Fragment {
* @param rowsAdapter Adapter that represents list of rows.
*/
public final void setAdapter(ObjectAdapter rowsAdapter) {
- mAdapter = rowsAdapter;
- updateAdapter();
+ if (mAdapter != rowsAdapter) {
+ mAdapter = rowsAdapter;
+ updateAdapter();
+ }
}
/**
diff --git a/android/support/v17/leanback/app/BaseRowSupportFragment.java b/android/support/v17/leanback/app/BaseRowSupportFragment.java
index dba78daf..6a477ab0 100644
--- a/android/support/v17/leanback/app/BaseRowSupportFragment.java
+++ b/android/support/v17/leanback/app/BaseRowSupportFragment.java
@@ -161,8 +161,10 @@ abstract class BaseRowSupportFragment extends Fragment {
* Set the presenter selector used to create and bind views.
*/
public final void setPresenterSelector(PresenterSelector presenterSelector) {
- mPresenterSelector = presenterSelector;
- updateAdapter();
+ if (mPresenterSelector != presenterSelector) {
+ mPresenterSelector = presenterSelector;
+ updateAdapter();
+ }
}
/**
@@ -177,8 +179,10 @@ abstract class BaseRowSupportFragment extends Fragment {
* @param rowsAdapter Adapter that represents list of rows.
*/
public final void setAdapter(ObjectAdapter rowsAdapter) {
- mAdapter = rowsAdapter;
- updateAdapter();
+ if (mAdapter != rowsAdapter) {
+ mAdapter = rowsAdapter;
+ updateAdapter();
+ }
}
/**
diff --git a/android/support/v17/leanback/app/BrandedFragment.java b/android/support/v17/leanback/app/BrandedFragment.java
index 1f6ad299..415c13e0 100644
--- a/android/support/v17/leanback/app/BrandedFragment.java
+++ b/android/support/v17/leanback/app/BrandedFragment.java
@@ -33,7 +33,9 @@ import android.view.ViewGroup;
/**
* Fragment class for managing search and branding using a view that implements
* {@link TitleViewAdapter.Provider}.
+ * @deprecated use {@link BrandedSupportFragment}
*/
+@Deprecated
public class BrandedFragment extends Fragment {
// BUNDLE attribute for title is showing
diff --git a/android/support/v17/leanback/app/BrowseFragment.java b/android/support/v17/leanback/app/BrowseFragment.java
index ae31c4fb..c561ea99 100644
--- a/android/support/v17/leanback/app/BrowseFragment.java
+++ b/android/support/v17/leanback/app/BrowseFragment.java
@@ -81,7 +81,9 @@ import java.util.Map;
* The recommended theme to use with a BrowseFragment is
* {@link android.support.v17.leanback.R.style#Theme_Leanback_Browse}.
* </p>
+ * @deprecated use {@link BrowseSupportFragment}
*/
+@Deprecated
public class BrowseFragment extends BaseFragment {
// BUNDLE attribute for saving header show/hide status when backstack is used:
@@ -203,7 +205,9 @@ public class BrowseFragment extends BaseFragment {
/**
* Listener for transitions between browse headers and rows.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public static class BrowseTransitionListener {
/**
* Callback when headers transition starts.
@@ -267,7 +271,9 @@ public class BrowseFragment extends BaseFragment {
/**
* Possible set of actions that {@link BrowseFragment} exposes to clients. Custom
* fragments can interact with {@link BrowseFragment} using this interface.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public interface FragmentHost {
/**
* Fragments are required to invoke this callback once their view is created
@@ -376,7 +382,9 @@ public class BrowseFragment extends BaseFragment {
* and provide that through {@link MainFragmentAdapterRegistry}.
* {@link MainFragmentAdapter} implementation can supply any fragment and override
* just those interactions that makes sense.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public static class MainFragmentAdapter<T extends Fragment> {
private boolean mScalingEnabled;
private final T mFragment;
@@ -466,7 +474,9 @@ public class BrowseFragment extends BaseFragment {
* Interface to be implemented by all fragments for providing an instance of
* {@link MainFragmentAdapter}. Both {@link RowsFragment} and custom fragment provided
* against {@link PageRow} will need to implement this interface.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public interface MainFragmentAdapterProvider {
/**
* Returns an instance of {@link MainFragmentAdapter} that {@link BrowseFragment}
@@ -478,7 +488,9 @@ public class BrowseFragment extends BaseFragment {
/**
* Interface to be implemented by {@link RowsFragment} and its subclasses for providing
* an instance of {@link MainFragmentRowsAdapter}.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public interface MainFragmentRowsAdapterProvider {
/**
* Returns an instance of {@link MainFragmentRowsAdapter} that {@link BrowseFragment}
@@ -491,7 +503,9 @@ public class BrowseFragment extends BaseFragment {
* This is used to pass information to {@link RowsFragment} or its subclasses.
* {@link BrowseFragment} uses this interface to pass row based interaction events to
* the target fragment.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public static class MainFragmentRowsAdapter<T extends Fragment> {
private final T mFragment;
@@ -570,14 +584,27 @@ public class BrowseFragment extends BaseFragment {
}
boolean oldIsPageRow = mIsPageRow;
+ Object oldPageRow = mPageRow;
mIsPageRow = mCanShowHeaders && item instanceof PageRow;
+ mPageRow = mIsPageRow ? item : null;
boolean swap;
if (mMainFragment == null) {
swap = true;
} else {
if (oldIsPageRow) {
- swap = true;
+ if (mIsPageRow) {
+ if (oldPageRow == null) {
+ // fragment is restored, page row object not yet set, so just set the
+ // mPageRow object and there is no need to replace the fragment
+ swap = false;
+ } else {
+ // swap if page row object changes
+ swap = oldPageRow != mPageRow;
+ }
+ } else {
+ swap = true;
+ }
} else {
swap = mIsPageRow;
}
@@ -590,37 +617,45 @@ public class BrowseFragment extends BaseFragment {
"Fragment must implement MainFragmentAdapterProvider");
}
- mMainFragmentAdapter = ((MainFragmentAdapterProvider)mMainFragment)
- .getMainFragmentAdapter();
- mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
- if (!mIsPageRow) {
- if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
- mMainFragmentRowsAdapter = ((MainFragmentRowsAdapterProvider)mMainFragment)
- .getMainFragmentRowsAdapter();
- } else {
- mMainFragmentRowsAdapter = null;
- }
- mIsPageRow = mMainFragmentRowsAdapter == null;
- } else {
- mMainFragmentRowsAdapter = null;
- }
+ setMainFragmentAdapter();
}
return swap;
}
+ void setMainFragmentAdapter() {
+ mMainFragmentAdapter = ((MainFragmentAdapterProvider) mMainFragment)
+ .getMainFragmentAdapter();
+ mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
+ if (!mIsPageRow) {
+ if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
+ setMainFragmentRowsAdapter(((MainFragmentRowsAdapterProvider) mMainFragment)
+ .getMainFragmentRowsAdapter());
+ } else {
+ setMainFragmentRowsAdapter(null);
+ }
+ mIsPageRow = mMainFragmentRowsAdapter == null;
+ } else {
+ setMainFragmentRowsAdapter(null);
+ }
+ }
+
/**
* Factory class responsible for creating fragment given the current item. {@link ListRow}
* should return {@link RowsFragment} or its subclass whereas {@link PageRow}
* can return any fragment class.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public abstract static class FragmentFactory<T extends Fragment> {
public abstract T createFragment(Object row);
}
/**
* FragmentFactory implementation for {@link ListRow}.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public static class ListRowFragmentFactory extends FragmentFactory<RowsFragment> {
@Override
public RowsFragment createFragment(Object row) {
@@ -634,7 +669,9 @@ public class BrowseFragment extends BaseFragment {
* handling {@link ListRow}. Developers can override that and also if they want to
* use custom fragment, they can register a custom {@link FragmentFactory}
* against {@link PageRow}.
+ * @deprecated use {@link BrowseSupportFragment}
*/
+ @Deprecated
public final static class MainFragmentAdapterRegistry {
private final Map<Class, FragmentFactory> mItemToFragmentFactoryMapping = new HashMap<>();
private final static FragmentFactory sDefaultFragmentFactory = new ListRowFragmentFactory();
@@ -678,7 +715,8 @@ public class BrowseFragment extends BaseFragment {
MainFragmentAdapter mMainFragmentAdapter;
Fragment mMainFragment;
HeadersFragment mHeadersFragment;
- private MainFragmentRowsAdapter mMainFragmentRowsAdapter;
+ MainFragmentRowsAdapter mMainFragmentRowsAdapter;
+ ListRowDataAdapter mMainFragmentListRowDataAdapter;
private ObjectAdapter mAdapter;
private PresenterSelector mAdapterPresenter;
@@ -701,6 +739,7 @@ public class BrowseFragment extends BaseFragment {
private int mSelectedPosition = -1;
private float mScaleFactor;
boolean mIsPageRow;
+ Object mPageRow;
private PresenterSelector mHeaderPresenterSelector;
private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
@@ -820,11 +859,45 @@ public class BrowseFragment extends BaseFragment {
return;
}
+ updateMainFragmentRowsAdapter();
+ mHeadersFragment.setAdapter(mAdapter);
+ }
+
+ void setMainFragmentRowsAdapter(MainFragmentRowsAdapter mainFragmentRowsAdapter) {
+ if (mainFragmentRowsAdapter == mMainFragmentRowsAdapter) {
+ return;
+ }
+ // first clear previous mMainFragmentRowsAdapter and set a new mMainFragmentRowsAdapter
if (mMainFragmentRowsAdapter != null) {
- mMainFragmentRowsAdapter.setAdapter(
- adapter == null ? null : new ListRowDataAdapter(adapter));
+ // RowsFragment cannot change click/select listeners after view created.
+ // The main fragment and adapter should be GCed as long as there is no reference from
+ // BrowseFragment to it.
+ mMainFragmentRowsAdapter.setAdapter(null);
+ }
+ mMainFragmentRowsAdapter = mainFragmentRowsAdapter;
+ if (mMainFragmentRowsAdapter != null) {
+ mMainFragmentRowsAdapter.setOnItemViewSelectedListener(
+ new MainFragmentItemViewSelectedListener(mMainFragmentRowsAdapter));
+ mMainFragmentRowsAdapter.setOnItemViewClickedListener(mOnItemViewClickedListener);
+ }
+ // second update mMainFragmentListRowDataAdapter set on mMainFragmentRowsAdapter
+ updateMainFragmentRowsAdapter();
+ }
+
+ /**
+ * Update mMainFragmentListRowDataAdapter and set it on mMainFragmentRowsAdapter.
+ * It also clears old mMainFragmentListRowDataAdapter.
+ */
+ void updateMainFragmentRowsAdapter() {
+ if (mMainFragmentListRowDataAdapter != null) {
+ mMainFragmentListRowDataAdapter.detach();
+ mMainFragmentListRowDataAdapter = null;
+ }
+ if (mMainFragmentRowsAdapter != null) {
+ mMainFragmentListRowDataAdapter = mAdapter == null
+ ? null : new ListRowDataAdapter(mAdapter);
+ mMainFragmentRowsAdapter.setAdapter(mMainFragmentListRowDataAdapter);
}
- mHeadersFragment.setAdapter(adapter);
}
public final MainFragmentAdapterRegistry getMainFragmentRegistry() {
@@ -1144,7 +1217,8 @@ public class BrowseFragment extends BaseFragment {
@Override
public void onDestroyView() {
- mMainFragmentRowsAdapter = null;
+ setMainFragmentRowsAdapter(null);
+ mPageRow = null;
mMainFragmentAdapter = null;
mMainFragment = null;
mHeadersFragment = null;
@@ -1198,26 +1272,17 @@ public class BrowseFragment extends BaseFragment {
mHeadersFragment = (HeadersFragment) getChildFragmentManager()
.findFragmentById(R.id.browse_headers_dock);
mMainFragment = getChildFragmentManager().findFragmentById(R.id.scale_frame);
- mMainFragmentAdapter = ((MainFragmentAdapterProvider)mMainFragment)
- .getMainFragmentAdapter();
- mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
mIsPageRow = savedInstanceState != null
&& savedInstanceState.getBoolean(IS_PAGE_ROW, false);
+ // mPageRow object is unable to restore, if its null and mIsPageRow is true, this is
+ // the case for restoring, later if setSelection() triggers a createMainFragment(),
+ // should not create fragment.
mSelectedPosition = savedInstanceState != null
? savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
- if (!mIsPageRow) {
- if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
- mMainFragmentRowsAdapter = ((MainFragmentRowsAdapterProvider) mMainFragment)
- .getMainFragmentRowsAdapter();
- } else {
- mMainFragmentRowsAdapter = null;
- }
- } else {
- mMainFragmentRowsAdapter = null;
- }
+ setMainFragmentAdapter();
}
mHeadersFragment.setHeadersGone(!mCanShowHeaders);
@@ -1242,8 +1307,6 @@ public class BrowseFragment extends BaseFragment {
mScaleFrameLayout.setPivotX(0);
mScaleFrameLayout.setPivotY(mContainerListAlignTop);
- setupMainFragment();
-
if (mBrandColorSet) {
mHeadersFragment.setBackgroundColor(mBrandColor);
}
@@ -1270,17 +1333,6 @@ public class BrowseFragment extends BaseFragment {
return root;
}
- private void setupMainFragment() {
- if (mMainFragmentRowsAdapter != null) {
- if (mAdapter != null) {
- mMainFragmentRowsAdapter.setAdapter(new ListRowDataAdapter(mAdapter));
- }
- mMainFragmentRowsAdapter.setOnItemViewSelectedListener(
- new MainFragmentItemViewSelectedListener(mMainFragmentRowsAdapter));
- mMainFragmentRowsAdapter.setOnItemViewClickedListener(mOnItemViewClickedListener);
- }
- }
-
void createHeadersTransition() {
mHeadersTransition = TransitionHelper.loadTransition(FragmentUtil.getContext(BrowseFragment.this),
mShowingHeaders
@@ -1470,10 +1522,10 @@ public class BrowseFragment extends BaseFragment {
};
void onRowSelected(int position) {
- if (position != mSelectedPosition) {
- mSetSelectionRunnable.post(
- position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
- }
+ // even position is same, it could be data changed, always post selection runnable
+ // to possibly swap main fragment.
+ mSetSelectionRunnable.post(
+ position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
}
void setSelection(int position, boolean smooth) {
@@ -1500,7 +1552,6 @@ public class BrowseFragment extends BaseFragment {
if (createMainFragment(mAdapter, position)) {
swapToMainFragment();
expandMainFragment(!(mCanShowHeaders && mShowingHeaders));
- setupMainFragment();
}
}
diff --git a/android/support/v17/leanback/app/BrowseSupportFragment.java b/android/support/v17/leanback/app/BrowseSupportFragment.java
index 4a2502a8..c28064ca 100644
--- a/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -567,14 +567,27 @@ public class BrowseSupportFragment extends BaseSupportFragment {
}
boolean oldIsPageRow = mIsPageRow;
+ Object oldPageRow = mPageRow;
mIsPageRow = mCanShowHeaders && item instanceof PageRow;
+ mPageRow = mIsPageRow ? item : null;
boolean swap;
if (mMainFragment == null) {
swap = true;
} else {
if (oldIsPageRow) {
- swap = true;
+ if (mIsPageRow) {
+ if (oldPageRow == null) {
+ // fragment is restored, page row object not yet set, so just set the
+ // mPageRow object and there is no need to replace the fragment
+ swap = false;
+ } else {
+ // swap if page row object changes
+ swap = oldPageRow != mPageRow;
+ }
+ } else {
+ swap = true;
+ }
} else {
swap = mIsPageRow;
}
@@ -587,25 +600,29 @@ public class BrowseSupportFragment extends BaseSupportFragment {
"Fragment must implement MainFragmentAdapterProvider");
}
- mMainFragmentAdapter = ((MainFragmentAdapterProvider)mMainFragment)
- .getMainFragmentAdapter();
- mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
- if (!mIsPageRow) {
- if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
- mMainFragmentRowsAdapter = ((MainFragmentRowsAdapterProvider)mMainFragment)
- .getMainFragmentRowsAdapter();
- } else {
- mMainFragmentRowsAdapter = null;
- }
- mIsPageRow = mMainFragmentRowsAdapter == null;
- } else {
- mMainFragmentRowsAdapter = null;
- }
+ setMainFragmentAdapter();
}
return swap;
}
+ void setMainFragmentAdapter() {
+ mMainFragmentAdapter = ((MainFragmentAdapterProvider) mMainFragment)
+ .getMainFragmentAdapter();
+ mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
+ if (!mIsPageRow) {
+ if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
+ setMainFragmentRowsAdapter(((MainFragmentRowsAdapterProvider) mMainFragment)
+ .getMainFragmentRowsAdapter());
+ } else {
+ setMainFragmentRowsAdapter(null);
+ }
+ mIsPageRow = mMainFragmentRowsAdapter == null;
+ } else {
+ setMainFragmentRowsAdapter(null);
+ }
+ }
+
/**
* Factory class responsible for creating fragment given the current item. {@link ListRow}
* should return {@link RowsSupportFragment} or its subclass whereas {@link PageRow}
@@ -675,7 +692,8 @@ public class BrowseSupportFragment extends BaseSupportFragment {
MainFragmentAdapter mMainFragmentAdapter;
Fragment mMainFragment;
HeadersSupportFragment mHeadersSupportFragment;
- private MainFragmentRowsAdapter mMainFragmentRowsAdapter;
+ MainFragmentRowsAdapter mMainFragmentRowsAdapter;
+ ListRowDataAdapter mMainFragmentListRowDataAdapter;
private ObjectAdapter mAdapter;
private PresenterSelector mAdapterPresenter;
@@ -698,6 +716,7 @@ public class BrowseSupportFragment extends BaseSupportFragment {
private int mSelectedPosition = -1;
private float mScaleFactor;
boolean mIsPageRow;
+ Object mPageRow;
private PresenterSelector mHeaderPresenterSelector;
private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
@@ -817,11 +836,45 @@ public class BrowseSupportFragment extends BaseSupportFragment {
return;
}
+ updateMainFragmentRowsAdapter();
+ mHeadersSupportFragment.setAdapter(mAdapter);
+ }
+
+ void setMainFragmentRowsAdapter(MainFragmentRowsAdapter mainFragmentRowsAdapter) {
+ if (mainFragmentRowsAdapter == mMainFragmentRowsAdapter) {
+ return;
+ }
+ // first clear previous mMainFragmentRowsAdapter and set a new mMainFragmentRowsAdapter
+ if (mMainFragmentRowsAdapter != null) {
+ // RowsFragment cannot change click/select listeners after view created.
+ // The main fragment and adapter should be GCed as long as there is no reference from
+ // BrowseSupportFragment to it.
+ mMainFragmentRowsAdapter.setAdapter(null);
+ }
+ mMainFragmentRowsAdapter = mainFragmentRowsAdapter;
+ if (mMainFragmentRowsAdapter != null) {
+ mMainFragmentRowsAdapter.setOnItemViewSelectedListener(
+ new MainFragmentItemViewSelectedListener(mMainFragmentRowsAdapter));
+ mMainFragmentRowsAdapter.setOnItemViewClickedListener(mOnItemViewClickedListener);
+ }
+ // second update mMainFragmentListRowDataAdapter set on mMainFragmentRowsAdapter
+ updateMainFragmentRowsAdapter();
+ }
+
+ /**
+ * Update mMainFragmentListRowDataAdapter and set it on mMainFragmentRowsAdapter.
+ * It also clears old mMainFragmentListRowDataAdapter.
+ */
+ void updateMainFragmentRowsAdapter() {
+ if (mMainFragmentListRowDataAdapter != null) {
+ mMainFragmentListRowDataAdapter.detach();
+ mMainFragmentListRowDataAdapter = null;
+ }
if (mMainFragmentRowsAdapter != null) {
- mMainFragmentRowsAdapter.setAdapter(
- adapter == null ? null : new ListRowDataAdapter(adapter));
+ mMainFragmentListRowDataAdapter = mAdapter == null
+ ? null : new ListRowDataAdapter(mAdapter);
+ mMainFragmentRowsAdapter.setAdapter(mMainFragmentListRowDataAdapter);
}
- mHeadersSupportFragment.setAdapter(adapter);
}
public final MainFragmentAdapterRegistry getMainFragmentRegistry() {
@@ -1141,7 +1194,8 @@ public class BrowseSupportFragment extends BaseSupportFragment {
@Override
public void onDestroyView() {
- mMainFragmentRowsAdapter = null;
+ setMainFragmentRowsAdapter(null);
+ mPageRow = null;
mMainFragmentAdapter = null;
mMainFragment = null;
mHeadersSupportFragment = null;
@@ -1195,26 +1249,17 @@ public class BrowseSupportFragment extends BaseSupportFragment {
mHeadersSupportFragment = (HeadersSupportFragment) getChildFragmentManager()
.findFragmentById(R.id.browse_headers_dock);
mMainFragment = getChildFragmentManager().findFragmentById(R.id.scale_frame);
- mMainFragmentAdapter = ((MainFragmentAdapterProvider)mMainFragment)
- .getMainFragmentAdapter();
- mMainFragmentAdapter.setFragmentHost(new FragmentHostImpl());
mIsPageRow = savedInstanceState != null
&& savedInstanceState.getBoolean(IS_PAGE_ROW, false);
+ // mPageRow object is unable to restore, if its null and mIsPageRow is true, this is
+ // the case for restoring, later if setSelection() triggers a createMainFragment(),
+ // should not create fragment.
mSelectedPosition = savedInstanceState != null
? savedInstanceState.getInt(CURRENT_SELECTED_POSITION, 0) : 0;
- if (!mIsPageRow) {
- if (mMainFragment instanceof MainFragmentRowsAdapterProvider) {
- mMainFragmentRowsAdapter = ((MainFragmentRowsAdapterProvider) mMainFragment)
- .getMainFragmentRowsAdapter();
- } else {
- mMainFragmentRowsAdapter = null;
- }
- } else {
- mMainFragmentRowsAdapter = null;
- }
+ setMainFragmentAdapter();
}
mHeadersSupportFragment.setHeadersGone(!mCanShowHeaders);
@@ -1239,8 +1284,6 @@ public class BrowseSupportFragment extends BaseSupportFragment {
mScaleFrameLayout.setPivotX(0);
mScaleFrameLayout.setPivotY(mContainerListAlignTop);
- setupMainFragment();
-
if (mBrandColorSet) {
mHeadersSupportFragment.setBackgroundColor(mBrandColor);
}
@@ -1267,17 +1310,6 @@ public class BrowseSupportFragment extends BaseSupportFragment {
return root;
}
- private void setupMainFragment() {
- if (mMainFragmentRowsAdapter != null) {
- if (mAdapter != null) {
- mMainFragmentRowsAdapter.setAdapter(new ListRowDataAdapter(mAdapter));
- }
- mMainFragmentRowsAdapter.setOnItemViewSelectedListener(
- new MainFragmentItemViewSelectedListener(mMainFragmentRowsAdapter));
- mMainFragmentRowsAdapter.setOnItemViewClickedListener(mOnItemViewClickedListener);
- }
- }
-
void createHeadersTransition() {
mHeadersTransition = TransitionHelper.loadTransition(getContext(),
mShowingHeaders
@@ -1467,10 +1499,10 @@ public class BrowseSupportFragment extends BaseSupportFragment {
};
void onRowSelected(int position) {
- if (position != mSelectedPosition) {
- mSetSelectionRunnable.post(
- position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
- }
+ // even position is same, it could be data changed, always post selection runnable
+ // to possibly swap main fragment.
+ mSetSelectionRunnable.post(
+ position, SetSelectionRunnable.TYPE_INTERNAL_SYNC, true);
}
void setSelection(int position, boolean smooth) {
@@ -1497,7 +1529,6 @@ public class BrowseSupportFragment extends BaseSupportFragment {
if (createMainFragment(mAdapter, position)) {
swapToMainFragment();
expandMainFragment(!(mCanShowHeaders && mShowingHeaders));
- setupMainFragment();
}
}
diff --git a/android/support/v17/leanback/app/DetailsFragment.java b/android/support/v17/leanback/app/DetailsFragment.java
index 36559637..18934f45 100644
--- a/android/support/v17/leanback/app/DetailsFragment.java
+++ b/android/support/v17/leanback/app/DetailsFragment.java
@@ -91,7 +91,9 @@ import java.lang.ref.WeakReference;
* DetailsFragment can use {@link DetailsFragmentBackgroundController} to add a parallax drawable
* background and embedded video playing fragment.
* </p>
+ * @deprecated use {@link DetailsSupportFragment}
*/
+@Deprecated
public class DetailsFragment extends BaseFragment {
static final String TAG = "DetailsFragment";
static boolean DEBUG = false;
diff --git a/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java b/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
index 223b8ef2..25ed723e 100644
--- a/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
+++ b/android/support/v17/leanback/app/DetailsFragmentBackgroundController.java
@@ -107,7 +107,9 @@ import android.app.Fragment;
* {@link #onCreateGlueHost()}.
* </p>
*
+ * @deprecated use {@link DetailsSupportFragmentBackgroundController}
*/
+@Deprecated
public class DetailsFragmentBackgroundController {
final DetailsFragment mFragment;
diff --git a/android/support/v17/leanback/app/ErrorFragment.java b/android/support/v17/leanback/app/ErrorFragment.java
index 2896d0f4..eda0de16 100644
--- a/android/support/v17/leanback/app/ErrorFragment.java
+++ b/android/support/v17/leanback/app/ErrorFragment.java
@@ -32,7 +32,9 @@ import android.widget.TextView;
/**
* A fragment for displaying an error indication.
+ * @deprecated use {@link ErrorSupportFragment}
*/
+@Deprecated
public class ErrorFragment extends BrandedFragment {
private ViewGroup mErrorFrame;
diff --git a/android/support/v17/leanback/app/GuidedStepFragment.java b/android/support/v17/leanback/app/GuidedStepFragment.java
index 2b7f2d0d..9be350d8 100644
--- a/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -27,6 +27,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.widget.DiffCallback;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
@@ -140,7 +141,9 @@ import java.util.List;
* @see GuidanceStylist.Guidance
* @see GuidedAction
* @see GuidedActionsStylist
+ * @deprecated use {@link GuidedStepSupportFragment}
*/
+@Deprecated
public class GuidedStepFragment extends Fragment implements GuidedActionAdapter.FocusListener {
private static final String TAG_LEAN_BACK_ACTIONS_FRAGMENT = "leanBackGuidedStepFragment";
@@ -806,6 +809,8 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter.
/**
* Sets the list of GuidedActions that the user may take in this fragment.
+ * Uses DiffCallback set by {@link #setActionsDiffCallback(DiffCallback)}.
+ *
* @param actions The list of GuidedActions for this fragment.
*/
public void setActions(List<GuidedAction> actions) {
@@ -816,6 +821,18 @@ public class GuidedStepFragment extends Fragment implements GuidedActionAdapter.
}
/**
+ * Sets the RecyclerView DiffCallback used when {@link #setActions(List)} is called. By default
+ * GuidedStepFragment uses
+ * {@link android.support.v17.leanback.widget.GuidedActionDiffCallback}.
+ * Sets it to null if app wants to refresh the whole list.
+ *
+ * @param diffCallback DiffCallback used in {@link #setActions(List)}.
+ */
+ public void setActionsDiffCallback(DiffCallback<GuidedAction> diffCallback) {
+ mAdapter.setDiffCallback(diffCallback);
+ }
+
+ /**
* Notify an action has changed and update its UI.
* @param position Position of the GuidedAction in array.
*/
diff --git a/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index aeb2d334..e276d076 100644
--- a/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -24,6 +24,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.support.v17.leanback.R;
import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.widget.DiffCallback;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
import android.support.v17.leanback.widget.GuidedAction;
@@ -803,6 +804,8 @@ public class GuidedStepSupportFragment extends Fragment implements GuidedActionA
/**
* Sets the list of GuidedActions that the user may take in this fragment.
+ * Uses DiffCallback set by {@link #setActionsDiffCallback(DiffCallback)}.
+ *
* @param actions The list of GuidedActions for this fragment.
*/
public void setActions(List<GuidedAction> actions) {
@@ -813,6 +816,18 @@ public class GuidedStepSupportFragment extends Fragment implements GuidedActionA
}
/**
+ * Sets the RecyclerView DiffCallback used when {@link #setActions(List)} is called. By default
+ * GuidedStepSupportFragment uses
+ * {@link android.support.v17.leanback.widget.GuidedActionDiffCallback}.
+ * Sets it to null if app wants to refresh the whole list.
+ *
+ * @param diffCallback DiffCallback used in {@link #setActions(List)}.
+ */
+ public void setActionsDiffCallback(DiffCallback<GuidedAction> diffCallback) {
+ mAdapter.setDiffCallback(diffCallback);
+ }
+
+ /**
* Notify an action has changed and update its UI.
* @param position Position of the GuidedAction in array.
*/
diff --git a/android/support/v17/leanback/app/HeadersFragment.java b/android/support/v17/leanback/app/HeadersFragment.java
index dd037d2f..08780a50 100644
--- a/android/support/v17/leanback/app/HeadersFragment.java
+++ b/android/support/v17/leanback/app/HeadersFragment.java
@@ -52,12 +52,16 @@ import android.widget.FrameLayout;
* </ul>
* Use {@link #setPresenterSelector(PresenterSelector)} in subclass constructor to customize
* Presenters. App may override {@link BrowseFragment#onCreateHeadersFragment()}.
+ * @deprecated use {@link HeadersSupportFragment}
*/
+@Deprecated
public class HeadersFragment extends BaseRowFragment {
/**
* Interface definition for a callback to be invoked when a header item is clicked.
+ * @deprecated use {@link HeadersSupportFragment}
*/
+ @Deprecated
public interface OnHeaderClickedListener {
/**
* Called when a header item has been clicked.
@@ -70,7 +74,9 @@ public class HeadersFragment extends BaseRowFragment {
/**
* Interface definition for a callback to be invoked when a header item is selected.
+ * @deprecated use {@link HeadersSupportFragment}
*/
+ @Deprecated
public interface OnHeaderViewSelectedListener {
/**
* Called when a header item has been selected.
diff --git a/android/support/v17/leanback/app/ListRowDataAdapter.java b/android/support/v17/leanback/app/ListRowDataAdapter.java
index f9af12f3..03d948be 100644
--- a/android/support/v17/leanback/app/ListRowDataAdapter.java
+++ b/android/support/v17/leanback/app/ListRowDataAdapter.java
@@ -13,6 +13,7 @@ import android.support.v17.leanback.widget.Row;
* thinks there are items even though they're invisible. This class takes care of filtering out
* the invisible rows at the end. In case the data inside the adapter changes, it adjusts the
* bounds to reflect the latest data.
+ * {@link #detach()} must be called to release DataObserver from Adapter.
*/
class ListRowDataAdapter extends ObjectAdapter {
public static final int ON_ITEM_RANGE_CHANGED = 2;
@@ -22,6 +23,7 @@ class ListRowDataAdapter extends ObjectAdapter {
private final ObjectAdapter mAdapter;
int mLastVisibleRowIndex;
+ final DataObserver mDataObserver;
public ListRowDataAdapter(ObjectAdapter adapter) {
super(adapter.getPresenterSelector());
@@ -34,10 +36,20 @@ class ListRowDataAdapter extends ObjectAdapter {
// operation. To handle this case, we use QueueBasedDataObserver which forces
// recyclerview to do a full data refresh after each update operation.
if (adapter.isImmediateNotifySupported()) {
- mAdapter.registerObserver(new SimpleDataObserver());
+ mDataObserver = new SimpleDataObserver();
} else {
- mAdapter.registerObserver(new QueueBasedDataObserver());
+ mDataObserver = new QueueBasedDataObserver();
}
+ attach();
+ }
+
+ void detach() {
+ mAdapter.unregisterObserver(mDataObserver);
+ }
+
+ void attach() {
+ initialize();
+ mAdapter.registerObserver(mDataObserver);
}
void initialize() {
diff --git a/android/support/v17/leanback/app/OnboardingFragment.java b/android/support/v17/leanback/app/OnboardingFragment.java
index b69d5a72..f352c413 100644
--- a/android/support/v17/leanback/app/OnboardingFragment.java
+++ b/android/support/v17/leanback/app/OnboardingFragment.java
@@ -154,7 +154,9 @@ import java.util.List;
* @attr ref R.styleable#LeanbackOnboardingTheme_onboardingPageIndicatorStyle
* @attr ref R.styleable#LeanbackOnboardingTheme_onboardingStartButtonStyle
* @attr ref R.styleable#LeanbackOnboardingTheme_onboardingLogoStyle
+ * @deprecated use {@link OnboardingSupportFragment}
*/
+@Deprecated
abstract public class OnboardingFragment extends Fragment {
private static final String TAG = "OnboardingF";
private static final boolean DEBUG = false;
diff --git a/android/support/v17/leanback/app/PlaybackFragment.java b/android/support/v17/leanback/app/PlaybackFragment.java
index 33e787c3..e2e6be48 100644
--- a/android/support/v17/leanback/app/PlaybackFragment.java
+++ b/android/support/v17/leanback/app/PlaybackFragment.java
@@ -81,7 +81,9 @@ import android.view.animation.AccelerateInterpolator;
* {@link #setControlsOverlayAutoHideEnabled(boolean)} upon play/pause. The auto hiding timer will
* be cancelled upon {@link #tickle()} triggered by input event.
* </p>
+ * @deprecated use {@link PlaybackSupportFragment}
*/
+@Deprecated
public class PlaybackFragment extends Fragment {
static final String BUNDLE_CONTROL_VISIBLE_ON_CREATEVIEW = "controlvisible_oncreateview";
@@ -181,7 +183,9 @@ public class PlaybackFragment extends Fragment {
* Listener allowing the application to receive notification of fade in and/or fade out
* completion events.
* @hide
+ * @deprecated use {@link PlaybackSupportFragment}
*/
+ @Deprecated
public static class OnFadeCompleteListener {
public void onFadeInComplete() {
}
diff --git a/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java b/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
index 4a9d10f8..9e342fdb 100644
--- a/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
+++ b/android/support/v17/leanback/app/PlaybackFragmentGlueHost.java
@@ -30,7 +30,9 @@ import android.view.View;
/**
* {@link PlaybackGlueHost} implementation
* the interaction between this class and {@link PlaybackFragment}.
+ * @deprecated use {@link PlaybackSupportFragmentGlueHost}
*/
+@Deprecated
public class PlaybackFragmentGlueHost extends PlaybackGlueHost implements PlaybackSeekUi {
private final PlaybackFragment mFragment;
diff --git a/android/support/v17/leanback/app/RowsFragment.java b/android/support/v17/leanback/app/RowsFragment.java
index a008ad60..aa346bd9 100644
--- a/android/support/v17/leanback/app/RowsFragment.java
+++ b/android/support/v17/leanback/app/RowsFragment.java
@@ -53,7 +53,9 @@ import java.util.ArrayList;
* of rows in a vertical list. The Adapter's {@link PresenterSelector} must maintain subclasses
* of {@link RowPresenter}.
* </p>
+ * @deprecated use {@link RowsSupportFragment}
*/
+@Deprecated
public class RowsFragment extends BaseRowFragment implements
BrowseFragment.MainFragmentRowsAdapterProvider,
BrowseFragment.MainFragmentAdapterProvider {
@@ -634,7 +636,9 @@ public class RowsFragment extends BaseRowFragment implements
* The adapter that RowsFragment implements
* BrowseFragment.MainFragmentRowsAdapter.
* @see #getMainFragmentRowsAdapter().
+ * @deprecated use {@link RowsSupportFragment}
*/
+ @Deprecated
public static class MainFragmentRowsAdapter
extends BrowseFragment.MainFragmentRowsAdapter<RowsFragment> {
diff --git a/android/support/v17/leanback/app/SearchFragment.java b/android/support/v17/leanback/app/SearchFragment.java
index 2154ff28..00f2cca8 100644
--- a/android/support/v17/leanback/app/SearchFragment.java
+++ b/android/support/v17/leanback/app/SearchFragment.java
@@ -66,7 +66,9 @@ import java.util.List;
* not when fragment is restored from an instance state. Activity may manually
* call {@link #startRecognition()}, typically in onNewIntent().
* </p>
+ * @deprecated use {@link SearchSupportFragment}
*/
+@Deprecated
public class SearchFragment extends Fragment {
static final String TAG = SearchFragment.class.getSimpleName();
static final boolean DEBUG = false;
diff --git a/android/support/v17/leanback/app/VerticalGridFragment.java b/android/support/v17/leanback/app/VerticalGridFragment.java
index 5bc52ff5..bff3dbab 100644
--- a/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -39,7 +39,9 @@ import android.view.ViewGroup;
*
* <p>Renders a vertical grid of objects given a {@link VerticalGridPresenter} and
* an {@link ObjectAdapter}.
+ * @deprecated use {@link VerticalGridSupportFragment}
*/
+@Deprecated
public class VerticalGridFragment extends BaseFragment {
static final String TAG = "VerticalGF";
static boolean DEBUG = false;
diff --git a/android/support/v17/leanback/app/VideoFragment.java b/android/support/v17/leanback/app/VideoFragment.java
index 1b2b8d07..e4d75f30 100644
--- a/android/support/v17/leanback/app/VideoFragment.java
+++ b/android/support/v17/leanback/app/VideoFragment.java
@@ -27,7 +27,9 @@ import android.view.ViewGroup;
/**
* Subclass of {@link PlaybackFragment} that is responsible for providing a {@link SurfaceView}
* and rendering video.
+ * @deprecated use {@link VideoSupportFragment}
*/
+@Deprecated
public class VideoFragment extends PlaybackFragment {
static final int SURFACE_NOT_CREATED = 0;
static final int SURFACE_CREATED = 1;
diff --git a/android/support/v17/leanback/app/VideoFragmentGlueHost.java b/android/support/v17/leanback/app/VideoFragmentGlueHost.java
index d123676f..546e581c 100644
--- a/android/support/v17/leanback/app/VideoFragmentGlueHost.java
+++ b/android/support/v17/leanback/app/VideoFragmentGlueHost.java
@@ -24,7 +24,9 @@ import android.view.SurfaceHolder;
/**
* {@link PlaybackGlueHost} implementation
* the interaction between {@link PlaybackGlue} and {@link VideoFragment}.
+ * @deprecated use {@link VideoSupportFragmentGlueHost}
*/
+@Deprecated
public class VideoFragmentGlueHost extends PlaybackFragmentGlueHost
implements SurfaceHolderGlueHost {
private final VideoFragment mFragment;
diff --git a/android/support/v17/leanback/widget/ArrayObjectAdapter.java b/android/support/v17/leanback/widget/ArrayObjectAdapter.java
index 00bc073d..2dcf51f7 100644
--- a/android/support/v17/leanback/widget/ArrayObjectAdapter.java
+++ b/android/support/v17/leanback/widget/ArrayObjectAdapter.java
@@ -225,6 +225,8 @@ public class ArrayObjectAdapter extends ObjectAdapter {
return true;
}
+ ListUpdateCallback mListUpdateCallback;
+
/**
* Set a new item list to adapter. The DiffUtil will compute the difference and dispatch it to
* specified position.
@@ -280,39 +282,43 @@ public class ArrayObjectAdapter extends ObjectAdapter {
mItems.addAll(itemList);
// dispatch diff result
- diffResult.dispatchUpdatesTo(new ListUpdateCallback() {
-
- @Override
- public void onInserted(int position, int count) {
- if (DEBUG) {
- Log.d(TAG, "onInserted");
+ if (mListUpdateCallback == null) {
+ mListUpdateCallback = new ListUpdateCallback() {
+
+ @Override
+ public void onInserted(int position, int count) {
+ if (DEBUG) {
+ Log.d(TAG, "onInserted");
+ }
+ notifyItemRangeInserted(position, count);
}
- notifyItemRangeInserted(position, count);
- }
- @Override
- public void onRemoved(int position, int count) {
- if (DEBUG) {
- Log.d(TAG, "onRemoved");
+ @Override
+ public void onRemoved(int position, int count) {
+ if (DEBUG) {
+ Log.d(TAG, "onRemoved");
+ }
+ notifyItemRangeRemoved(position, count);
}
- notifyItemRangeRemoved(position, count);
- }
- @Override
- public void onMoved(int fromPosition, int toPosition) {
- if (DEBUG) {
- Log.d(TAG, "onMoved");
+ @Override
+ public void onMoved(int fromPosition, int toPosition) {
+ if (DEBUG) {
+ Log.d(TAG, "onMoved");
+ }
+ notifyItemMoved(fromPosition, toPosition);
}
- notifyItemMoved(fromPosition, toPosition);
- }
- @Override
- public void onChanged(int position, int count, Object payload) {
- if (DEBUG) {
- Log.d(TAG, "onChanged");
+ @Override
+ public void onChanged(int position, int count, Object payload) {
+ if (DEBUG) {
+ Log.d(TAG, "onChanged");
+ }
+ notifyItemRangeChanged(position, count, payload);
}
- notifyItemRangeChanged(position, count, payload);
- }
- });
+ };
+ }
+ diffResult.dispatchUpdatesTo(mListUpdateCallback);
+ mOldItems.clear();
}
}
diff --git a/android/support/v17/leanback/widget/BaseGridView.java b/android/support/v17/leanback/widget/BaseGridView.java
index f4e01c0b..2ebec47e 100644
--- a/android/support/v17/leanback/widget/BaseGridView.java
+++ b/android/support/v17/leanback/widget/BaseGridView.java
@@ -1134,7 +1134,7 @@ public abstract class BaseGridView extends RecyclerView {
@Override
public void scrollToPosition(int position) {
// dont abort the animateOut() animation, just record the position
- if (mLayoutManager.mIsSlidingChildViews) {
+ if (mLayoutManager.isSlidingChildViews()) {
mLayoutManager.setSelectionWithSub(position, 0, 0);
return;
}
@@ -1144,7 +1144,7 @@ public abstract class BaseGridView extends RecyclerView {
@Override
public void smoothScrollToPosition(int position) {
// dont abort the animateOut() animation, just record the position
- if (mLayoutManager.mIsSlidingChildViews) {
+ if (mLayoutManager.isSlidingChildViews()) {
mLayoutManager.setSelectionWithSub(position, 0, 0);
return;
}
diff --git a/android/support/v17/leanback/widget/GridLayoutManager.java b/android/support/v17/leanback/widget/GridLayoutManager.java
index dded0715..d7020e91 100644
--- a/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -217,9 +217,9 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mFocusPosition = getTargetPosition();
}
if (hasFocus()) {
- mInSelection = true;
+ mFlag |= PF_IN_SELECTION;
targetView.requestFocus();
- mInSelection = false;
+ mFlag &= ~PF_IN_SELECTION;
}
dispatchChildSelected();
dispatchChildSelectedAndPositioned();
@@ -320,9 +320,9 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
}
if (newSelected != null && hasFocus()) {
- mInSelection = true;
+ mFlag |= PF_IN_SELECTION;
newSelected.requestFocus();
- mInSelection = false;
+ mFlag &= ~PF_IN_SELECTION;
}
}
@@ -355,7 +355,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
if (mPendingMoves == 0) {
return null;
}
- int direction = (mReverseFlowPrimary ? mPendingMoves > 0 : mPendingMoves < 0)
+ int direction = ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
+ ? mPendingMoves > 0 : mPendingMoves < 0)
? -1 : 1;
if (mOrientation == HORIZONTAL) {
return new PointF(direction, 0);
@@ -386,10 +387,6 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
// effect smooth scrolling too over to bind an item view then drag the item view back.
final static int MIN_MS_SMOOTH_SCROLL_MAIN_SCREEN = 30;
- // Represents whether child views are temporarily sliding out
- boolean mIsSlidingChildViews;
- boolean mLayoutEatenInSliding;
-
String getTag() {
return TAG + ":" + mBaseGridView.getId();
}
@@ -444,15 +441,101 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
private static final Rect sTempRect = new Rect();
- boolean mInLayout;
- private boolean mInScroll;
- boolean mInFastRelayout;
+ // 2 bits mask is for 3 STAGEs: 0, PF_STAGE_LAYOUT or PF_STAGE_SCROLL.
+ static final int PF_STAGE_MASK = 0x3;
+ static final int PF_STAGE_LAYOUT = 0x1;
+ static final int PF_STAGE_SCROLL = 0x2;
+
+ // Flag for "in fast relayout", determined by layoutInit() result.
+ static final int PF_FAST_RELAYOUT = 1 << 2;
+
+ // Flag for the selected item being updated in fast relayout.
+ static final int PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION = 1 << 3;
/**
* During full layout pass, when GridView had focus: onLayoutChildren will
* skip non-focusable child and adjust mFocusPosition.
*/
- boolean mInLayoutSearchFocus;
- boolean mInSelection = false;
+ static final int PF_IN_LAYOUT_SEARCH_FOCUS = 1 << 4;
+
+ // flag to prevent reentry if it's already processing selection request.
+ static final int PF_IN_SELECTION = 1 << 5;
+
+ // Represents whether child views are temporarily sliding out
+ static final int PF_SLIDING = 1 << 6;
+ static final int PF_LAYOUT_EATEN_IN_SLIDING = 1 << 7;
+
+ /**
+ * Force a full layout under certain situations. E.g. Rows change, jump to invisible child.
+ */
+ static final int PF_FORCE_FULL_LAYOUT = 1 << 8;
+
+ /**
+ * True if layout is enabled.
+ */
+ static final int PF_LAYOUT_ENABLED = 1 << 9;
+
+ /**
+ * Flag controlling whether the current/next layout should
+ * be updating the secondary size of rows.
+ */
+ static final int PF_ROW_SECONDARY_SIZE_REFRESH = 1 << 10;
+
+ /**
+ * Allow DPAD key to navigate out at the front of the View (where position = 0),
+ * default is false.
+ */
+ static final int PF_FOCUS_OUT_FRONT = 1 << 11;
+
+ /**
+ * Allow DPAD key to navigate out at the end of the view, default is false.
+ */
+ static final int PF_FOCUS_OUT_END = 1 << 12;
+
+ static final int PF_FOCUS_OUT_MASKS = PF_FOCUS_OUT_FRONT | PF_FOCUS_OUT_END;
+
+ /**
+ * Allow DPAD key to navigate out of second axis.
+ * default is true.
+ */
+ static final int PF_FOCUS_OUT_SIDE_START = 1 << 13;
+
+ /**
+ * Allow DPAD key to navigate out of second axis.
+ */
+ static final int PF_FOCUS_OUT_SIDE_END = 1 << 14;
+
+ static final int PF_FOCUS_OUT_SIDE_MASKS = PF_FOCUS_OUT_SIDE_START | PF_FOCUS_OUT_SIDE_END;
+
+ /**
+ * True if focus search is disabled.
+ */
+ static final int PF_FOCUS_SEARCH_DISABLED = 1 << 15;
+
+ /**
+ * True if prune child, might be disabled during transition.
+ */
+ static final int PF_PRUNE_CHILD = 1 << 16;
+
+ /**
+ * True if scroll content, might be disabled during transition.
+ */
+ static final int PF_SCROLL_ENABLED = 1 << 17;
+
+ /**
+ * Set to true for RTL layout in horizontal orientation
+ */
+ static final int PF_REVERSE_FLOW_PRIMARY = 1 << 18;
+
+ /**
+ * Set to true for RTL layout in vertical orientation
+ */
+ static final int PF_REVERSE_FLOW_SECONDARY = 1 << 19;
+
+ static final int PF_REVERSE_FLOW_MASK = PF_REVERSE_FLOW_PRIMARY | PF_REVERSE_FLOW_SECONDARY;
+
+ int mFlag = PF_LAYOUT_ENABLED
+ | PF_FOCUS_OUT_SIDE_START | PF_FOCUS_OUT_SIDE_END
+ | PF_PRUNE_CHILD | PF_SCROLL_ENABLED;
private OnChildSelectedListener mChildSelectedListener = null;
@@ -493,16 +576,6 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
private int mPrimaryScrollExtra;
/**
- * Force a full layout under certain situations. E.g. Rows change, jump to invisible child.
- */
- private boolean mForceFullLayout;
-
- /**
- * True if layout is enabled.
- */
- private boolean mLayoutEnabled = true;
-
- /**
* override child visibility
*/
@Visibility
@@ -535,12 +608,6 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
private int[] mRowSizeSecondary;
/**
- * Flag controlling whether the current/next layout should
- * be updating the secondary size of rows.
- */
- private boolean mRowSecondarySizeRefresh;
-
- /**
* The maximum measured size of the view.
*/
private int mMaxSizeSecondary;
@@ -605,58 +672,11 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
private int mExtraLayoutSpace;
/**
- * Allow DPAD key to navigate out at the front of the View (where position = 0),
- * default is false.
- */
- private boolean mFocusOutFront;
-
- /**
- * Allow DPAD key to navigate out at the end of the view, default is false.
- */
- private boolean mFocusOutEnd;
-
- /**
- * Allow DPAD key to navigate out of second axis.
- * default is true.
- */
- private boolean mFocusOutSideStart = true;
-
- /**
- * Allow DPAD key to navigate out of second axis.
- */
- private boolean mFocusOutSideEnd = true;
-
- /**
- * True if focus search is disabled.
- */
- private boolean mFocusSearchDisabled;
-
- /**
- * True if prune child, might be disabled during transition.
- */
- private boolean mPruneChild = true;
-
- /**
- * True if scroll content, might be disabled during transition.
- */
- private boolean mScrollEnabled = true;
-
- /**
* Temporary variable: an int array of length=2.
*/
static int[] sTwoInts = new int[2];
/**
- * Set to true for RTL layout in horizontal orientation
- */
- boolean mReverseFlowPrimary = false;
-
- /**
- * Set to true for RTL layout in vertical orientation
- */
- private boolean mReverseFlowSecondary = false;
-
- /**
* Temporaries used for measuring.
*/
private int[] mMeasuredDimension = new int[2];
@@ -685,24 +705,21 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
mWindowAlignment.setOrientation(orientation);
mItemAlignment.setOrientation(orientation);
- mForceFullLayout = true;
+ mFlag |= PF_FORCE_FULL_LAYOUT;
}
public void onRtlPropertiesChanged(int layoutDirection) {
- boolean reversePrimary, reverseSecondary;
+ final int flags;
if (mOrientation == HORIZONTAL) {
- reversePrimary = layoutDirection == View.LAYOUT_DIRECTION_RTL;
- reverseSecondary = false;
+ flags = layoutDirection == View.LAYOUT_DIRECTION_RTL ? PF_REVERSE_FLOW_PRIMARY : 0;
} else {
- reverseSecondary = layoutDirection == View.LAYOUT_DIRECTION_RTL;
- reversePrimary = false;
+ flags = layoutDirection == View.LAYOUT_DIRECTION_RTL ? PF_REVERSE_FLOW_SECONDARY : 0;
}
- if (mReverseFlowPrimary == reversePrimary && mReverseFlowSecondary == reverseSecondary) {
+ if ((mFlag & PF_REVERSE_FLOW_MASK) == flags) {
return;
}
- mReverseFlowPrimary = reversePrimary;
- mReverseFlowSecondary = reverseSecondary;
- mForceFullLayout = true;
+ mFlag = (mFlag & ~PF_REVERSE_FLOW_MASK) | flags;
+ mFlag |= PF_FORCE_FULL_LAYOUT;
mWindowAlignment.horizontal.setReversedFlow(layoutDirection == View.LAYOUT_DIRECTION_RTL);
}
@@ -775,13 +792,15 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
public void setFocusOutAllowed(boolean throughFront, boolean throughEnd) {
- mFocusOutFront = throughFront;
- mFocusOutEnd = throughEnd;
+ mFlag = (mFlag & ~PF_FOCUS_OUT_MASKS)
+ | (throughFront ? PF_FOCUS_OUT_FRONT : 0)
+ | (throughEnd ? PF_FOCUS_OUT_END : 0);
}
public void setFocusOutSideAllowed(boolean throughStart, boolean throughEnd) {
- mFocusOutSideStart = throughStart;
- mFocusOutSideEnd = throughEnd;
+ mFlag = (mFlag & ~PF_FOCUS_OUT_SIDE_MASKS)
+ | (throughStart ? PF_FOCUS_OUT_SIDE_START : 0)
+ | (throughEnd ? PF_FOCUS_OUT_SIDE_END : 0);
}
public void setNumRows(int numRows) {
@@ -971,7 +990,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
// layout warning.
// If not in layout, we may be scrolling in which case the child layout request will be
// eaten by recyclerview. Post a requestLayout.
- if (!mInLayout && !mBaseGridView.isLayoutRequested()) {
+ if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT && !mBaseGridView.isLayoutRequested()) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
if (getChildAt(i).isLayoutRequested()) {
@@ -1177,19 +1196,19 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mSubFocusPosition = 0;
}
if (!mState.didStructureChange() && mGrid != null && mGrid.getFirstVisibleIndex() >= 0
- && !mForceFullLayout && mGrid.getNumRows() == mNumRows) {
+ && (mFlag & PF_FORCE_FULL_LAYOUT) == 0 && mGrid.getNumRows() == mNumRows) {
updateScrollController();
updateSecondaryScrollLimits();
mGrid.setSpacing(mSpacingPrimary);
return true;
} else {
- mForceFullLayout = false;
+ mFlag &= ~PF_FORCE_FULL_LAYOUT;
if (mGrid == null || mNumRows != mGrid.getNumRows()
- || mReverseFlowPrimary != mGrid.isReversedFlow()) {
+ || ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) != mGrid.isReversedFlow()) {
mGrid = Grid.createGrid(mNumRows);
mGrid.setProvider(mGridProvider);
- mGrid.setReversedFlow(mReverseFlowPrimary);
+ mGrid.setReversedFlow((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0);
}
initScrollController();
updateSecondaryScrollLimits();
@@ -1216,7 +1235,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
int start = 0;
// Iterate from left to right, which is a different index traversal
// in RTL flow
- if (mReverseFlowSecondary) {
+ if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) {
for (int i = mNumRows-1; i > rowIndex; i--) {
start += getRowSizeSecondary(i) + mSpacingSecondary;
}
@@ -1229,7 +1248,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
private int getSizeSecondary() {
- int rightmostIndex = mReverseFlowSecondary ? 0 : mNumRows - 1;
+ int rightmostIndex = (mFlag & PF_REVERSE_FLOW_SECONDARY) != 0 ? 0 : mNumRows - 1;
return getRowStartSecondary(rightmostIndex) + getRowSizeSecondary(rightmostIndex);
}
@@ -1366,8 +1385,9 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
* Checks if we need to update row secondary sizes.
*/
private void updateRowSecondarySizeRefresh() {
- mRowSecondarySizeRefresh = processRowSizeSecondary(false);
- if (mRowSecondarySizeRefresh) {
+ mFlag = (mFlag & ~PF_ROW_SECONDARY_SIZE_REFRESH)
+ | (processRowSizeSecondary(false) ? PF_ROW_SECONDARY_SIZE_REFRESH : 0);
+ if ((mFlag & PF_ROW_SECONDARY_SIZE_REFRESH) != 0) {
if (DEBUG) Log.v(getTag(), "mRowSecondarySizeRefresh now set");
forceRequestLayout();
}
@@ -1599,7 +1619,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mPendingMoveSmoothScroller.consumePendingMovesBeforeLayout();
}
int subindex = getSubPositionByView(v, v.findFocus());
- if (!mInLayout) {
+ if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT) {
// when we are appending item during scroll pass and the item's position
// matches the mFocusPosition, we should signal a childSelected event.
// However if we are still running PendingMoveSmoothScroller, we defer and
@@ -1610,20 +1630,20 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
&& mPendingMoveSmoothScroller == null) {
dispatchChildSelected();
}
- } else if (!mInFastRelayout) {
+ } else if ((mFlag & PF_FAST_RELAYOUT) == 0) {
// fastRelayout will dispatch event at end of onLayoutChildren().
// For full layout, two situations here:
// 1. mInLayoutSearchFocus is false, dispatchChildSelected() at mFocusPosition.
// 2. mInLayoutSearchFocus is true: dispatchChildSelected() on first child
// equal to or after mFocusPosition that can take focus.
- if (!mInLayoutSearchFocus && index == mFocusPosition
+ if ((mFlag & PF_IN_LAYOUT_SEARCH_FOCUS) == 0 && index == mFocusPosition
&& subindex == mSubFocusPosition) {
dispatchChildSelected();
- } else if (mInLayoutSearchFocus && index >= mFocusPosition
+ } else if ((mFlag & PF_IN_LAYOUT_SEARCH_FOCUS) != 0 && index >= mFocusPosition
&& v.hasFocusable()) {
mFocusPosition = index;
mSubFocusPosition = subindex;
- mInLayoutSearchFocus = false;
+ mFlag &= ~PF_IN_LAYOUT_SEARCH_FOCUS;
dispatchChildSelected();
}
}
@@ -1663,7 +1683,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
if (!mState.isPreLayout()) {
updateScrollLimits();
}
- if (!mInLayout && mPendingMoveSmoothScroller != null) {
+ if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT && mPendingMoveSmoothScroller != null) {
mPendingMoveSmoothScroller.consumePendingMovesAfterLayout();
}
if (mChildLaidOutListener != null) {
@@ -1677,7 +1697,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
public void removeItem(int index) {
if (TRACE) TraceCompat.beginSection("removeItem");
View v = findViewByPosition(index - mPositionDeltaInPreLayout);
- if (mInLayout) {
+ if ((mFlag & PF_STAGE_MASK) == PF_STAGE_LAYOUT) {
detachAndScrapView(v, mRecycler);
} else {
removeAndRecycleView(v, mRecycler);
@@ -1688,7 +1708,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
@Override
public int getEdge(int index) {
View v = findViewByPosition(index - mPositionDeltaInPreLayout);
- return mReverseFlowPrimary ? getViewMax(v) : getViewMin(v);
+ return (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0 ? getViewMax(v) : getViewMin(v);
}
@Override
@@ -1705,7 +1725,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
sizeSecondary = Math.min(sizeSecondary, mFixedRowSizeSecondary);
}
final int verticalGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
- final int horizontalGravity = (mReverseFlowPrimary || mReverseFlowSecondary)
+ final int horizontalGravity = (mFlag & PF_REVERSE_FLOW_MASK) != 0
? Gravity.getAbsoluteGravity(mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK,
View.LAYOUT_DIRECTION_RTL)
: mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
@@ -1781,16 +1801,16 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
private void removeInvisibleViewsAtEnd() {
- if (mPruneChild && !mIsSlidingChildViews) {
- mGrid.removeInvisibleItemsAtEnd(mFocusPosition,
- mReverseFlowPrimary ? -mExtraLayoutSpace : mSizePrimary + mExtraLayoutSpace);
+ if ((mFlag & (PF_PRUNE_CHILD | PF_SLIDING)) == PF_PRUNE_CHILD) {
+ mGrid.removeInvisibleItemsAtEnd(mFocusPosition, (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
+ ? -mExtraLayoutSpace : mSizePrimary + mExtraLayoutSpace);
}
}
private void removeInvisibleViewsAtFront() {
- if (mPruneChild && !mIsSlidingChildViews) {
- mGrid.removeInvisibleItemsAtFront(mFocusPosition,
- mReverseFlowPrimary ? mSizePrimary + mExtraLayoutSpace: -mExtraLayoutSpace);
+ if ((mFlag & (PF_PRUNE_CHILD | PF_SLIDING)) == PF_PRUNE_CHILD) {
+ mGrid.removeInvisibleItemsAtFront(mFocusPosition, (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
+ ? mSizePrimary + mExtraLayoutSpace : -mExtraLayoutSpace);
}
}
@@ -1799,16 +1819,16 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
void slideIn() {
- if (mIsSlidingChildViews) {
- mIsSlidingChildViews = false;
+ if ((mFlag & PF_SLIDING) != 0) {
+ mFlag &= ~PF_SLIDING;
if (mFocusPosition >= 0) {
scrollToSelection(mFocusPosition, mSubFocusPosition, true, mPrimaryScrollExtra);
} else {
- mLayoutEatenInSliding = false;
+ mFlag &= ~PF_LAYOUT_EATEN_IN_SLIDING;
requestLayout();
}
- if (mLayoutEatenInSliding) {
- mLayoutEatenInSliding = false;
+ if ((mFlag & PF_LAYOUT_EATEN_IN_SLIDING) != 0) {
+ mFlag &= ~PF_LAYOUT_EATEN_IN_SLIDING;
if (mBaseGridView.getScrollState() != SCROLL_STATE_IDLE || isSmoothScrolling()) {
mBaseGridView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
@@ -1838,7 +1858,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
}
} else {
- if (mReverseFlowPrimary) {
+ if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0) {
distance = getWidth();
if (getChildCount() > 0) {
int start = getChildAt(0).getRight();
@@ -1861,14 +1881,18 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
return distance;
}
+ boolean isSlidingChildViews() {
+ return (mFlag & PF_SLIDING) != 0;
+ }
+
/**
* Temporarily slide out child and block layout and scroll requests.
*/
void slideOut() {
- if (mIsSlidingChildViews) {
+ if ((mFlag & PF_SLIDING) != 0) {
return;
}
- mIsSlidingChildViews = true;
+ mFlag |= PF_SLIDING;
if (getChildCount() == 0) {
return;
}
@@ -1886,13 +1910,13 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
private void appendVisibleItems() {
- mGrid.appendVisibleItems(mReverseFlowPrimary
+ mGrid.appendVisibleItems((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
? -mExtraLayoutSpace - mExtraLayoutSpaceInPreLayout
: mSizePrimary + mExtraLayoutSpace + mExtraLayoutSpaceInPreLayout);
}
private void prependVisibleItems() {
- mGrid.prependVisibleItems(mReverseFlowPrimary
+ mGrid.prependVisibleItems((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
? mSizePrimary + mExtraLayoutSpace + mExtraLayoutSpaceInPreLayout
: -mExtraLayoutSpace - mExtraLayoutSpaceInPreLayout);
}
@@ -1907,6 +1931,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
final int childCount = getChildCount();
int position = mGrid.getFirstVisibleIndex();
int index = 0;
+ mFlag &= ~PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION;
for (; index < childCount; index++, position++) {
View view = getChildAt(index);
// We don't hit fastRelayout() if State.didStructure() is true, but prelayout may add
@@ -1932,6 +1957,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
LayoutParams lp = (LayoutParams) view.getLayoutParams();
if (lp.viewNeedsUpdate()) {
+ mFlag |= PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION;
detachAndScrapView(view, mRecycler);
view = getViewForPosition(position);
addView(view, index);
@@ -1960,7 +1986,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
detachAndScrapView(v, mRecycler);
}
mGrid.invalidateItemsAfter(position);
- if (mPruneChild) {
+ if ((mFlag & PF_PRUNE_CHILD) != 0) {
// in regular prune child mode, we just append items up to edge limit
appendVisibleItems();
if (mFocusPosition >= 0 && mFocusPosition <= savedLastPos) {
@@ -2108,7 +2134,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
Log.v(getTag(), "layoutChildren start numRows " + mNumRows
+ " inPreLayout " + state.isPreLayout()
+ " didStructureChange " + state.didStructureChange()
- + " mForceFullLayout " + mForceFullLayout);
+ + " mForceFullLayout " + ((mFlag & PF_FORCE_FULL_LAYOUT) != 0));
Log.v(getTag(), "width " + getWidth() + " height " + getHeight());
}
@@ -2121,20 +2147,20 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
return;
}
- if (mIsSlidingChildViews) {
+ if ((mFlag & PF_SLIDING) != 0) {
// if there is already children, delay the layout process until slideIn(), if it's
// first time layout children: scroll them offscreen at end of onLayoutChildren()
if (getChildCount() > 0) {
- mLayoutEatenInSliding = true;
+ mFlag |= PF_LAYOUT_EATEN_IN_SLIDING;
return;
}
}
- if (!mLayoutEnabled) {
+ if ((mFlag & PF_LAYOUT_ENABLED) == 0) {
discardLayoutInfo();
removeAndRecycleAllViews(recycler);
return;
}
- mInLayout = true;
+ mFlag = (mFlag & ~PF_STAGE_MASK) | PF_STAGE_LAYOUT;
saveContext(recycler, state);
if (state.isPreLayout()) {
@@ -2172,7 +2198,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
appendVisibleItems();
prependVisibleItems();
}
- mInLayout = false;
+ mFlag &= ~PF_STAGE_MASK;
leaveContext();
if (DEBUG) Log.v(getTag(), "layoutChildren end");
return;
@@ -2206,13 +2232,16 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
deltaSecondary = state.getRemainingScrollHorizontal();
deltaPrimary = state.getRemainingScrollVertical();
}
- if (mInFastRelayout = layoutInit()) {
+ if (layoutInit()) {
+ mFlag |= PF_FAST_RELAYOUT;
// If grid view is empty, we will start from mFocusPosition
mGrid.setStart(mFocusPosition);
fastRelayout();
} else {
+ mFlag &= ~PF_FAST_RELAYOUT;
// layoutInit() has detached all views, so start from scratch
- mInLayoutSearchFocus = hadFocus;
+ mFlag = (mFlag & ~PF_IN_LAYOUT_SEARCH_FOCUS)
+ | (hadFocus ? PF_IN_LAYOUT_SEARCH_FOCUS : 0);
int startFromPosition, endPos;
if (scrollToFocus && (firstVisibleIndex < 0 || mFocusPosition > lastVisibleIndex
|| mFocusPosition < firstVisibleIndex)) {
@@ -2270,27 +2299,30 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
Log.d(getTag(), sw.toString());
}
- if (mRowSecondarySizeRefresh) {
- mRowSecondarySizeRefresh = false;
+ if ((mFlag & PF_ROW_SECONDARY_SIZE_REFRESH) != 0) {
+ mFlag &= ~PF_ROW_SECONDARY_SIZE_REFRESH;
} else {
updateRowSecondarySizeRefresh();
}
- // For fastRelayout, only dispatch event when focus position changes.
- if (mInFastRelayout && (mFocusPosition != savedFocusPos || mSubFocusPosition
- != savedSubFocusPos || findViewByPosition(mFocusPosition) != savedFocusView)) {
+ // For fastRelayout, only dispatch event when focus position changes or selected item
+ // being updated.
+ if ((mFlag & PF_FAST_RELAYOUT) != 0 && (mFocusPosition != savedFocusPos || mSubFocusPosition
+ != savedSubFocusPos || findViewByPosition(mFocusPosition) != savedFocusView
+ || (mFlag & PF_FAST_RELAYOUT_UPDATED_SELECTED_POSITION) != 0)) {
dispatchChildSelected();
- } else if (!mInFastRelayout && mInLayoutSearchFocus) {
+ } else if ((mFlag & (PF_FAST_RELAYOUT | PF_IN_LAYOUT_SEARCH_FOCUS))
+ == PF_IN_LAYOUT_SEARCH_FOCUS) {
// For full layout we dispatchChildSelected() in createItem() unless searched all
// children and found none is focusable then dispatchChildSelected() here.
dispatchChildSelected();
}
dispatchChildSelectedAndPositioned();
- if (mIsSlidingChildViews) {
+ if ((mFlag & PF_SLIDING) != 0) {
scrollDirectionPrimary(getSlideOutDistance());
}
- mInLayout = false;
+ mFlag &= ~PF_STAGE_MASK;
leaveContext();
if (DEBUG) Log.v(getTag(), "layoutChildren end");
}
@@ -2324,11 +2356,11 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
@Override
public int scrollHorizontallyBy(int dx, Recycler recycler, RecyclerView.State state) {
if (DEBUG) Log.v(getTag(), "scrollHorizontallyBy " + dx);
- if (!mLayoutEnabled || !hasDoneFirstLayout()) {
+ if ((mFlag & PF_LAYOUT_ENABLED) == 0 || !hasDoneFirstLayout()) {
return 0;
}
saveContext(recycler, state);
- mInScroll = true;
+ mFlag = (mFlag & ~PF_STAGE_MASK) | PF_STAGE_SCROLL;
int result;
if (mOrientation == HORIZONTAL) {
result = scrollDirectionPrimary(dx);
@@ -2336,17 +2368,17 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
result = scrollDirectionSecondary(dx);
}
leaveContext();
- mInScroll = false;
+ mFlag &= ~PF_STAGE_MASK;
return result;
}
@Override
public int scrollVerticallyBy(int dy, Recycler recycler, RecyclerView.State state) {
if (DEBUG) Log.v(getTag(), "scrollVerticallyBy " + dy);
- if (!mLayoutEnabled || !hasDoneFirstLayout()) {
+ if ((mFlag & PF_LAYOUT_ENABLED) == 0 || !hasDoneFirstLayout()) {
return 0;
}
- mInScroll = true;
+ mFlag = (mFlag & ~PF_STAGE_MASK) | PF_STAGE_SCROLL;
saveContext(recycler, state);
int result;
if (mOrientation == VERTICAL) {
@@ -2355,7 +2387,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
result = scrollDirectionSecondary(dy);
}
leaveContext();
- mInScroll = false;
+ mFlag &= ~PF_STAGE_MASK;
return result;
}
@@ -2367,7 +2399,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
// 2. During onLayoutChildren(), it may compensate the remaining scroll delta,
// we should honor the request regardless if it goes over minScroll / maxScroll.
// (see b/64931938 testScrollAndRemove and testScrollAndRemoveSample1)
- if (!mIsSlidingChildViews && !mInLayout) {
+ if ((mFlag & PF_SLIDING) == 0 && (mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT) {
if (da > 0) {
if (!mWindowAlignment.mainAxis().isMaxUnknown()) {
int maxScroll = mWindowAlignment.mainAxis().getMaxScroll();
@@ -2389,7 +2421,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
return 0;
}
offsetChildrenPrimary(-da);
- if (mInLayout) {
+ if ((mFlag & PF_STAGE_MASK) == PF_STAGE_LAYOUT) {
updateScrollLimits();
if (TRACE) TraceCompat.endSection();
return da;
@@ -2398,7 +2430,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
int childCount = getChildCount();
boolean updated;
- if (mReverseFlowPrimary ? da > 0 : da < 0) {
+ if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0 ? da > 0 : da < 0) {
prependVisibleItems();
} else {
appendVisibleItems();
@@ -2407,7 +2439,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
childCount = getChildCount();
if (TRACE) TraceCompat.beginSection("remove");
- if (mReverseFlowPrimary ? da > 0 : da < 0) {
+ if ((mFlag & PF_REVERSE_FLOW_PRIMARY) != 0 ? da > 0 : da < 0) {
removeInvisibleViewsAtEnd();
} else {
removeInvisibleViewsAtFront();
@@ -2476,7 +2508,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
int highVisiblePos, lowVisiblePos;
int highMaxPos, lowMinPos;
- if (!mReverseFlowPrimary) {
+ if ((mFlag & PF_REVERSE_FLOW_PRIMARY) == 0) {
highVisiblePos = mGrid.getLastVisibleIndex();
highMaxPos = mState.getItemCount() - 1;
lowVisiblePos = mGrid.getFirstVisibleIndex();
@@ -2614,14 +2646,14 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
// scrollToView() is based on Adapter position. Only call scrollToView() when item
// is still valid.
if (view != null && getAdapterPositionByView(view) == position) {
- mInSelection = true;
+ mFlag |= PF_IN_SELECTION;
scrollToView(view, smooth);
- mInSelection = false;
+ mFlag &= ~PF_IN_SELECTION;
} else {
mFocusPosition = position;
mSubFocusPosition = subposition;
mFocusPositionOffset = Integer.MIN_VALUE;
- if (!mLayoutEnabled || mIsSlidingChildViews) {
+ if ((mFlag & PF_LAYOUT_ENABLED) == 0 || (mFlag & PF_SLIDING) != 0) {
return;
}
if (smooth) {
@@ -2637,7 +2669,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mSubFocusPosition = 0;
}
} else {
- mForceFullLayout = true;
+ mFlag |= PF_FORCE_FULL_LAYOUT;
requestLayout();
}
}
@@ -2654,7 +2686,8 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
final int firstChildPos = getPosition(getChildAt(0));
// TODO We should be able to deduce direction from bounds of current and target
// focus, rather than making assumptions about positions and directionality
- final boolean isStart = mReverseFlowPrimary ? targetPosition > firstChildPos
+ final boolean isStart = (mFlag & PF_REVERSE_FLOW_PRIMARY) != 0
+ ? targetPosition > firstChildPos
: targetPosition < firstChildPos;
final int direction = isStart ? -1 : 1;
if (mOrientation == HORIZONTAL) {
@@ -2788,14 +2821,14 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
@Override
public boolean onRequestChildFocus(RecyclerView parent, View child, View focused) {
- if (mFocusSearchDisabled) {
+ if ((mFlag & PF_FOCUS_SEARCH_DISABLED) != 0) {
return true;
}
if (getAdapterPositionByView(child) == NO_POSITION) {
// This is could be the last view in DISAPPEARING animation.
return true;
}
- if (!mInLayout && !mInSelection && !mInScroll) {
+ if ((mFlag & (PF_STAGE_MASK | PF_IN_SELECTION)) == 0) {
scrollToView(child, focused, true);
}
return true;
@@ -2865,7 +2898,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
*/
private void scrollToView(View view, View childView, boolean smooth, int extraDelta,
int extraDeltaSecondary) {
- if (mIsSlidingChildViews) {
+ if ((mFlag & PF_SLIDING) != 0) {
return;
}
int newFocusPosition = getAdapterPositionByView(view);
@@ -2874,7 +2907,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mFocusPosition = newFocusPosition;
mSubFocusPosition = newSubFocusPosition;
mFocusPositionOffset = 0;
- if (!mInLayout) {
+ if ((mFlag & PF_STAGE_MASK) != PF_STAGE_LAYOUT) {
dispatchChildSelected();
}
if (mBaseGridView.isChildrenDrawingOrderEnabledInternal()) {
@@ -2889,7 +2922,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
// by setSelection())
view.requestFocus();
}
- if (!mScrollEnabled && smooth) {
+ if ((mFlag & PF_SCROLL_ENABLED) == 0 && smooth) {
return;
}
if (getScrollPosition(view, childView, sTwoInts)
@@ -3007,7 +3040,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
private void scrollGrid(int scrollPrimary, int scrollSecondary, boolean smooth) {
- if (mInLayout) {
+ if ((mFlag & PF_STAGE_MASK) == PF_STAGE_LAYOUT) {
scrollDirectionPrimary(scrollPrimary);
scrollDirectionSecondary(scrollSecondary);
} else {
@@ -3030,22 +3063,23 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
public void setPruneChild(boolean pruneChild) {
- if (mPruneChild != pruneChild) {
- mPruneChild = pruneChild;
- if (mPruneChild) {
+ if (((mFlag & PF_PRUNE_CHILD) != 0) != pruneChild) {
+ mFlag = (mFlag & ~PF_PRUNE_CHILD) | (pruneChild ? PF_PRUNE_CHILD : 0);
+ if (pruneChild) {
requestLayout();
}
}
}
public boolean getPruneChild() {
- return mPruneChild;
+ return (mFlag & PF_PRUNE_CHILD) != 0;
}
public void setScrollEnabled(boolean scrollEnabled) {
- if (mScrollEnabled != scrollEnabled) {
- mScrollEnabled = scrollEnabled;
- if (mScrollEnabled && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED
+ if (((mFlag & PF_SCROLL_ENABLED) != 0) != scrollEnabled) {
+ mFlag = (mFlag & ~PF_SCROLL_ENABLED) | (scrollEnabled ? PF_SCROLL_ENABLED : 0);
+ if (((mFlag & PF_SCROLL_ENABLED) != 0)
+ && mFocusScrollStrategy == BaseGridView.FOCUS_SCROLL_ALIGNED
&& mFocusPosition != NO_POSITION) {
scrollToSelection(mFocusPosition, mSubFocusPosition,
true, mPrimaryScrollExtra);
@@ -3054,7 +3088,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
public boolean isScrollEnabled() {
- return mScrollEnabled;
+ return (mFlag & PF_SCROLL_ENABLED) != 0;
}
private int findImmediateChildIndex(View view) {
@@ -3088,16 +3122,16 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
}
void setFocusSearchDisabled(boolean disabled) {
- mFocusSearchDisabled = disabled;
+ mFlag = (mFlag & ~PF_FOCUS_SEARCH_DISABLED) | (disabled ? PF_FOCUS_SEARCH_DISABLED : 0);
}
boolean isFocusSearchDisabled() {
- return mFocusSearchDisabled;
+ return (mFlag & PF_FOCUS_SEARCH_DISABLED) != 0;
}
@Override
public View onInterceptFocusSearch(View focused, int direction) {
- if (mFocusSearchDisabled) {
+ if ((mFlag & PF_FOCUS_SEARCH_DISABLED) != 0) {
return focused;
}
@@ -3132,27 +3166,27 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
int movement = getMovement(direction);
final boolean isScroll = mBaseGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE;
if (movement == NEXT_ITEM) {
- if (isScroll || !mFocusOutEnd) {
+ if (isScroll || (mFlag & PF_FOCUS_OUT_END) == 0) {
result = focused;
}
- if (mScrollEnabled && !hasCreatedLastItem()) {
+ if ((mFlag & PF_SCROLL_ENABLED) != 0 && !hasCreatedLastItem()) {
processPendingMovement(true);
result = focused;
}
} else if (movement == PREV_ITEM) {
- if (isScroll || !mFocusOutFront) {
+ if (isScroll || (mFlag & PF_FOCUS_OUT_FRONT) == 0) {
result = focused;
}
- if (mScrollEnabled && !hasCreatedFirstItem()) {
+ if ((mFlag & PF_SCROLL_ENABLED) != 0 && !hasCreatedFirstItem()) {
processPendingMovement(false);
result = focused;
}
} else if (movement == NEXT_ROW) {
- if (isScroll || !mFocusOutSideEnd) {
+ if (isScroll || (mFlag & PF_FOCUS_OUT_SIDE_END) == 0) {
result = focused;
}
} else if (movement == PREV_ROW) {
- if (isScroll || !mFocusOutSideStart) {
+ if (isScroll || (mFlag & PF_FOCUS_OUT_SIDE_START) == 0) {
result = focused;
}
}
@@ -3191,7 +3225,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
@Override
public boolean onAddFocusables(RecyclerView recyclerView,
ArrayList<View> views, int direction, int focusableMode) {
- if (mFocusSearchDisabled) {
+ if ((mFlag & PF_FOCUS_SEARCH_DISABLED) != 0) {
return true;
}
// If this viewgroup or one of its children currently has focus then we
@@ -3423,10 +3457,10 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
if (mOrientation == HORIZONTAL) {
switch(direction) {
case View.FOCUS_LEFT:
- movement = (!mReverseFlowPrimary) ? PREV_ITEM : NEXT_ITEM;
+ movement = (mFlag & PF_REVERSE_FLOW_PRIMARY) == 0 ? PREV_ITEM : NEXT_ITEM;
break;
case View.FOCUS_RIGHT:
- movement = (!mReverseFlowPrimary) ? NEXT_ITEM : PREV_ITEM;
+ movement = (mFlag & PF_REVERSE_FLOW_PRIMARY) == 0 ? NEXT_ITEM : PREV_ITEM;
break;
case View.FOCUS_UP:
movement = PREV_ROW;
@@ -3438,10 +3472,10 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
} else if (mOrientation == VERTICAL) {
switch(direction) {
case View.FOCUS_LEFT:
- movement = (!mReverseFlowSecondary) ? PREV_ROW : NEXT_ROW;
+ movement = (mFlag & PF_REVERSE_FLOW_SECONDARY) == 0 ? PREV_ROW : NEXT_ROW;
break;
case View.FOCUS_RIGHT:
- movement = (!mReverseFlowSecondary) ? NEXT_ROW : PREV_ROW;
+ movement = (mFlag & PF_REVERSE_FLOW_SECONDARY) == 0 ? NEXT_ROW : PREV_ROW;
break;
case View.FOCUS_UP:
movement = PREV_ITEM;
@@ -3497,12 +3531,12 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
private void discardLayoutInfo() {
mGrid = null;
mRowSizeSecondary = null;
- mRowSecondarySizeRefresh = false;
+ mFlag &= ~PF_ROW_SECONDARY_SIZE_REFRESH;
}
public void setLayoutEnabled(boolean layoutEnabled) {
- if (mLayoutEnabled != layoutEnabled) {
- mLayoutEnabled = layoutEnabled;
+ if (((mFlag & PF_LAYOUT_ENABLED) != 0) != layoutEnabled) {
+ mFlag = (mFlag & ~PF_LAYOUT_ENABLED) | (layoutEnabled ? PF_LAYOUT_ENABLED : 0);
requestLayout();
}
}
@@ -3592,7 +3626,7 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
mFocusPosition = loadingState.index;
mFocusPositionOffset = 0;
mChildrenStates.loadFromBundle(loadingState.childStates);
- mForceFullLayout = true;
+ mFlag |= PF_FORCE_FULL_LAYOUT;
requestLayout();
if (DEBUG) Log.v(getTag(), "onRestoreInstanceState mFocusPosition " + mFocusPosition);
}
@@ -3699,9 +3733,9 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
if (newSelected != null) {
if (preventScroll) {
if (hasFocus()) {
- mInSelection = true;
+ mFlag |= PF_IN_SELECTION;
newSelected.requestFocus();
- mInSelection = false;
+ mFlag &= ~PF_IN_SELECTION;
}
mFocusPosition = focusPosition;
mSubFocusPosition = 0;
@@ -3717,11 +3751,11 @@ final class GridLayoutManager extends RecyclerView.LayoutManager {
AccessibilityNodeInfoCompat info) {
saveContext(recycler, state);
int count = state.getItemCount();
- if (mScrollEnabled && count > 1 && !isItemFullyVisible(0)) {
+ if ((mFlag & PF_SCROLL_ENABLED) != 0 && count > 1 && !isItemFullyVisible(0)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
info.setScrollable(true);
}
- if (mScrollEnabled && count > 1 && !isItemFullyVisible(count - 1)) {
+ if ((mFlag & PF_SCROLL_ENABLED) != 0 && count > 1 && !isItemFullyVisible(count - 1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
info.setScrollable(true);
}
diff --git a/android/support/v17/leanback/widget/GuidedActionAdapter.java b/android/support/v17/leanback/widget/GuidedActionAdapter.java
index 5b755f5e..51b29e21 100644
--- a/android/support/v17/leanback/widget/GuidedActionAdapter.java
+++ b/android/support/v17/leanback/widget/GuidedActionAdapter.java
@@ -15,7 +15,9 @@ package android.support.v17.leanback.widget;
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
+import android.support.v7.util.DiffUtil;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.Log;
@@ -103,6 +105,7 @@ public class GuidedActionAdapter extends RecyclerView.Adapter {
private ClickListener mClickListener;
final GuidedActionsStylist mStylist;
GuidedActionAdapterGroup mGroup;
+ DiffCallback<GuidedAction> mDiffCallback;
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
@@ -145,20 +148,78 @@ public class GuidedActionAdapter extends RecyclerView.Adapter {
mActionOnFocusListener = new ActionOnFocusListener(focusListener);
mActionEditListener = new ActionEditListener();
mIsSubAdapter = isSubAdapter;
+ if (!isSubAdapter) {
+ mDiffCallback = GuidedActionDiffCallback.getInstance();
+ }
+ }
+
+ /**
+ * Change DiffCallback used in {@link #setActions(List)}. Set to null for firing a
+ * general {@link #notifyDataSetChanged()}.
+ *
+ * @param diffCallback
+ */
+ public void setDiffCallback(DiffCallback<GuidedAction> diffCallback) {
+ mDiffCallback = diffCallback;
}
/**
- * Sets the list of actions managed by this adapter.
+ * Sets the list of actions managed by this adapter. Use {@link #setDiffCallback(DiffCallback)}
+ * to change DiffCallback.
* @param actions The list of actions to be managed.
*/
- public void setActions(List<GuidedAction> actions) {
+ public void setActions(final List<GuidedAction> actions) {
if (!mIsSubAdapter) {
mStylist.collapseAction(false);
}
mActionOnFocusListener.unFocus();
- mActions.clear();
- mActions.addAll(actions);
- notifyDataSetChanged();
+ if (mDiffCallback != null) {
+ // temporary variable used for DiffCallback
+ final List<GuidedAction> oldActions = new ArrayList();
+ oldActions.addAll(mActions);
+
+ // update items.
+ mActions.clear();
+ mActions.addAll(actions);
+
+ DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
+ @Override
+ public int getOldListSize() {
+ return oldActions.size();
+ }
+
+ @Override
+ public int getNewListSize() {
+ return mActions.size();
+ }
+
+ @Override
+ public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
+ return mDiffCallback.areItemsTheSame(oldActions.get(oldItemPosition),
+ mActions.get(newItemPosition));
+ }
+
+ @Override
+ public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
+ return mDiffCallback.areContentsTheSame(oldActions.get(oldItemPosition),
+ mActions.get(newItemPosition));
+ }
+
+ @Nullable
+ @Override
+ public Object getChangePayload(int oldItemPosition, int newItemPosition) {
+ return mDiffCallback.getChangePayload(oldActions.get(oldItemPosition),
+ mActions.get(newItemPosition));
+ }
+ });
+
+ // dispatch diff result
+ diffResult.dispatchUpdatesTo(this);
+ } else {
+ mActions.clear();
+ mActions.addAll(actions);
+ notifyDataSetChanged();
+ }
}
/**
diff --git a/android/support/v17/leanback/widget/GuidedActionDiffCallback.java b/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
new file mode 100644
index 00000000..d4d4d77a
--- /dev/null
+++ b/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 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.support.annotation.NonNull;
+import android.text.TextUtils;
+
+/**
+ * DiffCallback used for GuidedActions, see {@link
+ * android.support.v17.leanback.app.GuidedStepSupportFragment#setActionsDiffCallback(DiffCallback)}.
+ */
+public class GuidedActionDiffCallback extends DiffCallback<GuidedAction> {
+
+ static final GuidedActionDiffCallback sInstance = new GuidedActionDiffCallback();
+
+ /**
+ * Returns the singleton GuidedActionDiffCallback.
+ * @return The singleton GuidedActionDiffCallback.
+ */
+ public static final GuidedActionDiffCallback getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public boolean areItemsTheSame(@NonNull GuidedAction oldItem, @NonNull GuidedAction newItem) {
+ if (oldItem == null) {
+ return newItem == null;
+ } else if (newItem == null) {
+ return false;
+ }
+ return oldItem.getId() == newItem.getId();
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull GuidedAction oldItem,
+ @NonNull GuidedAction newItem) {
+ if (oldItem == null) {
+ return newItem == null;
+ } else if (newItem == null) {
+ return false;
+ }
+ return oldItem.getCheckSetId() == newItem.getCheckSetId()
+ && oldItem.mActionFlags == newItem.mActionFlags
+ && TextUtils.equals(oldItem.getTitle(), newItem.getTitle())
+ && TextUtils.equals(oldItem.getDescription(), newItem.getDescription())
+ && oldItem.getInputType() == newItem.getInputType()
+ && TextUtils.equals(oldItem.getEditTitle(), newItem.getEditTitle())
+ && TextUtils.equals(oldItem.getEditDescription(), newItem.getEditDescription())
+ && oldItem.getEditInputType() == newItem.getEditInputType()
+ && oldItem.getDescriptionEditInputType() == newItem.getDescriptionEditInputType();
+ }
+}
diff --git a/android/support/v17/leanback/widget/ObjectAdapter.java b/android/support/v17/leanback/widget/ObjectAdapter.java
index 535f81b4..d411f9e7 100644
--- a/android/support/v17/leanback/widget/ObjectAdapter.java
+++ b/android/support/v17/leanback/widget/ObjectAdapter.java
@@ -13,7 +13,10 @@
*/
package android.support.v17.leanback.widget;
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
import android.database.Observable;
+import android.support.annotation.RestrictTo;
/**
* Base class adapter to be used in leanback activities. Provides access to a data model and is
@@ -132,6 +135,10 @@ public abstract class ObjectAdapter {
mObservers.get(i).onItemMoved(positionStart, toPosition);
}
}
+
+ boolean hasObserver() {
+ return mObservers.size() > 0;
+ }
}
private final DataObservable mObservable = new DataObservable();
@@ -207,6 +214,14 @@ public abstract class ObjectAdapter {
}
/**
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public final boolean hasObserver() {
+ return mObservable.hasObserver();
+ }
+
+ /**
* Unregisters all DataObservers for this ObjectAdapter.
*/
public final void unregisterAllObservers() {
diff --git a/android/support/v4/app/FragmentActivity.java b/android/support/v4/app/FragmentActivity.java
index 614ff351..78161a87 100644
--- a/android/support/v4/app/FragmentActivity.java
+++ b/android/support/v4/app/FragmentActivity.java
@@ -536,7 +536,7 @@ public class FragmentActivity extends BaseFragmentActivityApi16 implements
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- markState(getSupportFragmentManager(), Lifecycle.State.CREATED);
+ markFragmentsCreated();
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
@@ -591,7 +591,7 @@ public class FragmentActivity extends BaseFragmentActivityApi16 implements
super.onStop();
mStopped = true;
- markState(getSupportFragmentManager(), Lifecycle.State.CREATED);
+ markFragmentsCreated();
mHandler.sendEmptyMessage(MSG_REALLY_STOPPED);
mFragments.dispatchStop();
@@ -970,18 +970,30 @@ public class FragmentActivity extends BaseFragmentActivityApi16 implements
}
}
- private static void markState(FragmentManager manager, Lifecycle.State state) {
+ private void markFragmentsCreated() {
+ boolean reiterate;
+ do {
+ reiterate = markState(getSupportFragmentManager(), Lifecycle.State.CREATED);
+ } while (reiterate);
+ }
+
+ private static boolean markState(FragmentManager manager, Lifecycle.State state) {
+ boolean hadNotMarked = false;
Collection<Fragment> fragments = manager.getFragments();
for (Fragment fragment : fragments) {
if (fragment == null) {
continue;
}
- fragment.mLifecycleRegistry.markState(state);
+ if (fragment.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
+ fragment.mLifecycleRegistry.markState(state);
+ hadNotMarked = true;
+ }
FragmentManager childFragmentManager = fragment.peekChildFragmentManager();
if (childFragmentManager != null) {
- markState(childFragmentManager, state);
+ hadNotMarked |= markState(childFragmentManager, state);
}
}
+ return hadNotMarked;
}
}
diff --git a/android/support/v4/graphics/TypefaceCompat.java b/android/support/v4/graphics/TypefaceCompat.java
index 3c55df62..734f1837 100644
--- a/android/support/v4/graphics/TypefaceCompat.java
+++ b/android/support/v4/graphics/TypefaceCompat.java
@@ -35,7 +35,6 @@ import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.provider.FontsContractCompat;
import android.support.v4.provider.FontsContractCompat.FontInfo;
import android.support.v4.util.LruCache;
-
/**
* Helper for accessing features in {@link Typeface}.
* @hide
@@ -46,7 +45,9 @@ public class TypefaceCompat {
private static final TypefaceCompatImpl sTypefaceCompatImpl;
static {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ sTypefaceCompatImpl = new TypefaceCompatApi28Impl();
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
sTypefaceCompatImpl = new TypefaceCompatApi26Impl();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
&& TypefaceCompatApi24Impl.isUsable()) {
diff --git a/android/support/v4/graphics/TypefaceCompatApi26Impl.java b/android/support/v4/graphics/TypefaceCompatApi26Impl.java
index 1b55a2e0..00e31a1a 100644
--- a/android/support/v4/graphics/TypefaceCompatApi26Impl.java
+++ b/android/support/v4/graphics/TypefaceCompatApi26Impl.java
@@ -60,76 +60,69 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
"createFromFamiliesWithDefault";
private static final String FREEZE_METHOD = "freeze";
private static final String ABORT_CREATION_METHOD = "abortCreation";
- private static final Class sFontFamily;
- private static final Constructor sFontFamilyCtor;
- private static final Method sAddFontFromAssetManager;
- private static final Method sAddFontFromBuffer;
- private static final Method sFreeze;
- private static final Method sAbortCreation;
- private static final Method sCreateFromFamiliesWithDefault;
private static final int RESOLVE_BY_FONT_TABLE = -1;
- static {
- Class fontFamilyClass;
+ protected final Class mFontFamily;
+ protected final Constructor mFontFamilyCtor;
+ protected final Method mAddFontFromAssetManager;
+ protected final Method mAddFontFromBuffer;
+ protected final Method mFreeze;
+ protected final Method mAbortCreation;
+ protected final Method mCreateFromFamiliesWithDefault;
+
+ public TypefaceCompatApi26Impl() {
+ Class fontFamily;
Constructor fontFamilyCtor;
- Method addFontMethod;
- Method addFromBufferMethod;
- Method freezeMethod;
- Method abortCreationMethod;
- Method createFromFamiliesWithDefaultMethod;
+ Method addFontFromAssetManager;
+ Method addFontFromBuffer;
+ Method freeze;
+ Method abortCreation;
+ Method createFromFamiliesWithDefault;
try {
- fontFamilyClass = Class.forName(FONT_FAMILY_CLASS);
- fontFamilyCtor = fontFamilyClass.getConstructor();
- addFontMethod = fontFamilyClass.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
- AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
- Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
- addFromBufferMethod = fontFamilyClass.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
- ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
- Integer.TYPE);
- freezeMethod = fontFamilyClass.getMethod(FREEZE_METHOD);
- abortCreationMethod = fontFamilyClass.getMethod(ABORT_CREATION_METHOD);
- Object familyArray = Array.newInstance(fontFamilyClass, 1);
- createFromFamiliesWithDefaultMethod =
- Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
- familyArray.getClass(), Integer.TYPE, Integer.TYPE);
- createFromFamiliesWithDefaultMethod.setAccessible(true);
+ fontFamily = obtainFontFamily();
+ fontFamilyCtor = obtainFontFamilyCtor(fontFamily);
+ addFontFromAssetManager = obtainAddFontFromAssetManagerMethod(fontFamily);
+ addFontFromBuffer = obtainAddFontFromBufferMethod(fontFamily);
+ freeze = obtainFreezeMethod(fontFamily);
+ abortCreation = obtainAbortCreationMethod(fontFamily);
+ createFromFamiliesWithDefault = obtainCreateFromFamiliesWithDefaultMethod(fontFamily);
} catch (ClassNotFoundException | NoSuchMethodException e) {
Log.e(TAG, "Unable to collect necessary methods for class " + e.getClass().getName(),
e);
- fontFamilyClass = null;
+ fontFamily = null;
fontFamilyCtor = null;
- addFontMethod = null;
- addFromBufferMethod = null;
- freezeMethod = null;
- abortCreationMethod = null;
- createFromFamiliesWithDefaultMethod = null;
+ addFontFromAssetManager = null;
+ addFontFromBuffer = null;
+ freeze = null;
+ abortCreation = null;
+ createFromFamiliesWithDefault = null;
}
- sFontFamilyCtor = fontFamilyCtor;
- sFontFamily = fontFamilyClass;
- sAddFontFromAssetManager = addFontMethod;
- sAddFontFromBuffer = addFromBufferMethod;
- sFreeze = freezeMethod;
- sAbortCreation = abortCreationMethod;
- sCreateFromFamiliesWithDefault = createFromFamiliesWithDefaultMethod;
+ mFontFamily = fontFamily;
+ mFontFamilyCtor = fontFamilyCtor;
+ mAddFontFromAssetManager = addFontFromAssetManager;
+ mAddFontFromBuffer = addFontFromBuffer;
+ mFreeze = freeze;
+ mAbortCreation = abortCreation;
+ mCreateFromFamiliesWithDefault = createFromFamiliesWithDefault;
}
/**
- * Returns true if API26 implementation is usable.
+ * Returns true if all the necessary methods were found.
*/
- private static boolean isFontFamilyPrivateAPIAvailable() {
- if (sAddFontFromAssetManager == null) {
+ private boolean isFontFamilyPrivateAPIAvailable() {
+ if (mAddFontFromAssetManager == null) {
Log.w(TAG, "Unable to collect necessary private methods. "
+ "Fallback to legacy implementation.");
}
- return sAddFontFromAssetManager != null;
+ return mAddFontFromAssetManager != null;
}
/**
* Create a new FontFamily instance
*/
- private static Object newFamily() {
+ private Object newFamily() {
try {
- return sFontFamilyCtor.newInstance();
+ return mFontFamilyCtor.newInstance();
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
@@ -139,10 +132,10 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
* Call FontFamily#addFontFromAssetManager(AssetManager mgr, String path, int cookie,
* boolean isAsset, int ttcIndex, int weight, int isItalic, FontVariationAxis[] axes)
*/
- private static boolean addFontFromAssetManager(Context context, Object family, String fileName,
+ private boolean addFontFromAssetManager(Context context, Object family, String fileName,
int ttcIndex, int weight, int style) {
try {
- final Boolean result = (Boolean) sAddFontFromAssetManager.invoke(family,
+ final Boolean result = (Boolean) mAddFontFromAssetManager.invoke(family,
context.getAssets(), fileName, 0 /* cookie */, false /* isAsset */, ttcIndex,
weight, style, null /* axes */);
return result.booleanValue();
@@ -155,10 +148,10 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
* Call FontFamily#addFontFromBuffer(ByteBuffer font, int ttcIndex, FontVariationAxis[] axes,
* int weight, int italic)
*/
- private static boolean addFontFromBuffer(Object family, ByteBuffer buffer,
+ private boolean addFontFromBuffer(Object family, ByteBuffer buffer,
int ttcIndex, int weight, int style) {
try {
- final Boolean result = (Boolean) sAddFontFromBuffer.invoke(family,
+ final Boolean result = (Boolean) mAddFontFromBuffer.invoke(family,
buffer, ttcIndex, null /* axes */, weight, style);
return result.booleanValue();
} catch (IllegalAccessException | InvocationTargetException e) {
@@ -167,14 +160,14 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
}
/**
- * Call static method Typeface#createFromFamiliesWithDefault(
+ * Call method Typeface#createFromFamiliesWithDefault(
* FontFamily[] families, int weight, int italic)
*/
- private static Typeface createFromFamiliesWithDefault(Object family) {
+ protected Typeface createFromFamiliesWithDefault(Object family) {
try {
- Object familyArray = Array.newInstance(sFontFamily, 1);
+ Object familyArray = Array.newInstance(mFontFamily, 1);
Array.set(familyArray, 0, family);
- return (Typeface) sCreateFromFamiliesWithDefault.invoke(null /* static method */,
+ return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,
familyArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
@@ -184,9 +177,9 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
/**
* Call FontFamily#freeze()
*/
- private static boolean freeze(Object family) {
+ private boolean freeze(Object family) {
try {
- Boolean result = (Boolean) sFreeze.invoke(family);
+ Boolean result = (Boolean) mFreeze.invoke(family);
return result.booleanValue();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
@@ -196,9 +189,9 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
/**
* Call FontFamily#abortCreation()
*/
- private static boolean abortCreation(Object family) {
+ private boolean abortCreation(Object family) {
try {
- Boolean result = (Boolean) sAbortCreation.invoke(family);
+ Boolean result = (Boolean) mAbortCreation.invoke(family);
return result.booleanValue();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
@@ -299,4 +292,47 @@ public class TypefaceCompatApi26Impl extends TypefaceCompatApi21Impl {
}
return createFromFamiliesWithDefault(fontFamily);
}
+
+ // The following getters retrieve by reflection the Typeface methods, belonging to the
+ // framework code, which will be invoked. Since the definitions of these methods can change
+ // across different API versions, inheriting classes should override these getters in order to
+ // reflect the method definitions in the API versions they represent.
+ //===========================================================================================
+ protected Class obtainFontFamily() throws ClassNotFoundException {
+ return Class.forName(FONT_FAMILY_CLASS);
+ }
+
+ protected Constructor obtainFontFamilyCtor(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getConstructor();
+ }
+
+ protected Method obtainAddFontFromAssetManagerMethod(Class fontFamily)
+ throws NoSuchMethodException {
+ return fontFamily.getMethod(ADD_FONT_FROM_ASSET_MANAGER_METHOD,
+ AssetManager.class, String.class, Integer.TYPE, Boolean.TYPE, Integer.TYPE,
+ Integer.TYPE, Integer.TYPE, FontVariationAxis[].class);
+ }
+
+ protected Method obtainAddFontFromBufferMethod(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getMethod(ADD_FONT_FROM_BUFFER_METHOD,
+ ByteBuffer.class, Integer.TYPE, FontVariationAxis[].class, Integer.TYPE,
+ Integer.TYPE);
+ }
+
+ protected Method obtainFreezeMethod(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getMethod(FREEZE_METHOD);
+ }
+
+ protected Method obtainAbortCreationMethod(Class fontFamily) throws NoSuchMethodException {
+ return fontFamily.getMethod(ABORT_CREATION_METHOD);
+ }
+
+ protected Method obtainCreateFromFamiliesWithDefaultMethod(Class fontFamily)
+ throws NoSuchMethodException {
+ Object familyArray = Array.newInstance(fontFamily, 1);
+ Method m = Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+ familyArray.getClass(), Integer.TYPE, Integer.TYPE);
+ m.setAccessible(true);
+ return m;
+ }
}
diff --git a/android/support/v4/graphics/TypefaceCompatApi28Impl.java b/android/support/v4/graphics/TypefaceCompatApi28Impl.java
new file mode 100644
index 00000000..baa2ce67
--- /dev/null
+++ b/android/support/v4/graphics/TypefaceCompatApi28Impl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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.v4.graphics;
+
+import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.graphics.Typeface;
+import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Implementation of the Typeface compat methods for API 28 and above.
+ * @hide
+ */
+@RestrictTo(LIBRARY_GROUP)
+@RequiresApi(28)
+public class TypefaceCompatApi28Impl extends TypefaceCompatApi26Impl {
+ private static final String TAG = "TypefaceCompatApi28Impl";
+
+ private static final String CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD =
+ "createFromFamiliesWithDefault";
+ private static final int RESOLVE_BY_FONT_TABLE = -1;
+ private static final String DEFAULT_FAMILY = "sans-serif";
+
+ /**
+ * Call method Typeface#createFromFamiliesWithDefault(
+ * FontFamily[] families, String fallbackName, int weight, int italic)
+ */
+ @Override
+ protected Typeface createFromFamiliesWithDefault(Object family) {
+ try {
+ Object familyArray = Array.newInstance(mFontFamily, 1);
+ Array.set(familyArray, 0, family);
+ return (Typeface) mCreateFromFamiliesWithDefault.invoke(null /* static method */,
+ familyArray, DEFAULT_FAMILY, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected Method obtainCreateFromFamiliesWithDefaultMethod(Class fontFamily)
+ throws NoSuchMethodException {
+ Object familyArray = Array.newInstance(fontFamily, 1);
+ Method m = Typeface.class.getDeclaredMethod(CREATE_FROM_FAMILIES_WITH_DEFAULT_METHOD,
+ familyArray.getClass(), String.class, Integer.TYPE, Integer.TYPE);
+ m.setAccessible(true);
+ return m;
+ }
+}
diff --git a/android/support/v4/media/MediaBrowserCompat.java b/android/support/v4/media/MediaBrowserCompat.java
index 85f5a512..7adf7d78 100644
--- a/android/support/v4/media/MediaBrowserCompat.java
+++ b/android/support/v4/media/MediaBrowserCompat.java
@@ -676,17 +676,15 @@ public final class MediaBrowserCompat {
WeakReference<Subscription> mSubscriptionRef;
public SubscriptionCallback() {
+ mToken = new Binder();
if (Build.VERSION.SDK_INT >= 26) {
mSubscriptionCallbackObj =
MediaBrowserCompatApi26.createSubscriptionCallback(new StubApi26());
- mToken = null;
} else if (Build.VERSION.SDK_INT >= 21) {
mSubscriptionCallbackObj =
MediaBrowserCompatApi21.createSubscriptionCallback(new StubApi21());
- mToken = new Binder();
} else {
mSubscriptionCallbackObj = null;
- mToken = new Binder();
}
}
@@ -1958,22 +1956,30 @@ public final class MediaBrowserCompat {
@Override
public void subscribe(@NonNull String parentId, @Nullable Bundle options,
@NonNull SubscriptionCallback callback) {
- if (options == null) {
- MediaBrowserCompatApi21.subscribe(
- mBrowserObj, parentId, callback.mSubscriptionCallbackObj);
+ if (mServiceBinderWrapper == null) {
+ if (options == null) {
+ MediaBrowserCompatApi21.subscribe(
+ mBrowserObj, parentId, callback.mSubscriptionCallbackObj);
+ } else {
+ MediaBrowserCompatApi26.subscribe(
+ mBrowserObj, parentId, options, callback.mSubscriptionCallbackObj);
+ }
} else {
- MediaBrowserCompatApi26.subscribe(
- mBrowserObj, parentId, options, callback.mSubscriptionCallbackObj);
+ super.subscribe(parentId, options, callback);
}
}
@Override
public void unsubscribe(@NonNull String parentId, SubscriptionCallback callback) {
- if (callback == null) {
- MediaBrowserCompatApi21.unsubscribe(mBrowserObj, parentId);
+ if (mServiceBinderWrapper == null) {
+ if (callback == null) {
+ MediaBrowserCompatApi21.unsubscribe(mBrowserObj, parentId);
+ } else {
+ MediaBrowserCompatApi26.unsubscribe(mBrowserObj, parentId,
+ callback.mSubscriptionCallbackObj);
+ }
} else {
- MediaBrowserCompatApi26.unsubscribe(mBrowserObj, parentId,
- callback.mSubscriptionCallbackObj);
+ super.unsubscribe(parentId, callback);
}
}
}
diff --git a/android/support/v4/media/MediaBrowserServiceCompat.java b/android/support/v4/media/MediaBrowserServiceCompat.java
index 53b111ab..debc66e8 100644
--- a/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -422,11 +422,15 @@ public abstract class MediaBrowserServiceCompat extends Service {
@Override
public void notifyChildrenChanged(final String parentId, final Bundle options) {
- if (options == null) {
- MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
+ if (mMessenger == null) {
+ if (options == null) {
+ MediaBrowserServiceCompatApi21.notifyChildrenChanged(mServiceObj, parentId);
+ } else {
+ MediaBrowserServiceCompatApi26.notifyChildrenChanged(mServiceObj, parentId,
+ options);
+ }
} else {
- MediaBrowserServiceCompatApi26.notifyChildrenChanged(mServiceObj, parentId,
- options);
+ super.notifyChildrenChanged(parentId, options);
}
}
diff --git a/android/support/v4/media/MediaMetadataCompat.java b/android/support/v4/media/MediaMetadataCompat.java
index 3ddf255c..00f16cb3 100644
--- a/android/support/v4/media/MediaMetadataCompat.java
+++ b/android/support/v4/media/MediaMetadataCompat.java
@@ -365,10 +365,12 @@ public final class MediaMetadataCompat implements Parcelable {
MediaMetadataCompat(Bundle bundle) {
mBundle = new Bundle(bundle);
+ mBundle.setClassLoader(MediaMetadataCompat.class.getClassLoader());
}
MediaMetadataCompat(Parcel in) {
mBundle = in.readBundle();
+ mBundle.setClassLoader(MediaMetadataCompat.class.getClassLoader());
}
/**
diff --git a/android/support/v4/view/ViewCompat.java b/android/support/v4/view/ViewCompat.java
index 34a198a1..204a1218 100644
--- a/android/support/v4/view/ViewCompat.java
+++ b/android/support/v4/view/ViewCompat.java
@@ -1356,7 +1356,7 @@ public class ViewCompat {
// after applying the tint
Drawable background = view.getBackground();
boolean hasTint = (view.getBackgroundTintList() != null)
- && (view.getBackgroundTintMode() != null);
+ || (view.getBackgroundTintMode() != null);
if ((background != null) && hasTint) {
if (background.isStateful()) {
background.setState(view.getDrawableState());
@@ -1375,7 +1375,7 @@ public class ViewCompat {
// after applying the tint
Drawable background = view.getBackground();
boolean hasTint = (view.getBackgroundTintList() != null)
- && (view.getBackgroundTintMode() != null);
+ || (view.getBackgroundTintMode() != null);
if ((background != null) && hasTint) {
if (background.isStateful()) {
background.setState(view.getDrawableState());
diff --git a/android/support/v7/app/AppCompatDelegateImplV9.java b/android/support/v7/app/AppCompatDelegateImplV9.java
index 056e33e3..5b53401c 100644
--- a/android/support/v7/app/AppCompatDelegateImplV9.java
+++ b/android/support/v7/app/AppCompatDelegateImplV9.java
@@ -1001,7 +1001,26 @@ class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
- mAppCompatViewInflater = new AppCompatViewInflater();
+ TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
+ String viewInflaterClassName =
+ a.getString(R.styleable.AppCompatTheme_viewInflaterClass);
+ if ((viewInflaterClassName == null)
+ || AppCompatViewInflater.class.getName().equals(viewInflaterClassName)) {
+ // Either default class name or set explicitly to null. In both cases
+ // create the base inflater (no reflection)
+ mAppCompatViewInflater = new AppCompatViewInflater();
+ } else {
+ try {
+ Class viewInflaterClass = Class.forName(viewInflaterClassName);
+ mAppCompatViewInflater =
+ (AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor()
+ .newInstance();
+ } catch (Throwable t) {
+ Log.i(TAG, "Failed to instantiate custom view inflater "
+ + viewInflaterClassName + ". Falling back to default.", t);
+ mAppCompatViewInflater = new AppCompatViewInflater();
+ }
+ }
}
boolean inheritContext = false;
diff --git a/android/support/v7/app/AppCompatViewInflater.java b/android/support/v7/app/AppCompatViewInflater.java
index 54d01bce..87a1a3c7 100644
--- a/android/support/v7/app/AppCompatViewInflater.java
+++ b/android/support/v7/app/AppCompatViewInflater.java
@@ -51,14 +51,12 @@ import java.lang.reflect.Method;
import java.util.Map;
/**
- * This class is responsible for manually inflating our tinted widgets which are used on devices
- * running {@link android.os.Build.VERSION_CODES#KITKAT KITKAT} or below. As such, this class
- * should only be used when running on those devices.
+ * This class is responsible for manually inflating our tinted widgets.
* <p>This class two main responsibilities: the first is to 'inject' our tinted views in place of
* the framework versions in layout inflation; the second is backport the {@code android:theme}
* functionality for any inflated widgets. This include theme inheritance from its parent.
*/
-class AppCompatViewInflater {
+public class AppCompatViewInflater {
private static final Class<?>[] sConstructorSignature = new Class[]{
Context.class, AttributeSet.class};
@@ -77,7 +75,7 @@ class AppCompatViewInflater {
private final Object[] mConstructorArgs = new Object[2];
- public final View createView(View parent, final String name, @NonNull Context context,
+ final View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs, boolean inheritContext,
boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {
final Context originalContext = context;
@@ -100,44 +98,63 @@ class AppCompatViewInflater {
// We need to 'inject' our tint aware Views in place of the standard framework versions
switch (name) {
case "TextView":
- view = new AppCompatTextView(context, attrs);
+ view = createTextView(context, attrs);
+ verifyNotNull(view, name);
break;
case "ImageView":
- view = new AppCompatImageView(context, attrs);
+ view = createImageView(context, attrs);
+ verifyNotNull(view, name);
break;
case "Button":
- view = new AppCompatButton(context, attrs);
+ view = createButton(context, attrs);
+ verifyNotNull(view, name);
break;
case "EditText":
- view = new AppCompatEditText(context, attrs);
+ view = createEditText(context, attrs);
+ verifyNotNull(view, name);
break;
case "Spinner":
- view = new AppCompatSpinner(context, attrs);
+ view = createSpinner(context, attrs);
+ verifyNotNull(view, name);
break;
case "ImageButton":
- view = new AppCompatImageButton(context, attrs);
+ view = createImageButton(context, attrs);
+ verifyNotNull(view, name);
break;
case "CheckBox":
- view = new AppCompatCheckBox(context, attrs);
+ view = createCheckBox(context, attrs);
+ verifyNotNull(view, name);
break;
case "RadioButton":
- view = new AppCompatRadioButton(context, attrs);
+ view = createRadioButton(context, attrs);
+ verifyNotNull(view, name);
break;
case "CheckedTextView":
- view = new AppCompatCheckedTextView(context, attrs);
+ view = createCheckedTextView(context, attrs);
+ verifyNotNull(view, name);
break;
case "AutoCompleteTextView":
- view = new AppCompatAutoCompleteTextView(context, attrs);
+ view = createAutoCompleteTextView(context, attrs);
+ verifyNotNull(view, name);
break;
case "MultiAutoCompleteTextView":
- view = new AppCompatMultiAutoCompleteTextView(context, attrs);
+ view = createMultiAutoCompleteTextView(context, attrs);
+ verifyNotNull(view, name);
break;
case "RatingBar":
- view = new AppCompatRatingBar(context, attrs);
+ view = createRatingBar(context, attrs);
+ verifyNotNull(view, name);
break;
case "SeekBar":
- view = new AppCompatSeekBar(context, attrs);
+ view = createSeekBar(context, attrs);
+ verifyNotNull(view, name);
break;
+ default:
+ // The fallback that allows extending class to take over view inflation
+ // for other tags. Note that we don't check that the result is not-null.
+ // That allows the custom inflater path to fall back on the default one
+ // later in this method.
+ view = createView(context, name, attrs);
}
if (view == null && originalContext != context) {
@@ -154,6 +171,85 @@ class AppCompatViewInflater {
return view;
}
+ @NonNull
+ protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
+ return new AppCompatTextView(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatImageView createImageView(Context context, AttributeSet attrs) {
+ return new AppCompatImageView(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatButton createButton(Context context, AttributeSet attrs) {
+ return new AppCompatButton(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatEditText createEditText(Context context, AttributeSet attrs) {
+ return new AppCompatEditText(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatSpinner createSpinner(Context context, AttributeSet attrs) {
+ return new AppCompatSpinner(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatImageButton createImageButton(Context context, AttributeSet attrs) {
+ return new AppCompatImageButton(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatCheckBox createCheckBox(Context context, AttributeSet attrs) {
+ return new AppCompatCheckBox(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatRadioButton createRadioButton(Context context, AttributeSet attrs) {
+ return new AppCompatRadioButton(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatCheckedTextView createCheckedTextView(Context context, AttributeSet attrs) {
+ return new AppCompatCheckedTextView(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatAutoCompleteTextView createAutoCompleteTextView(Context context,
+ AttributeSet attrs) {
+ return new AppCompatAutoCompleteTextView(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatMultiAutoCompleteTextView createMultiAutoCompleteTextView(Context context,
+ AttributeSet attrs) {
+ return new AppCompatMultiAutoCompleteTextView(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatRatingBar createRatingBar(Context context, AttributeSet attrs) {
+ return new AppCompatRatingBar(context, attrs);
+ }
+
+ @NonNull
+ protected AppCompatSeekBar createSeekBar(Context context, AttributeSet attrs) {
+ return new AppCompatSeekBar(context, attrs);
+ }
+
+ private void verifyNotNull(View view, String name) {
+ if (view == null) {
+ throw new IllegalStateException(this.getClass().getName()
+ + " asked to inflate view for <" + name + ">, but returned null");
+ }
+ }
+
+ @Nullable
+ protected View createView(Context context, String name, AttributeSet attrs) {
+ return null;
+ }
+
private View createViewFromTag(Context context, String name, AttributeSet attrs) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
@@ -165,14 +261,14 @@ class AppCompatViewInflater {
if (-1 == name.indexOf('.')) {
for (int i = 0; i < sClassPrefixList.length; i++) {
- final View view = createView(context, name, sClassPrefixList[i]);
+ final View view = createViewByPrefix(context, name, sClassPrefixList[i]);
if (view != null) {
return view;
}
}
return null;
} else {
- return createView(context, name, null);
+ return createViewByPrefix(context, name, null);
}
} catch (Exception e) {
// We do not want to catch these, lets return null and let the actual LayoutInflater
@@ -209,7 +305,7 @@ class AppCompatViewInflater {
a.recycle();
}
- private View createView(Context context, String name, String prefix)
+ private View createViewByPrefix(Context context, String name, String prefix)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
diff --git a/android/support/v7/util/SortedList.java b/android/support/v7/util/SortedList.java
index c62d0ce8..af000a1e 100644
--- a/android/support/v7/util/SortedList.java
+++ b/android/support/v7/util/SortedList.java
@@ -16,6 +16,8 @@
package android.support.v7.util;
+import android.support.annotation.Nullable;
+
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
@@ -315,7 +317,8 @@ public class SortedList<T> {
newDataStart++;
mOldDataStart++;
if (!mCallback.areContentsTheSame(oldItem, newItem)) {
- mCallback.onChanged(mMergedSize - 1, 1);
+ mCallback.onChanged(mMergedSize - 1, 1,
+ mCallback.getChangePayload(oldItem, newItem));
}
} else {
// Old item is lower than or equal to (but not the same as the new). Output it.
@@ -401,7 +404,7 @@ public class SortedList<T> {
return index;
} else {
mData[index] = item;
- mCallback.onChanged(index, 1);
+ mCallback.onChanged(index, 1, mCallback.getChangePayload(existing, item));
return index;
}
}
@@ -488,13 +491,13 @@ public class SortedList<T> {
if (cmp == 0) {
mData[index] = item;
if (contentsChanged) {
- mCallback.onChanged(index, 1);
+ mCallback.onChanged(index, 1, mCallback.getChangePayload(existing, item));
}
return;
}
}
if (contentsChanged) {
- mCallback.onChanged(index, 1);
+ mCallback.onChanged(index, 1, mCallback.getChangePayload(existing, item));
}
// TODO this done in 1 pass to avoid shifting twice.
removeItemAtIndex(index, false);
@@ -741,6 +744,28 @@ public class SortedList<T> {
* @return True if the two items represent the same object or false if they are different.
*/
abstract public boolean areItemsTheSame(T2 item1, T2 item2);
+
+ /**
+ * When {@link #areItemsTheSame(T2, T2)} returns {@code true} for two items and
+ * {@link #areContentsTheSame(T2, T2)} returns false for them, {@link Callback} calls this
+ * method to get a payload about the change.
+ * <p>
+ * For example, if you are using {@link Callback} with
+ * {@link android.support.v7.widget.RecyclerView}, you can return the particular field that
+ * changed in the item and your
+ * {@link android.support.v7.widget.RecyclerView.ItemAnimator ItemAnimator} can use that
+ * information to run the correct animation.
+ * <p>
+ * Default implementation returns {@code null}.
+ *
+ * @param item1 The first item to check.
+ * @param item2 The second item to check.
+ * @return A payload object that represents the changes between the two items.
+ */
+ @Nullable
+ public Object getChangePayload(T2 item1, T2 item2) {
+ return null;
+ }
}
/**
@@ -801,6 +826,11 @@ public class SortedList<T> {
}
@Override
+ public void onChanged(int position, int count, Object payload) {
+ mBatchingListUpdateCallback.onChanged(position, count, payload);
+ }
+
+ @Override
public boolean areContentsTheSame(T2 oldItem, T2 newItem) {
return mWrappedCallback.areContentsTheSame(oldItem, newItem);
}
@@ -810,6 +840,12 @@ public class SortedList<T> {
return mWrappedCallback.areItemsTheSame(item1, item2);
}
+ @Nullable
+ @Override
+ public Object getChangePayload(T2 item1, T2 item2) {
+ return mWrappedCallback.getChangePayload(item1, item2);
+ }
+
/**
* This method dispatches any pending event notifications to the wrapped Callback.
* You <b>must</b> always call this method after you are done with editing the SortedList.
diff --git a/android/support/v7/util/SortedListBatchedCallbackTest.java b/android/support/v7/util/SortedListBatchedCallbackTest.java
index 3ace2178..bc50415d 100644
--- a/android/support/v7/util/SortedListBatchedCallbackTest.java
+++ b/android/support/v7/util/SortedListBatchedCallbackTest.java
@@ -50,6 +50,16 @@ public class SortedListBatchedCallbackTest {
}
@Test
+ public void onChangeWithPayload() {
+ final Object payload = 7;
+ mBatchedCallback.onChanged(1, 2, payload);
+ verifyZeroInteractions(mMockCallback);
+ mBatchedCallback.dispatchLastEvent();
+ verify(mMockCallback).onChanged(1, 2, payload);
+ verifyNoMoreInteractions(mMockCallback);
+ }
+
+ @Test
public void onRemoved() {
mBatchedCallback.onRemoved(2, 3);
verifyZeroInteractions(mMockCallback);
diff --git a/android/support/v7/util/SortedListTest.java b/android/support/v7/util/SortedListTest.java
index da3c9572..47d2ac0f 100644
--- a/android/support/v7/util/SortedListTest.java
+++ b/android/support/v7/util/SortedListTest.java
@@ -16,6 +16,7 @@
package android.support.v7.util;
+import android.support.annotation.Nullable;
import android.support.test.filters.SmallTest;
import junit.framework.TestCase;
@@ -41,6 +42,8 @@ public class SortedListTest extends TestCase {
List<Pair> mRemovals = new ArrayList<Pair>();
List<Pair> mMoves = new ArrayList<Pair>();
List<Pair> mUpdates = new ArrayList<Pair>();
+ private boolean mPayloadChanges = false;
+ List<PayloadChange> mPayloadUpdates = new ArrayList<>();
private SortedList.Callback<Item> mCallback;
InsertedCallback<Item> mInsertedCallback;
ChangedCallback<Item> mChangedCallback;
@@ -97,6 +100,15 @@ public class SortedListTest extends TestCase {
}
@Override
+ public void onChanged(int position, int count, Object payload) {
+ if (mPayloadChanges) {
+ mPayloadUpdates.add(new PayloadChange(position, count, payload));
+ } else {
+ onChanged(position, count);
+ }
+ }
+
+ @Override
public boolean areContentsTheSame(Item oldItem, Item newItem) {
return oldItem.cmpField == newItem.cmpField && oldItem.data == newItem.data;
}
@@ -105,6 +117,15 @@ public class SortedListTest extends TestCase {
public boolean areItemsTheSame(Item item1, Item item2) {
return item1.id == item2.id;
}
+
+ @Nullable
+ @Override
+ public Object getChangePayload(Item item1, Item item2) {
+ if (mPayloadChanges) {
+ return item2.data;
+ }
+ return null;
+ }
};
mInsertedCallback = null;
mChangedCallback = null;
@@ -705,6 +726,76 @@ public class SortedListTest extends TestCase {
assertTrue(mAdditions.contains(new Pair(0, 6)));
}
+ @Test
+ public void testAddExistingItemCallsChangeWithPayload() {
+ mList.addAll(
+ new Item(1, 10),
+ new Item(2, 20),
+ new Item(3, 30)
+ );
+ mPayloadChanges = true;
+
+ // add an item with the same id but a new data field i.e. send an update
+ final Item twoUpdate = new Item(2, 20);
+ twoUpdate.data = 1337;
+ mList.add(twoUpdate);
+ assertEquals(1, mPayloadUpdates.size());
+ final PayloadChange update = mPayloadUpdates.get(0);
+ assertEquals(1, update.position);
+ assertEquals(1, update.count);
+ assertEquals(1337, update.payload);
+ assertEquals(3, size());
+ }
+
+ @Test
+ public void testUpdateItemCallsChangeWithPayload() {
+ mList.addAll(
+ new Item(1, 10),
+ new Item(2, 20),
+ new Item(3, 30)
+ );
+ mPayloadChanges = true;
+
+ // add an item with the same id but a new data field i.e. send an update
+ final Item twoUpdate = new Item(2, 20);
+ twoUpdate.data = 1337;
+ mList.updateItemAt(1, twoUpdate);
+ assertEquals(1, mPayloadUpdates.size());
+ final PayloadChange update = mPayloadUpdates.get(0);
+ assertEquals(1, update.position);
+ assertEquals(1, update.count);
+ assertEquals(1337, update.payload);
+ assertEquals(3, size());
+ assertEquals(1337, mList.get(1).data);
+ }
+
+ @Test
+ public void testAddMultipleExistingItemCallsChangeWithPayload() {
+ mList.addAll(
+ new Item(1, 10),
+ new Item(2, 20),
+ new Item(3, 30)
+ );
+ mPayloadChanges = true;
+
+ // add two items with the same ids but a new data fields i.e. send two updates
+ final Item twoUpdate = new Item(2, 20);
+ twoUpdate.data = 222;
+ final Item threeUpdate = new Item(3, 30);
+ threeUpdate.data = 333;
+ mList.addAll(twoUpdate, threeUpdate);
+ assertEquals(2, mPayloadUpdates.size());
+ final PayloadChange update1 = mPayloadUpdates.get(0);
+ assertEquals(1, update1.position);
+ assertEquals(1, update1.count);
+ assertEquals(222, update1.payload);
+ final PayloadChange update2 = mPayloadUpdates.get(1);
+ assertEquals(2, update2.position);
+ assertEquals(1, update2.count);
+ assertEquals(333, update2.payload);
+ assertEquals(3, size());
+ }
+
private int size() {
return mList.size();
}
@@ -821,4 +912,37 @@ public class SortedListTest extends TestCase {
return result;
}
}
+
+ private static final class PayloadChange {
+ public final int position;
+ public final int count;
+ public final Object payload;
+
+ PayloadChange(int position, int count, Object payload) {
+ this.position = position;
+ this.count = count;
+ this.payload = payload;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PayloadChange payloadChange = (PayloadChange) o;
+
+ if (position != payloadChange.position) return false;
+ if (count != payloadChange.count) return false;
+ return payload != null ? payload.equals(payloadChange.payload)
+ : payloadChange.payload == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = position;
+ result = 31 * result + count;
+ result = 31 * result + (payload != null ? payload.hashCode() : 0);
+ return result;
+ }
+ }
} \ No newline at end of file
diff --git a/android/support/v7/widget/AppCompatTextHelper.java b/android/support/v7/widget/AppCompatTextHelper.java
index fa6196f5..b8ce82a4 100644
--- a/android/support/v7/widget/AppCompatTextHelper.java
+++ b/android/support/v7/widget/AppCompatTextHelper.java
@@ -213,9 +213,9 @@ class AppCompatTextHelper {
if (a.hasValue(R.styleable.TextAppearance_android_fontFamily)
|| a.hasValue(R.styleable.TextAppearance_fontFamily)) {
mFontTypeface = null;
- int fontFamilyId = a.hasValue(R.styleable.TextAppearance_android_fontFamily)
- ? R.styleable.TextAppearance_android_fontFamily
- : R.styleable.TextAppearance_fontFamily;
+ int fontFamilyId = a.hasValue(R.styleable.TextAppearance_fontFamily)
+ ? R.styleable.TextAppearance_fontFamily
+ : R.styleable.TextAppearance_android_fontFamily;
if (!context.isRestricted()) {
final WeakReference<TextView> textViewWeak = new WeakReference<>(mView);
ResourcesCompat.FontCallback replyCallback = new ResourcesCompat.FontCallback() {
diff --git a/android/support/v7/widget/TooltipCompatHandler.java b/android/support/v7/widget/TooltipCompatHandler.java
index 5ce1f8b3..63a61982 100644
--- a/android/support/v7/widget/TooltipCompatHandler.java
+++ b/android/support/v7/widget/TooltipCompatHandler.java
@@ -66,6 +66,10 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
private TooltipPopup mPopup;
private boolean mFromTouch;
+ // The handler currently scheduled to show a tooltip, triggered by a hover
+ // (there can be only one).
+ private static TooltipCompatHandler sPendingHandler;
+
// The handler currently showing a tooltip (there can be only one).
private static TooltipCompatHandler sActiveHandler;
@@ -76,6 +80,15 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
* @param tooltipText the tooltip text
*/
public static void setTooltipText(View view, CharSequence tooltipText) {
+ // The code below is not attempting to update the tooltip text
+ // for a pending or currently active tooltip, because it may lead
+ // to updating the wrong tooltip in in some rare cases (e.g. when
+ // action menu item views are recycled). Instead, the tooltip is
+ // canceled/hidden. This might still be the wrong tooltip,
+ // but hiding a wrong tooltip is less disruptive UX.
+ if (sPendingHandler != null && sPendingHandler.mAnchor == view) {
+ setPendingHandler(null);
+ }
if (TextUtils.isEmpty(tooltipText)) {
if (sActiveHandler != null && sActiveHandler.mAnchor == view) {
sActiveHandler.hide();
@@ -119,8 +132,7 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
if (mAnchor.isEnabled() && mPopup == null) {
mAnchorX = (int) event.getX();
mAnchorY = (int) event.getY();
- mAnchor.removeCallbacks(mShowRunnable);
- mAnchor.postDelayed(mShowRunnable, ViewConfiguration.getLongPressTimeout());
+ setPendingHandler(this);
}
break;
case MotionEvent.ACTION_HOVER_EXIT:
@@ -145,6 +157,7 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
if (!ViewCompat.isAttachedToWindow(mAnchor)) {
return;
}
+ setPendingHandler(null);
if (sActiveHandler != null) {
sActiveHandler.hide();
}
@@ -180,7 +193,27 @@ class TooltipCompatHandler implements View.OnLongClickListener, View.OnHoverList
Log.e(TAG, "sActiveHandler.mPopup == null");
}
}
- mAnchor.removeCallbacks(mShowRunnable);
+ if (sPendingHandler == this) {
+ setPendingHandler(null);
+ }
mAnchor.removeCallbacks(mHideRunnable);
}
+
+ private static void setPendingHandler(TooltipCompatHandler handler) {
+ if (sPendingHandler != null) {
+ sPendingHandler.cancelPendingShow();
+ }
+ sPendingHandler = handler;
+ if (sPendingHandler != null) {
+ sPendingHandler.scheduleShow();
+ }
+ }
+
+ private void scheduleShow() {
+ mAnchor.postDelayed(mShowRunnable, ViewConfiguration.getLongPressTimeout());
+ }
+
+ private void cancelPendingShow() {
+ mAnchor.removeCallbacks(mShowRunnable);
+ }
}
diff --git a/android/support/v7/widget/util/SortedListAdapterCallback.java b/android/support/v7/widget/util/SortedListAdapterCallback.java
index 4921541b..a1203a69 100644
--- a/android/support/v7/widget/util/SortedListAdapterCallback.java
+++ b/android/support/v7/widget/util/SortedListAdapterCallback.java
@@ -56,4 +56,9 @@ public abstract class SortedListAdapterCallback<T2> extends SortedList.Callback<
public void onChanged(int position, int count) {
mAdapter.notifyItemRangeChanged(position, count);
}
+
+ @Override
+ public void onChanged(int position, int count, Object payload) {
+ mAdapter.notifyItemRangeChanged(position, count, payload);
+ }
}
diff --git a/android/support/wear/ambient/AmbientDelegate.java b/android/support/wear/ambient/AmbientDelegate.java
index 49012908..8e96a020 100644
--- a/android/support/wear/ambient/AmbientDelegate.java
+++ b/android/support/wear/ambient/AmbientDelegate.java
@@ -19,14 +19,12 @@ import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.util.Log;
import com.google.android.wearable.compat.WearableActivityController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-import java.lang.reflect.Method;
/**
* Provides compatibility for ambient mode.
@@ -146,19 +144,6 @@ final class AmbientDelegate {
}
/**
- * Sets whether this activity's task should be moved to the front when the system exits ambient
- * mode. If true, the activity's task may be moved to the front if it was the last activity to
- * be running when ambient started, depending on how much time the system spent in ambient mode.
- */
- void setAutoResumeEnabled(boolean enabled) {
- if (mWearableController != null) {
- if (hasSetAutoResumeEnabledMethod()) {
- mWearableController.setAutoResumeEnabled(enabled);
- }
- }
- }
-
- /**
* @return {@code true} if the activity is currently in ambient.
*/
boolean isAmbient() {
@@ -177,31 +162,4 @@ final class AmbientDelegate {
mWearableController.dump(prefix, fd, writer, args);
}
}
-
- private boolean hasSetAutoResumeEnabledMethod() {
- if (!sInitAutoResumeEnabledMethod) {
- sInitAutoResumeEnabledMethod = true;
- try {
- Method method =
- WearableActivityController.class
- .getDeclaredMethod("setAutoResumeEnabled", boolean.class);
- // Proguard is sneaky -- it will actually rewrite strings it finds in addition to
- // function names. Therefore add a "." prefix to the method name check to ensure the
- // function was not renamed by proguard.
- if (!(".setAutoResumeEnabled".equals("." + method.getName()))) {
- throw new NoSuchMethodException();
- }
- sHasAutoResumeEnabledMethod = true;
- } catch (NoSuchMethodException e) {
- Log.w(
- "WearableActivity",
- "Could not find a required method for auto-resume "
- + "support, likely due to proguard optimization. Please add "
- + "com.google.android.wearable:wearable jar to the list of library "
- + "jars for your project");
- sHasAutoResumeEnabledMethod = false;
- }
- }
- return sHasAutoResumeEnabledMethod;
- }
}
diff --git a/android/support/wear/ambient/AmbientMode.java b/android/support/wear/ambient/AmbientMode.java
index db53dfc1..5db93830 100644
--- a/android/support/wear/ambient/AmbientMode.java
+++ b/android/support/wear/ambient/AmbientMode.java
@@ -38,13 +38,13 @@ import java.io.PrintWriter;
* It should be called with an {@link Activity} as an argument and that {@link Activity} will then
* be able to receive ambient lifecycle events through an {@link AmbientCallback}. The
* {@link Activity} will also receive a {@link AmbientController} object from the attachment which
- * can be used to query the current status of the ambient mode, or toggle simple settings.
+ * can be used to query the current status of the ambient mode.
* An example of how to attach {@link AmbientMode} to your {@link Activity} and use
* the {@link AmbientController} can be found below:
* <p>
* <pre class="prettyprint">{@code
* AmbientMode.AmbientController controller = AmbientMode.attachAmbientSupport(this);
- * controller.setAutoResumeEnabled(true);
+ * boolean isAmbient = controller.isAmbient();
* }</pre>
*/
public final class AmbientMode extends Fragment {
@@ -117,7 +117,7 @@ public final class AmbientMode extends Fragment {
* Called when the system is updating the display for ambient mode. Activities may use this
* opportunity to update or invalidate views.
*/
- public void onUpdateAmbient() {};
+ public void onUpdateAmbient() {}
/**
* Called when an activity should exit ambient mode. This event is sent while an activity is
@@ -126,7 +126,7 @@ public final class AmbientMode extends Fragment {
* <p><em>Derived classes must call through to the super class's implementation of this
* method. If they do not, an exception will be thrown.</em>
*/
- public void onExitAmbient() {};
+ public void onExitAmbient() {}
}
private final AmbientDelegate.AmbientCallback mCallback =
@@ -220,7 +220,7 @@ public final class AmbientMode extends Fragment {
* @param activity the activity to attach ambient support to. This activity has to also
* implement {@link AmbientCallbackProvider}
* @return the associated {@link AmbientController} which can be used to query the state of
- * ambient mode and toggle simple settings related to it.
+ * ambient mode.
*/
public static <T extends Activity & AmbientCallbackProvider> AmbientController
attachAmbientSupport(T activity) {
@@ -251,9 +251,8 @@ public final class AmbientMode extends Fragment {
/**
* A class for interacting with the ambient mode on a wearable device. This class can be used to
- * query the current state of ambient mode and to enable or disable certain settings.
- * An instance of this class is returned to the user when they attach their {@link Activity}
- * to {@link AmbientMode}.
+ * query the current state of ambient mode. An instance of this class is returned to the user
+ * when they attach their {@link Activity} to {@link AmbientMode}.
*/
public final class AmbientController {
private static final String TAG = "AmbientController";
diff --git a/android/support/wear/ambient/SharedLibraryVersion.java b/android/support/wear/ambient/SharedLibraryVersion.java
index cd90a3b7..9421d9ee 100644
--- a/android/support/wear/ambient/SharedLibraryVersion.java
+++ b/android/support/wear/ambient/SharedLibraryVersion.java
@@ -16,7 +16,6 @@
package android.support.wear.ambient;
import android.os.Build;
-import android.support.annotation.RestrictTo;
import android.support.annotation.VisibleForTesting;
import com.google.android.wearable.WearableSharedLib;
@@ -24,10 +23,7 @@ import com.google.android.wearable.WearableSharedLib;
/**
* Internal class which can be used to determine the version of the wearable shared library that is
* available on the current device.
- *
- * @hide
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
final class SharedLibraryVersion {
private SharedLibraryVersion() {
diff --git a/android/support/wear/ambient/WearableControllerProvider.java b/android/support/wear/ambient/WearableControllerProvider.java
index 1682dc0e..4b6ae8ee 100644
--- a/android/support/wear/ambient/WearableControllerProvider.java
+++ b/android/support/wear/ambient/WearableControllerProvider.java
@@ -28,7 +28,7 @@ import java.lang.reflect.Method;
*
* @hide
*/
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@RestrictTo(RestrictTo.Scope.LIBRARY)
public class WearableControllerProvider {
private static final String TAG = "WearableControllerProvider";
diff --git a/android/support/wear/internal/widget/ResourcesUtil.java b/android/support/wear/internal/widget/ResourcesUtil.java
index f23a6889..8ba3adf0 100644
--- a/android/support/wear/internal/widget/ResourcesUtil.java
+++ b/android/support/wear/internal/widget/ResourcesUtil.java
@@ -26,7 +26,7 @@ import android.support.annotation.RestrictTo.Scope;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public final class ResourcesUtil {
/**
diff --git a/android/support/wear/internal/widget/drawer/MultiPagePresenter.java b/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
index ad56048b..4a7ce667 100644
--- a/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
+++ b/android/support/wear/internal/widget/drawer/MultiPagePresenter.java
@@ -28,7 +28,7 @@ import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableN
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class MultiPagePresenter extends WearableNavigationDrawerPresenter {
private final Ui mUi;
diff --git a/android/support/wear/internal/widget/drawer/MultiPageUi.java b/android/support/wear/internal/widget/drawer/MultiPageUi.java
index 90568451..0ba2f5d1 100644
--- a/android/support/wear/internal/widget/drawer/MultiPageUi.java
+++ b/android/support/wear/internal/widget/drawer/MultiPageUi.java
@@ -16,6 +16,7 @@
package android.support.wear.internal.widget.drawer;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.support.annotation.RestrictTo.Scope;
@@ -37,7 +38,7 @@ import android.widget.TextView;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class MultiPageUi implements MultiPagePresenter.Ui {
private static final String TAG = "MultiPageUi";
@@ -62,13 +63,8 @@ public class MultiPageUi implements MultiPagePresenter.Ui {
final View content = inflater.inflate(R.layout.ws_navigation_drawer_view, drawer,
false /* attachToRoot */);
- mNavigationPager =
- (ViewPager) content
- .findViewById(R.id.ws_navigation_drawer_view_pager);
- mPageIndicatorView =
- (PageIndicatorView)
- content.findViewById(
- R.id.ws_navigation_drawer_page_indicator);
+ mNavigationPager = content.findViewById(R.id.ws_navigation_drawer_view_pager);
+ mPageIndicatorView = content.findViewById(R.id.ws_navigation_drawer_page_indicator);
drawer.setDrawerContent(content);
}
@@ -132,8 +128,9 @@ public class MultiPageUi implements MultiPagePresenter.Ui {
mAdapter = adapter;
}
+ @NonNull
@Override
- public Object instantiateItem(ViewGroup container, int position) {
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
// Do not attach to root in the inflate method. The view needs to returned at the end
// of this method. Attaching to root will cause view to point to container instead.
final View view =
@@ -141,17 +138,17 @@ public class MultiPageUi implements MultiPagePresenter.Ui {
.inflate(R.layout.ws_navigation_drawer_item_view, container, false);
container.addView(view);
final ImageView iconView =
- (ImageView) view
- .findViewById(R.id.ws_navigation_drawer_item_icon);
+ view.findViewById(R.id.ws_navigation_drawer_item_icon);
final TextView textView =
- (TextView) view.findViewById(R.id.ws_navigation_drawer_item_text);
+ view.findViewById(R.id.ws_navigation_drawer_item_text);
iconView.setImageDrawable(mAdapter.getItemDrawable(position));
textView.setText(mAdapter.getItemText(position));
return view;
}
@Override
- public void destroyItem(ViewGroup container, int position, Object object) {
+ public void destroyItem(@NonNull ViewGroup container, int position,
+ @NonNull Object object) {
container.removeView((View) object);
}
@@ -161,12 +158,12 @@ public class MultiPageUi implements MultiPagePresenter.Ui {
}
@Override
- public int getItemPosition(Object object) {
+ public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
@Override
- public boolean isViewFromObject(View view, Object object) {
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
}
diff --git a/android/support/wear/internal/widget/drawer/SinglePagePresenter.java b/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
index d90b5891..42cc7d06 100644
--- a/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
+++ b/android/support/wear/internal/widget/drawer/SinglePagePresenter.java
@@ -29,7 +29,7 @@ import android.support.wear.widget.drawer.WearableNavigationDrawerView.WearableN
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class SinglePagePresenter extends WearableNavigationDrawerPresenter {
private static final long DRAWER_CLOSE_DELAY_MS = 500;
diff --git a/android/support/wear/internal/widget/drawer/SinglePageUi.java b/android/support/wear/internal/widget/drawer/SinglePageUi.java
index f3a42904..ffc966f0 100644
--- a/android/support/wear/internal/widget/drawer/SinglePageUi.java
+++ b/android/support/wear/internal/widget/drawer/SinglePageUi.java
@@ -38,7 +38,7 @@ import android.widget.Toast;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class SinglePageUi implements SinglePagePresenter.Ui {
@IdRes
@@ -111,11 +111,10 @@ public class SinglePageUi implements SinglePagePresenter.Ui {
R.layout.ws_single_page_nav_drawer_peek_view, mDrawer,
false /* attachToRoot */);
- mTextView = (TextView) content.findViewById(R.id.ws_nav_drawer_text);
+ mTextView = content.findViewById(R.id.ws_nav_drawer_text);
mSinglePageImageViews = new CircledImageView[count];
for (int i = 0; i < count; i++) {
- mSinglePageImageViews[i] = (CircledImageView) content
- .findViewById(SINGLE_PAGE_BUTTON_IDS[i]);
+ mSinglePageImageViews[i] = content.findViewById(SINGLE_PAGE_BUTTON_IDS[i]);
mSinglePageImageViews[i].setOnClickListener(new OnSelectedClickHandler(i, mPresenter));
mSinglePageImageViews[i].setCircleHidden(true);
}
diff --git a/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java b/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
index 1c8c4fb7..df108aac 100644
--- a/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
+++ b/android/support/wear/internal/widget/drawer/WearableNavigationDrawerPresenter.java
@@ -30,7 +30,7 @@ import java.util.Set;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public abstract class WearableNavigationDrawerPresenter {
private final Set<OnItemSelectedListener> mOnItemSelectedListeners = new HashSet<>();
diff --git a/android/support/wear/utils/MetadataConstants.java b/android/support/wear/utils/MetadataConstants.java
index 5be9c523..c7335c2d 100644
--- a/android/support/wear/utils/MetadataConstants.java
+++ b/android/support/wear/utils/MetadataConstants.java
@@ -15,16 +15,13 @@
*/
package android.support.wear.utils;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.os.Build;
/**
* Constants for android wear apps which are related to manifest meta-data.
*/
-@TargetApi(Build.VERSION_CODES.N)
public class MetadataConstants {
// Constants for standalone apps. //
diff --git a/android/support/wear/widget/BezierSCurveInterpolator.java b/android/support/wear/widget/BezierSCurveInterpolator.java
index 131bae84..9c56a83d 100644
--- a/android/support/wear/widget/BezierSCurveInterpolator.java
+++ b/android/support/wear/widget/BezierSCurveInterpolator.java
@@ -17,8 +17,6 @@
package android.support.wear.widget;
import android.animation.TimeInterpolator;
-import android.annotation.TargetApi;
-import android.os.Build;
import android.support.annotation.RestrictTo;
import android.support.annotation.RestrictTo.Scope;
@@ -27,8 +25,7 @@ import android.support.annotation.RestrictTo.Scope;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
-@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+@RestrictTo(Scope.LIBRARY)
class BezierSCurveInterpolator implements TimeInterpolator {
/**
diff --git a/android/support/wear/widget/BoxInsetLayout.java b/android/support/wear/widget/BoxInsetLayout.java
index ba35f2c9..a8b13814 100644
--- a/android/support/wear/widget/BoxInsetLayout.java
+++ b/android/support/wear/widget/BoxInsetLayout.java
@@ -20,7 +20,6 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -111,21 +110,6 @@ public class BoxInsetLayout extends ViewGroup {
}
@Override
- public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- insets = super.onApplyWindowInsets(insets);
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- final boolean round = insets.isRound();
- if (round != mIsRound) {
- mIsRound = round;
- requestLayout();
- }
- mInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
- }
- return insets;
- }
-
- @Override
public void setForeground(Drawable drawable) {
super.setForeground(drawable);
mForegroundDrawable = drawable;
@@ -145,14 +129,10 @@ public class BoxInsetLayout extends ViewGroup {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
- requestApplyInsets();
- } else {
- mIsRound = getResources().getConfiguration().isScreenRound();
- WindowInsets insets = getRootWindowInsets();
- mInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
- }
+ mIsRound = getResources().getConfiguration().isScreenRound();
+ WindowInsets insets = getRootWindowInsets();
+ mInsets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom());
}
@Override
@@ -413,7 +393,7 @@ public class BoxInsetLayout extends ViewGroup {
public static class LayoutParams extends FrameLayout.LayoutParams {
/** @hide */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+ @RestrictTo(RestrictTo.Scope.LIBRARY)
@IntDef({BOX_NONE, BOX_LEFT, BOX_TOP, BOX_RIGHT, BOX_BOTTOM, BOX_ALL})
@Retention(RetentionPolicy.SOURCE)
public @interface BoxedEdges {}
diff --git a/android/support/wear/widget/CircledImageView.java b/android/support/wear/widget/CircledImageView.java
index 03ed8c98..c441dd57 100644
--- a/android/support/wear/widget/CircledImageView.java
+++ b/android/support/wear/widget/CircledImageView.java
@@ -19,7 +19,6 @@ package android.support.wear.widget;
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -32,7 +31,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.Px;
import android.support.annotation.RestrictTo;
import android.support.annotation.RestrictTo.Scope;
@@ -47,8 +45,7 @@ import java.util.Objects;
*
* @hide
*/
-@TargetApi(Build.VERSION_CODES.M)
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class CircledImageView extends View {
private static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
@@ -133,13 +130,9 @@ public class CircledImageView extends View {
if (mDrawable != null && mDrawable.getConstantState() != null) {
// The provided Drawable may be used elsewhere, so make a mutable clone before setTint()
// or setAlpha() is called on it.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- mDrawable =
- mDrawable.getConstantState()
- .newDrawable(context.getResources(), context.getTheme());
- } else {
- mDrawable = mDrawable.getConstantState().newDrawable(context.getResources());
- }
+ mDrawable =
+ mDrawable.getConstantState()
+ .newDrawable(context.getResources(), context.getTheme());
mDrawable = mDrawable.mutate();
}
diff --git a/android/support/wear/widget/CurvingLayoutCallback.java b/android/support/wear/widget/CurvingLayoutCallback.java
index 275f1f8b..5e88a8c4 100644
--- a/android/support/wear/widget/CurvingLayoutCallback.java
+++ b/android/support/wear/widget/CurvingLayoutCallback.java
@@ -113,7 +113,7 @@ public class CurvingLayoutCallback extends WearableLinearLayoutManager.LayoutCal
*/
public void adjustAnchorOffsetXY(View child, float[] anchorOffsetXY) {
return;
- };
+ }
@VisibleForTesting
void setRound(boolean isScreenRound) {
diff --git a/android/support/wear/widget/ProgressDrawable.java b/android/support/wear/widget/ProgressDrawable.java
index 08e8ec2e..28e05708 100644
--- a/android/support/wear/widget/ProgressDrawable.java
+++ b/android/support/wear/widget/ProgressDrawable.java
@@ -19,14 +19,12 @@ package android.support.wear.widget;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.RestrictTo;
import android.support.annotation.RestrictTo.Scope;
import android.util.Property;
@@ -37,8 +35,7 @@ import android.view.animation.LinearInterpolator;
*
* @hide
*/
-@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class ProgressDrawable extends Drawable {
private static final Property<ProgressDrawable, Integer> LEVEL =
diff --git a/android/support/wear/widget/RoundedDrawable.java b/android/support/wear/widget/RoundedDrawable.java
index fd09a878..300b6dd6 100644
--- a/android/support/wear/widget/RoundedDrawable.java
+++ b/android/support/wear/widget/RoundedDrawable.java
@@ -15,7 +15,6 @@
*/
package android.support.wear.widget;
-import android.annotation.TargetApi;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
@@ -29,7 +28,6 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@@ -76,7 +74,6 @@ import java.util.Objects;
* app:radius="dimension"
* app:clipEnabled="boolean" /&gt;</pre>
*/
-@TargetApi(Build.VERSION_CODES.N)
public class RoundedDrawable extends Drawable {
@VisibleForTesting
diff --git a/android/support/wear/widget/ScrollManager.java b/android/support/wear/widget/ScrollManager.java
index 8155f62d..e01a2710 100644
--- a/android/support/wear/widget/ScrollManager.java
+++ b/android/support/wear/widget/ScrollManager.java
@@ -16,11 +16,8 @@
package android.support.wear.widget;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.annotation.TargetApi;
-import android.os.Build;
import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
import android.support.v7.widget.RecyclerView;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -30,8 +27,7 @@ import android.view.VelocityTracker;
*
* @hide
*/
-@TargetApi(Build.VERSION_CODES.M)
-@RestrictTo(LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class ScrollManager {
// One second in milliseconds.
private static final int ONE_SEC_IN_MS = 1000;
diff --git a/android/support/wear/widget/SimpleAnimatorListener.java b/android/support/wear/widget/SimpleAnimatorListener.java
index a60b0bd2..3a1e56b9 100644
--- a/android/support/wear/widget/SimpleAnimatorListener.java
+++ b/android/support/wear/widget/SimpleAnimatorListener.java
@@ -29,7 +29,7 @@ import android.support.annotation.RestrictTo.Scope;
* @hide Hidden until this goes through review
*/
@RequiresApi(Build.VERSION_CODES.KITKAT_WATCH)
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class SimpleAnimatorListener implements Animator.AnimatorListener {
private boolean mWasCanceled;
diff --git a/android/support/wear/widget/SwipeDismissLayout.java b/android/support/wear/widget/SwipeDismissLayout.java
index 6e7a6f36..33da79c2 100644
--- a/android/support/wear/widget/SwipeDismissLayout.java
+++ b/android/support/wear/widget/SwipeDismissLayout.java
@@ -16,12 +16,11 @@
package android.support.wear.widget;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
import android.content.Context;
import android.content.res.Resources;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
+import android.support.annotation.RestrictTo.Scope;
import android.support.annotation.UiThread;
import android.util.AttributeSet;
import android.util.Log;
@@ -40,7 +39,7 @@ import android.widget.FrameLayout;
*
* @hide
*/
-@RestrictTo(LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
@UiThread
class SwipeDismissLayout extends FrameLayout {
private static final String TAG = "SwipeDismissLayout";
diff --git a/android/support/wear/widget/WearableRecyclerView.java b/android/support/wear/widget/WearableRecyclerView.java
index 5cacdfcb..1425e68b 100644
--- a/android/support/wear/widget/WearableRecyclerView.java
+++ b/android/support/wear/widget/WearableRecyclerView.java
@@ -16,11 +16,9 @@
package android.support.wear.widget;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Point;
-import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.support.wear.R;
@@ -35,7 +33,6 @@ import android.view.ViewTreeObserver;
*
* @see #setCircularScrollingGestureEnabled(boolean)
*/
-@TargetApi(Build.VERSION_CODES.M)
public class WearableRecyclerView extends RecyclerView {
private static final String TAG = "WearableRecyclerView";
diff --git a/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java b/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
index f1cb640d..e9b2a40a 100644
--- a/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
+++ b/android/support/wear/widget/drawer/AbsListViewFlingWatcher.java
@@ -32,7 +32,7 @@ import java.lang.ref.WeakReference;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class AbsListViewFlingWatcher implements FlingWatcher, OnScrollListener {
private final FlingListener mListener;
diff --git a/android/support/wear/widget/drawer/FlingWatcherFactory.java b/android/support/wear/widget/drawer/FlingWatcherFactory.java
index 3fe84c63..2fdfa13c 100644
--- a/android/support/wear/widget/drawer/FlingWatcherFactory.java
+++ b/android/support/wear/widget/drawer/FlingWatcherFactory.java
@@ -33,7 +33,7 @@ import java.util.WeakHashMap;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class FlingWatcherFactory {
/**
diff --git a/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java b/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
index ca95ab22..4c0e5c8c 100644
--- a/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
+++ b/android/support/wear/widget/drawer/NestedScrollViewFlingWatcher.java
@@ -38,7 +38,7 @@ import java.lang.ref.WeakReference;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class NestedScrollViewFlingWatcher implements FlingWatcher, OnScrollChangeListener {
static final int MAX_WAIT_TIME_MS = 100;
diff --git a/android/support/wear/widget/drawer/PageIndicatorView.java b/android/support/wear/widget/drawer/PageIndicatorView.java
index 99c7c09f..1285f725 100644
--- a/android/support/wear/widget/drawer/PageIndicatorView.java
+++ b/android/support/wear/widget/drawer/PageIndicatorView.java
@@ -54,7 +54,7 @@ import java.util.concurrent.TimeUnit;
* @hide
*/
@RequiresApi(Build.VERSION_CODES.M)
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
public class PageIndicatorView extends View implements OnPageChangeListener {
private static final String TAG = "Dots";
diff --git a/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java b/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
index 7570fae5..7916875e 100644
--- a/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
+++ b/android/support/wear/widget/drawer/RecyclerViewFlingWatcher.java
@@ -31,7 +31,7 @@ import java.lang.ref.WeakReference;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class RecyclerViewFlingWatcher extends OnScrollListener implements FlingWatcher {
private final FlingListener mListener;
diff --git a/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java b/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
index f0b973be..5154e7bb 100644
--- a/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
+++ b/android/support/wear/widget/drawer/ScrollViewFlingWatcher.java
@@ -38,7 +38,7 @@ import java.lang.ref.WeakReference;
*
* @hide
*/
-@RestrictTo(Scope.LIBRARY_GROUP)
+@RestrictTo(Scope.LIBRARY)
class ScrollViewFlingWatcher implements FlingWatcher, OnScrollChangeListener {
static final int MAX_WAIT_TIME_MS = 100;
diff --git a/android/support/wear/widget/drawer/WearableActionDrawerMenu.java b/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
index 158467dc..092ac72d 100644
--- a/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
+++ b/android/support/wear/widget/drawer/WearableActionDrawerMenu.java
@@ -16,12 +16,10 @@
package android.support.wear.widget.drawer;
-import android.annotation.TargetApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.Nullable;
import android.view.ActionProvider;
import android.view.ContextMenu;
@@ -34,7 +32,6 @@ import android.view.View;
import java.util.ArrayList;
import java.util.List;
-@TargetApi(Build.VERSION_CODES.M)
/* package */ class WearableActionDrawerMenu implements Menu {
private final Context mContext;
diff --git a/android/support/wear/widget/drawer/WearableActionDrawerView.java b/android/support/wear/widget/drawer/WearableActionDrawerView.java
index 8154e6b9..99cd4ff6 100644
--- a/android/support/wear/widget/drawer/WearableActionDrawerView.java
+++ b/android/support/wear/widget/drawer/WearableActionDrawerView.java
@@ -16,12 +16,10 @@
package android.support.wear.widget.drawer;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -38,6 +36,7 @@ import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -75,7 +74,6 @@ import java.util.Objects;
* <p>For {@link MenuItem}, setting and getting the title and icon, {@link MenuItem#getItemId}, and
* {@link MenuItem#setOnMenuItemClickListener} are implemented.
*/
-@TargetApi(Build.VERSION_CODES.M)
public class WearableActionDrawerView extends WearableDrawerView {
private static final String TAG = "WearableActionDrawer";
@@ -140,12 +138,8 @@ public class WearableActionDrawerView extends WearableDrawerView {
View peekView = layoutInflater.inflate(R.layout.ws_action_drawer_peek_view,
getPeekContainer(), false /* attachToRoot */);
setPeekContent(peekView);
- mPeekActionIcon =
- (ImageView) peekView
- .findViewById(R.id.ws_action_drawer_peek_action_icon);
- mPeekExpandIcon =
- (ImageView) peekView
- .findViewById(R.id.ws_action_drawer_expand_icon);
+ mPeekActionIcon = peekView.findViewById(R.id.ws_action_drawer_peek_action_icon);
+ mPeekExpandIcon = peekView.findViewById(R.id.ws_action_drawer_expand_icon);
} else {
mPeekActionIcon = null;
mPeekExpandIcon = null;
@@ -193,6 +187,16 @@ public class WearableActionDrawerView extends WearableDrawerView {
}
@Override
+ public void onDrawerOpened() {
+ if (mActionListAdapter.getItemCount() > 0) {
+ RecyclerView.ViewHolder holder = mActionList.findViewHolderForAdapterPosition(0);
+ if (holder != null && holder.itemView != null) {
+ holder.itemView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+ }
+ }
+
+ @Override
public boolean canScrollHorizontally(int direction) {
// Prevent the window from being swiped closed while it is open by saying that it can scroll
// horizontally.
@@ -412,7 +416,6 @@ public class WearableActionDrawerView extends WearableDrawerView {
CharSequence title = mActionMenu.getItem(titleAwarePosition).getTitle();
holder.textView.setText(title);
holder.textView.setContentDescription(title);
- holder.iconView.setContentDescription(title);
holder.iconView.setImageDrawable(icon);
} else if (viewHolder instanceof TitleViewHolder) {
TitleViewHolder holder = (TitleViewHolder) viewHolder;
diff --git a/android/support/wear/widget/drawer/WearableDrawerLayout.java b/android/support/wear/widget/drawer/WearableDrawerLayout.java
index 6d270647..e100a467 100644
--- a/android/support/wear/widget/drawer/WearableDrawerLayout.java
+++ b/android/support/wear/widget/drawer/WearableDrawerLayout.java
@@ -19,11 +19,10 @@ package android.support.wear.widget.drawer;
import static android.support.wear.widget.drawer.WearableDrawerView.STATE_IDLE;
import static android.support.wear.widget.drawer.WearableDrawerView.STATE_SETTLING;
-import android.annotation.TargetApi;
import android.content.Context;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.support.v4.view.NestedScrollingParent;
@@ -98,7 +97,6 @@ import android.widget.FrameLayout;
* &lt;/android.support.wear.widget.drawer.WearableDrawerView&gt;
* &lt;/android.support.wear.widget.drawer.WearableDrawerLayout&gt;</pre>
*/
-@TargetApi(Build.VERSION_CODES.M)
public class WearableDrawerLayout extends FrameLayout
implements View.OnLayoutChangeListener, NestedScrollingParent, FlingListener {
@@ -654,12 +652,13 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override // NestedScrollingParent
- public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
+ public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY,
+ boolean consumed) {
return false;
}
@Override // NestedScrollingParent
- public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+ public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
maybeUpdateScrollingContentView(target);
mLastScrollWasFling = true;
@@ -674,13 +673,13 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override // NestedScrollingParent
- public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+ public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {
maybeUpdateScrollingContentView(target);
}
@Override // NestedScrollingParent
- public void onNestedScroll(
- View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
+ public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
+ int dxUnconsumed, int dyUnconsumed) {
boolean scrolledUp = dyConsumed < 0;
boolean scrolledDown = dyConsumed > 0;
@@ -873,18 +872,20 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override // NestedScrollingParent
- public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+ public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
+ int nestedScrollAxes) {
mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);
}
@Override // NestedScrollingParent
- public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+ public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
+ int nestedScrollAxes) {
mCurrentNestedScrollSlopTracker = 0;
return true;
}
@Override // NestedScrollingParent
- public void onStopNestedScroll(View target) {
+ public void onStopNestedScroll(@NonNull View target) {
mNestedScrollingParentHelper.onStopNestedScroll(target);
}
@@ -961,7 +962,7 @@ public class WearableDrawerLayout extends FrameLayout
public abstract WearableDrawerView getDrawerView();
@Override
- public boolean tryCaptureView(View child, int pointerId) {
+ public boolean tryCaptureView(@NonNull View child, int pointerId) {
WearableDrawerView drawerView = getDrawerView();
// Returns true if the dragger is dragging the drawer.
return child == drawerView && !drawerView.isLocked()
@@ -969,13 +970,13 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override
- public int getViewVerticalDragRange(View child) {
+ public int getViewVerticalDragRange(@NonNull View child) {
// Defines the vertical drag range of the drawer.
return child == getDrawerView() ? child.getHeight() : 0;
}
@Override
- public void onViewCaptured(View capturedChild, int activePointerId) {
+ public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
showDrawerContentMaybeAnimate((WearableDrawerView) capturedChild);
}
@@ -1036,7 +1037,7 @@ public class WearableDrawerLayout extends FrameLayout
private class TopDrawerDraggerCallback extends DrawerDraggerCallback {
@Override
- public int clampViewPositionVertical(View child, int top, int dy) {
+ public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
if (mTopDrawerView == child) {
int peekHeight = mTopDrawerView.getPeekContainer().getHeight();
// The top drawer can be dragged vertically from peekHeight - height to 0.
@@ -1063,7 +1064,7 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
if (releasedChild == mTopDrawerView) {
// Settle to final position. Either swipe open or close.
final float openedPercent = mTopDrawerView.getOpenedPercent();
@@ -1085,7 +1086,8 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override
- public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx,
+ int dy) {
if (changedView == mTopDrawerView) {
// Compute the offset and invalidate will move the drawer during layout.
final int height = changedView.getHeight();
@@ -1106,7 +1108,7 @@ public class WearableDrawerLayout extends FrameLayout
private class BottomDrawerDraggerCallback extends DrawerDraggerCallback {
@Override
- public int clampViewPositionVertical(View child, int top, int dy) {
+ public int clampViewPositionVertical(@NonNull View child, int top, int dy) {
if (mBottomDrawerView == child) {
// The bottom drawer can be dragged vertically from (parentHeight - height) to
// (parentHeight - peekHeight).
@@ -1131,7 +1133,7 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override
- public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
if (releasedChild == mBottomDrawerView) {
// Settle to final position. Either swipe open or close.
final int parentHeight = getHeight();
@@ -1151,7 +1153,8 @@ public class WearableDrawerLayout extends FrameLayout
}
@Override
- public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx,
+ int dy) {
if (changedView == mBottomDrawerView) {
// Compute the offset and invalidate will move the drawer during layout.
final int height = changedView.getHeight();
diff --git a/android/support/wear/widget/drawer/WearableDrawerView.java b/android/support/wear/widget/drawer/WearableDrawerView.java
index dafac39b..2462cba8 100644
--- a/android/support/wear/widget/drawer/WearableDrawerView.java
+++ b/android/support/wear/widget/drawer/WearableDrawerView.java
@@ -16,11 +16,9 @@
package android.support.wear.widget.drawer;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.support.annotation.IdRes;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
@@ -87,7 +85,6 @@ import java.lang.annotation.RetentionPolicy;
* &lt;/LinearLayout&gt;
* &lt;/android.support.wear.widget.drawer.WearableDrawerView&gt;</pre>
*/
-@TargetApi(Build.VERSION_CODES.M)
public class WearableDrawerView extends FrameLayout {
/**
* Indicates that the drawer is in an idle, settled state. No animation is in progress.
@@ -109,7 +106,7 @@ public class WearableDrawerView extends FrameLayout {
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @RestrictTo(Scope.LIBRARY_GROUP)
+ @RestrictTo(Scope.LIBRARY)
@IntDef({STATE_IDLE, STATE_DRAGGING, STATE_SETTLING})
public @interface DrawerState {}
@@ -155,8 +152,8 @@ public class WearableDrawerView extends FrameLayout {
setElevation(context.getResources()
.getDimension(R.dimen.ws_wearable_drawer_view_elevation));
- mPeekContainer = (ViewGroup) findViewById(R.id.ws_drawer_view_peek_container);
- mPeekIcon = (ImageView) findViewById(R.id.ws_drawer_view_peek_icon);
+ mPeekContainer = findViewById(R.id.ws_drawer_view_peek_container);
+ mPeekIcon = findViewById(R.id.ws_drawer_view_peek_icon);
mPeekContainer.setOnClickListener(
new OnClickListener() {
diff --git a/android/support/wear/widget/drawer/WearableNavigationDrawerView.java b/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
index 480812b8..c5c49fe3 100644
--- a/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
+++ b/android/support/wear/widget/drawer/WearableNavigationDrawerView.java
@@ -16,11 +16,9 @@
package android.support.wear.widget.drawer;
-import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.IntDef;
@@ -58,7 +56,6 @@ import java.util.concurrent.TimeUnit;
* <p>The developer may specify which style to use with the {@code app:navigationStyle} custom
* attribute. If not specified, {@link #SINGLE_PAGE singlePage} will be used as the default.
*/
-@TargetApi(Build.VERSION_CODES.M)
public class WearableNavigationDrawerView extends WearableDrawerView {
private static final String TAG = "WearableNavDrawer";
@@ -79,7 +76,7 @@ public class WearableNavigationDrawerView extends WearableDrawerView {
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @RestrictTo(Scope.LIBRARY_GROUP)
+ @RestrictTo(Scope.LIBRARY)
@IntDef({SINGLE_PAGE, MULTI_PAGE})
public @interface NavigationStyle {}
@@ -282,7 +279,7 @@ public class WearableNavigationDrawerView extends WearableDrawerView {
/**
* @hide
*/
- @RestrictTo(Scope.LIBRARY_GROUP)
+ @RestrictTo(Scope.LIBRARY)
public void setPresenter(WearableNavigationDrawerPresenter presenter) {
mPresenter = presenter;
}
diff --git a/android/support/wearable/watchface/decomposition/package-info.java b/android/support/wearable/watchface/decomposition/package-info.java
new file mode 100644
index 00000000..dbd815e7
--- /dev/null
+++ b/android/support/wearable/watchface/decomposition/package-info.java
@@ -0,0 +1,2 @@
+/** @hide */
+package android.support.wearable.watchface.decomposition;