Merge changes I8da92d0d,I4fa3fe98
* changes: Propagate apply flags to nested RemoteViews Make setRemoteAdapter() work for nested RemoteViews
This commit is contained in:
committed by
Android (Google) Code Review
commit
3380e190f3
@@ -300,6 +300,13 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
*/
|
||||
public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4;
|
||||
|
||||
/**
|
||||
* This mask determines which flags are propagated to nested RemoteViews (either added by
|
||||
* addView, or set as portrait/landscape/sized RemoteViews).
|
||||
*/
|
||||
static final int FLAG_MASK_TO_PROPAGATE =
|
||||
FLAG_WIDGET_IS_COLLECTION_CHILD | FLAG_USE_LIGHT_BACKGROUND_LAYOUT;
|
||||
|
||||
/**
|
||||
* A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is
|
||||
* intentionally a different instance in order to trick Bundle reader so that it doesn't allow
|
||||
@@ -467,6 +474,18 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
*/
|
||||
public void addFlags(@ApplyFlags int flags) {
|
||||
mApplyFlags = mApplyFlags | flags;
|
||||
|
||||
int flagsToPropagate = flags & FLAG_MASK_TO_PROPAGATE;
|
||||
if (flagsToPropagate != 0) {
|
||||
if (hasSizedRemoteViews()) {
|
||||
for (RemoteViews remoteView : mSizedRemoteViews) {
|
||||
remoteView.addFlags(flagsToPropagate);
|
||||
}
|
||||
} else if (hasLandscapeAndPortraitLayouts()) {
|
||||
mLandscape.addFlags(flagsToPropagate);
|
||||
mPortrait.addFlags(flagsToPropagate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2407,6 +2426,10 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
// will return -1.
|
||||
final int nextChild = getNextRecyclableChild(target);
|
||||
RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context);
|
||||
|
||||
int flagsToPropagate = mApplyFlags & FLAG_MASK_TO_PROPAGATE;
|
||||
if (flagsToPropagate != 0) rvToApply.addFlags(flagsToPropagate);
|
||||
|
||||
if (nextChild >= 0 && mStableId != NO_ID) {
|
||||
// At that point, the views starting at index nextChild are the ones recyclable but
|
||||
// not yet recycled. All views added on that round of application are placed before.
|
||||
@@ -2419,8 +2442,8 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
target.removeViews(nextChild, recycledViewIndex - nextChild);
|
||||
}
|
||||
setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
|
||||
rvToApply.reapply(context, child, handler, null /* size */, colorResources,
|
||||
false /* topLevel */);
|
||||
rvToApply.reapplyNestedViews(context, child, rootParent, handler,
|
||||
null /* size */, colorResources);
|
||||
return;
|
||||
}
|
||||
// If we cannot recycle the views, we still remove all views in between to
|
||||
@@ -2431,8 +2454,8 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
// If we cannot recycle, insert the new view before the next recyclable child.
|
||||
|
||||
// Inflate nested views and add as children
|
||||
View nestedView = rvToApply.apply(context, target, handler, null /* size */,
|
||||
colorResources);
|
||||
View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
|
||||
null /* size */, colorResources);
|
||||
if (mStableId != NO_ID) {
|
||||
setStableId(nestedView, mStableId);
|
||||
}
|
||||
@@ -3780,7 +3803,7 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
* @param parcel
|
||||
*/
|
||||
public RemoteViews(Parcel parcel) {
|
||||
this(parcel, /* rootParent= */ null, /* info= */ null, /* depth= */ 0);
|
||||
this(parcel, /* rootData= */ null, /* info= */ null, /* depth= */ 0);
|
||||
}
|
||||
|
||||
private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData,
|
||||
@@ -5580,6 +5603,16 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
return result;
|
||||
}
|
||||
|
||||
private View applyNestedViews(Context context, ViewGroup directParent,
|
||||
ViewGroup rootParent, InteractionHandler handler, SizeF size,
|
||||
ColorResources colorResources) {
|
||||
RemoteViews rvToApply = getRemoteViewsToApply(context, size);
|
||||
|
||||
View result = inflateView(context, rvToApply, directParent, 0, colorResources);
|
||||
rvToApply.performApply(result, rootParent, handler, colorResources);
|
||||
return result;
|
||||
}
|
||||
|
||||
private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
|
||||
return inflateView(context, rv, parent, 0, null);
|
||||
}
|
||||
@@ -5895,6 +5928,12 @@ public class RemoteViews implements Parcelable, Filter {
|
||||
}
|
||||
}
|
||||
|
||||
private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
|
||||
InteractionHandler handler, SizeF size, ColorResources colorResources) {
|
||||
RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
|
||||
rvToApply.performApply(v, rootParent, handler, colorResources);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all the actions to the provided view, moving as much of the task on the background
|
||||
* thread as possible.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
**
|
||||
** Copyright 2008, 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
**
|
||||
** Copyright 2008, 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/themed_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:theme="@style/RelativeLayoutAlignTop25Alpha"/>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2016 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
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/light_background_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
21
core/tests/coretests/res/layout/remote_views_list.xml
Normal file
21
core/tests/coretests/res/layout/remote_views_list.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2016 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
|
||||
-->
|
||||
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
||||
@@ -34,6 +34,16 @@
|
||||
<style name="LayoutInDisplayCutoutModeAlways">
|
||||
<item name="android:windowLayoutInDisplayCutoutMode">always</item>
|
||||
</style>
|
||||
<style name="RelativeLayoutAlignBottom50Alpha">
|
||||
<item name="android:layout_alignParentTop">false</item>
|
||||
<item name="android:layout_alignParentBottom">true</item>
|
||||
<item name="android:alpha">0.5</item>
|
||||
</style>
|
||||
<style name="RelativeLayoutAlignTop25Alpha">
|
||||
<item name="android:layout_alignParentTop">true</item>
|
||||
<item name="android:layout_alignParentBottom">false</item>
|
||||
<item name="android:alpha">0.25</item>
|
||||
</style>
|
||||
<style name="WindowBackgroundColorLiteral">
|
||||
<item name="android:windowBackground">#00FF00</item>
|
||||
</style>
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import static com.android.internal.R.id.pending_intent_tag;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -31,7 +35,10 @@ import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Binder;
|
||||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.util.SizeF;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@@ -49,6 +56,7 @@ import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
@@ -261,6 +269,148 @@ public class RemoteViewsTest {
|
||||
verifyViewTree(syncView, asyncView, "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedViews_setRemoteAdapter_intent() {
|
||||
Looper.prepare();
|
||||
|
||||
AppWidgetHostView widget = new AppWidgetHostView(mContext);
|
||||
RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list);
|
||||
inner2.setRemoteAdapter(R.id.list, new Intent());
|
||||
inner1.addView(R.id.container, inner2);
|
||||
top.addView(R.id.container, inner1);
|
||||
|
||||
View view = top.apply(mContext, widget);
|
||||
widget.addView(view);
|
||||
|
||||
ListView listView = (ListView) view.findViewById(R.id.list);
|
||||
listView.onRemoteAdapterConnected();
|
||||
assertNotNull(listView.getAdapter());
|
||||
|
||||
top.reapply(mContext, view);
|
||||
listView = (ListView) view.findViewById(R.id.list);
|
||||
assertNotNull(listView.getAdapter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedViews_setRemoteAdapter_remoteCollectionItems() {
|
||||
AppWidgetHostView widget = new AppWidgetHostView(mContext);
|
||||
RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list);
|
||||
inner2.setRemoteAdapter(
|
||||
R.id.list,
|
||||
new RemoteViews.RemoteCollectionItems.Builder()
|
||||
.addItem(0, new RemoteViews(mPackage, R.layout.remote_view_host))
|
||||
.build());
|
||||
inner1.addView(R.id.container, inner2);
|
||||
top.addView(R.id.container, inner1);
|
||||
|
||||
View view = top.apply(mContext, widget);
|
||||
widget.addView(view);
|
||||
|
||||
ListView listView = (ListView) view.findViewById(R.id.list);
|
||||
assertNotNull(listView.getAdapter());
|
||||
|
||||
top.reapply(mContext, view);
|
||||
listView = (ListView) view.findViewById(R.id.list);
|
||||
assertNotNull(listView.getAdapter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedViews_collectionChildFlag() throws Exception {
|
||||
RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text);
|
||||
nested.setOnClickPendingIntent(
|
||||
R.id.text,
|
||||
PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
|
||||
);
|
||||
|
||||
RemoteViews listItem = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
listItem.addView(R.id.container, nested);
|
||||
listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
|
||||
|
||||
View view = listItem.apply(mContext, mContainer);
|
||||
TextView text = (TextView) view.findViewById(R.id.text);
|
||||
assertNull(text.getTag(pending_intent_tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void landscapePortraitViews_collectionChildFlag() throws Exception {
|
||||
RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
|
||||
inner.setOnClickPendingIntent(
|
||||
R.id.text,
|
||||
PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
|
||||
);
|
||||
|
||||
RemoteViews listItem = new RemoteViews(inner, inner);
|
||||
listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
|
||||
|
||||
View view = listItem.apply(mContext, mContainer);
|
||||
TextView text = (TextView) view.findViewById(R.id.text);
|
||||
assertNull(text.getTag(pending_intent_tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sizedViews_collectionChildFlag() throws Exception {
|
||||
RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
|
||||
inner.setOnClickPendingIntent(
|
||||
R.id.text,
|
||||
PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE)
|
||||
);
|
||||
|
||||
RemoteViews listItem = new RemoteViews(
|
||||
Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
|
||||
listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD);
|
||||
|
||||
View view = listItem.apply(mContext, mContainer);
|
||||
TextView text = (TextView) view.findViewById(R.id.text);
|
||||
assertNull(text.getTag(pending_intent_tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedViews_lightBackgroundLayoutFlag() {
|
||||
RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text);
|
||||
nested.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
|
||||
|
||||
RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
parent.addView(R.id.container, nested);
|
||||
parent.setLightBackgroundLayoutId(R.layout.remote_view_host);
|
||||
parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
|
||||
|
||||
View view = parent.apply(mContext, mContainer);
|
||||
assertNull(view.findViewById(R.id.text));
|
||||
assertNotNull(view.findViewById(R.id.light_background_text));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void landscapePortraitViews_lightBackgroundLayoutFlag() {
|
||||
RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
|
||||
inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
|
||||
|
||||
RemoteViews parent = new RemoteViews(inner, inner);
|
||||
parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
|
||||
|
||||
View view = parent.apply(mContext, mContainer);
|
||||
assertNull(view.findViewById(R.id.text));
|
||||
assertNotNull(view.findViewById(R.id.light_background_text));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sizedViews_lightBackgroundLayoutFlag() {
|
||||
RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text);
|
||||
inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text);
|
||||
|
||||
RemoteViews parent = new RemoteViews(
|
||||
Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner));
|
||||
parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT);
|
||||
|
||||
View view = parent.apply(mContext, mContainer);
|
||||
assertNull(view.findViewById(R.id.text));
|
||||
assertNotNull(view.findViewById(R.id.light_background_text));
|
||||
}
|
||||
|
||||
private RemoteViews createViewChained(int depth, String... texts) {
|
||||
RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host);
|
||||
|
||||
@@ -483,6 +633,47 @@ public class RemoteViewsTest {
|
||||
index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedViews_themesPropagateCorrectly() {
|
||||
Context themedContext =
|
||||
new ContextThemeWrapper(mContext, R.style.RelativeLayoutAlignBottom50Alpha);
|
||||
RelativeLayout rootParent = new RelativeLayout(themedContext);
|
||||
|
||||
RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_relative_layout);
|
||||
RemoteViews inner1 =
|
||||
new RemoteViews(mPackage, R.layout.remote_view_relative_layout_with_theme);
|
||||
RemoteViews inner2 =
|
||||
new RemoteViews(mPackage, R.layout.remote_view_relative_layout);
|
||||
|
||||
inner1.addView(R.id.themed_layout, inner2);
|
||||
top.addView(R.id.container, inner1);
|
||||
|
||||
RelativeLayout root = (RelativeLayout) top.apply(themedContext, rootParent);
|
||||
assertEquals(0.5, root.getAlpha(), 0.);
|
||||
RelativeLayout.LayoutParams rootParams =
|
||||
(RelativeLayout.LayoutParams) root.getLayoutParams();
|
||||
assertEquals(RelativeLayout.TRUE,
|
||||
rootParams.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM));
|
||||
|
||||
// The theme is set on inner1View and its descendants. However, inner1View does
|
||||
// not get its layout params from its theme (though its descendants do), but other
|
||||
// attributes such as alpha are set.
|
||||
RelativeLayout inner1View = (RelativeLayout) root.getChildAt(0);
|
||||
assertEquals(R.id.themed_layout, inner1View.getId());
|
||||
assertEquals(0.25, inner1View.getAlpha(), 0.);
|
||||
RelativeLayout.LayoutParams inner1Params =
|
||||
(RelativeLayout.LayoutParams) inner1View.getLayoutParams();
|
||||
assertEquals(RelativeLayout.TRUE,
|
||||
inner1Params.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM));
|
||||
|
||||
RelativeLayout inner2View = (RelativeLayout) inner1View.getChildAt(0);
|
||||
assertEquals(0.25, inner2View.getAlpha(), 0.);
|
||||
RelativeLayout.LayoutParams inner2Params =
|
||||
(RelativeLayout.LayoutParams) inner2View.getLayoutParams();
|
||||
assertEquals(RelativeLayout.TRUE,
|
||||
inner2Params.getRule(RelativeLayout.ALIGN_PARENT_TOP));
|
||||
}
|
||||
|
||||
private class WidgetContainer extends AppWidgetHostView {
|
||||
int[] mSharedViewIds;
|
||||
String[] mSharedViewNames;
|
||||
|
||||
Reference in New Issue
Block a user