diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | api/system-current.txt | 1 | ||||
| -rw-r--r-- | api/test-current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/appwidget/AppWidgetHostView.java | 6 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java | 143 |
5 files changed, 149 insertions, 3 deletions
diff --git a/api/current.txt b/api/current.txt index ec16f5a5f56b..017320be1499 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6759,6 +6759,7 @@ package android.appwidget { method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setAsyncExecutor(java.util.concurrent.Executor); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); diff --git a/api/system-current.txt b/api/system-current.txt index 4bba111f9392..1cb5c606b4ae 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7072,6 +7072,7 @@ package android.appwidget { method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setAsyncExecutor(java.util.concurrent.Executor); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); diff --git a/api/test-current.txt b/api/test-current.txt index d4fb027bcb4d..86cc1c3e4772 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6781,6 +6781,7 @@ package android.appwidget { method protected android.view.View getErrorView(); method protected void prepareView(android.view.View); method public void setAppWidget(int, android.appwidget.AppWidgetProviderInfo); + method public void setAsyncExecutor(java.util.concurrent.Executor); method public void updateAppWidget(android.widget.RemoteViews); method public void updateAppWidgetOptions(android.os.Bundle); method public void updateAppWidgetSize(android.os.Bundle, int, int, int, int); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 4c8360f6c345..ef3b1c50ffd3 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -347,11 +347,11 @@ public class AppWidgetHostView extends FrameLayout { } /** - * Sets an executor which can be used for asynchronously inflating and applying the remoteviews. - * @see {@link RemoteViews#applyAsync(Context, ViewGroup, RemoteViews.OnViewAppliedListener, Executor)} + * Sets an executor which can be used for asynchronously inflating. CPU intensive tasks like + * view inflation or loading images will be performed on the executor. The updates will still + * be applied on the UI thread. * * @param executor the executor to use or null. - * @hide */ public void setAsyncExecutor(Executor executor) { if (mLastExecutionSignal != null) { diff --git a/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java new file mode 100644 index 000000000000..1e55fb182b4a --- /dev/null +++ b/core/tests/coretests/src/android/widget/AppWidgetHostViewTest.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 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.widget; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.appwidget.AppWidgetHostView; +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.view.View; +import android.view.ViewGroup.OnHierarchyChangeListener; + +import com.android.frameworks.coretests.R; + +import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +/** + * Tests for AppWidgetHostView + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class AppWidgetHostViewTest { + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + private Context mContext; + private String mPackage; + private AppWidgetHostView mHostView; + + private ViewAddListener mViewAddListener; + private RemoteViews mViews; + + @Before + public void setup() { + mContext = InstrumentationRegistry.getContext(); + mPackage = mContext.getPackageName(); + mHostView = new AppWidgetHostView(mContext); + mHostView.setAppWidget(0, AppWidgetManager.getInstance( + mContext).getInstalledProviders().get(0)); + + mViewAddListener = new ViewAddListener(); + mHostView.setOnHierarchyChangeListener(mViewAddListener); + + mViews = new RemoteViews(mPackage, R.layout.remote_views_test); + } + + @Test + public void syncInflation() { + mHostView.updateAppWidget(mViews); + assertNotNull(mHostView.findViewById(R.id.image)); + } + + @Test + public void asyncInflation() throws Exception { + RunnableList executor = new RunnableList(); + mHostView.setAsyncExecutor(executor); + + mHostView.updateAppWidget(mViews); + assertNull(mHostView.findViewById(R.id.image)); + + // Task queued. + assertEquals(1, executor.size()); + + // Execute the pending task + executor.get(0).run(); + mViewAddListener.addLatch.await(); + assertNotNull(mHostView.findViewById(R.id.image)); + } + + @Test + public void asyncInflation_cancelled() throws Exception { + RunnableList executor = new RunnableList(); + mHostView.setAsyncExecutor(executor); + + mHostView.updateAppWidget(mViews.clone()); + mHostView.updateAppWidget(mViews.clone()); + assertNull(mHostView.findViewById(R.id.image)); + + // Tasks queued. + assertEquals(2, executor.size()); + // First task cancelled + assertTrue(((Future) executor.get(0)).isCancelled()); + + // Execute the pending task + executor.get(0).run(); + executor.get(1).run(); + mViewAddListener.addLatch.await(); + assertNotNull(mHostView.findViewById(R.id.image)); + } + + private static class RunnableList extends ArrayList<Runnable> implements Executor { + + @Override + public void execute(Runnable runnable) { + add(runnable); + } + } + + private class ViewAddListener implements OnHierarchyChangeListener { + + public final CountDownLatch addLatch = new CountDownLatch(1); + + + @Override + public void onChildViewAdded(View parent, View child) { + addLatch.countDown(); + } + + @Override + public void onChildViewRemoved(View parent, View child) { + } + } +} |