1e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal/*
2e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * Copyright (C) 2017 The Android Open Source Project
3e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal *
4e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * Licensed under the Apache License, Version 2.0 (the "License");
5e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * you may not use this file except in compliance with the License.
6e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * You may obtain a copy of the License at
7e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal *
8e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal *      http://www.apache.org/licenses/LICENSE-2.0
9e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal *
10e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * Unless required by applicable law or agreed to in writing, software
11e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * distributed under the License is distributed on an "AS IS" BASIS,
12e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * See the License for the specific language governing permissions and
14e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * limitations under the License.
15e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal */
16e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
17e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalpackage android.widget;
18e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
19e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.junit.Assert.assertEquals;
20e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.junit.Assert.assertFalse;
21e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.junit.Assert.assertNotNull;
22e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.junit.Assert.assertTrue;
23e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.any;
24e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.anyInt;
25e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.mock;
26e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.never;
27e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.times;
28e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.verify;
29e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport static org.mockito.Mockito.when;
30e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
31e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.app.IServiceConnection;
32e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.appwidget.AppWidgetManager;
33e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.content.Context;
34e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.content.ContextWrapper;
35e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.content.Intent;
36e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.content.ServiceConnection;
37e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.database.DataSetObserver;
38e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.os.Handler;
39e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.os.Looper;
40e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.os.RemoteException;
41e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.support.test.InstrumentationRegistry;
42e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.support.test.filters.SmallTest;
43e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.support.test.runner.AndroidJUnit4;
44e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport android.view.View;
45e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
46e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport com.android.frameworks.coretests.R;
47e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport com.android.internal.widget.IRemoteViewsFactory;
48e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
49e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport org.junit.Before;
50e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport org.junit.Test;
51e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport org.junit.runner.RunWith;
52e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport org.mockito.Mock;
53e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport org.mockito.MockitoAnnotations;
54e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
55e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport java.util.ArrayList;
56e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport java.util.concurrent.CountDownLatch;
57e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport java.util.concurrent.TimeUnit;
58e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalimport java.util.function.Supplier;
59e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
60e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal/**
61e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal * Tests for RemoteViewsAdapter.
62e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal */
63e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal@RunWith(AndroidJUnit4.class)
64e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal@SmallTest
65e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyalpublic class RemoteViewsAdapterTest {
66e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
67e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Mock AppWidgetManager mAppWidgetManager;
68e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Mock IServiceConnection mIServiceConnection;
69e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Mock RemoteViewsAdapter.RemoteAdapterConnectionCallback mCallback;
70e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
71e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private Handler mMainHandler;
72e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private TestContext mContext;
73e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
74e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Before
75e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    public void setup() {
76e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        MockitoAnnotations.initMocks(this);
77e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        when(mAppWidgetManager
78e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                .bindRemoteViewsService(any(), anyInt(), any(), any(), anyInt())).thenReturn(true);
79e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mContext = new TestContext();
80e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mMainHandler = new Handler(Looper.getMainLooper());
81e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
82e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
83e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Test
84e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    public void onRemoteAdapterConnected_after_metadata_loaded() throws Throwable {
85e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        RemoteViewsAdapter adapter = getOnUiThread(
86e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
87e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertFalse(adapter.isDataReady());
88e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
89e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertNotNull(mContext.conn.get());
90e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
91e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ViewsFactory factory = new ViewsFactory(1);
92e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mContext.sendConnect(factory);
93e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
94e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
95e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        verify(mCallback, never()).onRemoteAdapterConnected();
96e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
97e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.loadingView.set(createViews("loading"));
98e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
99e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
100e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        verify(mCallback, times(1)).onRemoteAdapterConnected();
101e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
102e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
103e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
104e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Service is unbound
105e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertTrue(isUnboundOrScheduled());
106e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
107e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
108e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Test
109e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    public void viewReplaced_after_mainView_loaded() throws Throwable {
110e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        RemoteViewsAdapter adapter = getOnUiThread(
111e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
112e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
113e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ViewsFactory factory = new ViewsFactory(1);
114e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.loadingView.set(createViews("loading"));
115e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mContext.sendConnect(factory);
116e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
117e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
118e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
119e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
120e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Returned view contains the loading text
121e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        View view = getOnUiThread(() -> adapter.getView(0, null, new FrameLayout(mContext)));
122e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ArrayList<View> search = new ArrayList<>();
123e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
124e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals(1, search.size());
125e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
126e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Send the final remoteViews
127e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.views[0].set(createViews("updated"));
128e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
129e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
130e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
131e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Existing view got updated with new text
132e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        search.clear();
133e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
134e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertTrue(search.isEmpty());
135e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        view.findViewsWithText(search, "updated", View.FIND_VIEWS_WITH_TEXT);
136e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals(1, search.size());
137e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
138e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Service is unbound
139e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertTrue(isUnboundOrScheduled());
140e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
141e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
142e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Test
143e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    public void notifyDataSetChanged_deferred() throws Throwable {
144e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        RemoteViewsAdapter adapter = getOnUiThread(
145e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
146e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
147e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ViewsFactory factory = new ViewsFactory(1);
148e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.loadingView.set(createViews("loading"));
149e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mContext.sendConnect(factory);
150e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
151e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
152e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
153e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
154e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
155e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Reset the loading view so that next refresh is blocked
156e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.loadingView = new LockedValue<>();
157e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.mCount = 3;
158e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        DataSetObserver observer = mock(DataSetObserver.class);
159e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        getOnUiThread(() -> {
160e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            adapter.registerDataSetObserver(observer);
161e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            adapter.notifyDataSetChanged();
162e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return null;
163e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        });
164e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
165e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
166e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Still giving the old values
167e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        verify(observer, never()).onChanged();
168e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
169e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
170e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.loadingView.set(createViews("refreshed"));
171e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
172e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
173e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
174e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // When the service returns new data, UI is updated.
175e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        verify(observer, times(1)).onChanged();
176e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals((Integer) 3, getOnUiThread(adapter::getCount));
177e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
178e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Service is unbound
179e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertTrue(isUnboundOrScheduled());
180e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
181e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
182e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    @Test
183e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    public void serviceDisconnected_before_getView() throws Throwable {
184e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        RemoteViewsAdapter adapter = getOnUiThread(
185e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                () -> new RemoteViewsAdapter(mContext, new DistinctIntent(), mCallback, false));
186e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
187e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ViewsFactory factory = new ViewsFactory(1);
188e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        factory.loadingView.set(createViews("loading"));
189e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mContext.sendConnect(factory);
190e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
191e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
192e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
193e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        verify(mCallback, times(1)).onRemoteAdapterConnected();
194e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals((Integer) 1, getOnUiThread(adapter::getCount));
195e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
196e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Unbind the service
197e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ServiceConnection conn = mContext.conn.get();
198e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        getOnHandler(mContext.handler.get(), () -> {
199e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            conn.onServiceDisconnected(null);
200e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return null;
201e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        });
202e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
203e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Returned view contains the loading text
204e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        View view = getOnUiThread(() -> adapter.getView(0, null, new FrameLayout(mContext)));
205e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ArrayList<View> search = new ArrayList<>();
206e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        view.findViewsWithText(search, "loading", View.FIND_VIEWS_WITH_TEXT);
207e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertEquals(1, search.size());
208e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
209e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        // Unbind is not scheduled
210e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        assertFalse(isUnboundOrScheduled());
211e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
212e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        mContext.sendConnect(factory);
213e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mContext.handler.get());
214e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        waitOnHandler(mMainHandler);
215e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        verify(mCallback, times(2)).onRemoteAdapterConnected();
216e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
217e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
218e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private RemoteViews createViews(String text) {
219e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        RemoteViews views = new RemoteViews(mContext.getPackageName(), R.layout.remote_views_text);
220e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        views.setTextViewText(R.id.text, text);
221e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        return views;
222e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
223e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
224e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private <T> T getOnUiThread(Supplier<T> supplier) throws Throwable {
225e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        return getOnHandler(mMainHandler, supplier);
226e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
227e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
228e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private boolean isUnboundOrScheduled() throws Throwable {
229e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        Handler handler = mContext.handler.get();
230e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        return getOnHandler(handler, () -> mContext.boundCount == 0
231e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                || handler.hasMessages(RemoteViewsAdapter.MSG_UNBIND_SERVICE));
232e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
233e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
234e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private static <T> T getOnHandler(Handler handler, Supplier<T> supplier) throws Throwable {
235e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        LockedValue<T> result = new LockedValue<>();
236e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        handler.post(() -> result.set(supplier.get()));
237e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        return result.get();
238e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
239e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
240e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private class TestContext extends ContextWrapper {
241e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
242e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public final LockedValue<ServiceConnection> conn = new LockedValue<>();
243e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public final LockedValue<Handler> handler = new LockedValue<>();
244e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public int boundCount;
245e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
246e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        TestContext() {
247e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            super(InstrumentationRegistry.getContext());
248e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
249e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
250e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
251e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public void unbindService(ServiceConnection conn) {
252e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            boundCount--;
253e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
254e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
255e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
256e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public Object getSystemService(String name) {
257e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            if (Context.APPWIDGET_SERVICE.equals(name)) {
258e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                return mAppWidgetManager;
259e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            }
260e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return super.getSystemService(name);
261e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
262e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
263e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
264e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public IServiceConnection getServiceDispatcher(
265e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                ServiceConnection conn, Handler handler, int flags) {
266e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            this.conn.set(conn);
267e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            this.handler.set(handler);
268e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            boundCount++;
269e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return mIServiceConnection;
270e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
271e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
272e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
273e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public Context getApplicationContext() {
274e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return this;
275e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
276e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
277e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public void sendConnect(ViewsFactory factory) throws Exception {
278e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            ServiceConnection connection = conn.get();
279e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            handler.get().post(() -> connection.onServiceConnected(null, factory.asBinder()));
280e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
281e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
282e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
283e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private static void waitOnHandler(Handler handler) throws Exception {
284e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        CountDownLatch latch = new CountDownLatch(1);
285e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        handler.post(() -> latch.countDown());
286e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        latch.await(20, TimeUnit.SECONDS);
287e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
288e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
289e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private static class ViewsFactory extends IRemoteViewsFactory.Stub {
290e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
291e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public LockedValue<RemoteViews> loadingView = new LockedValue<>();
292e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public LockedValue<RemoteViews>[] views;
293e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
294e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        private int mCount;
295e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
296e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        ViewsFactory(int count) {
297e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            mCount = count;
298e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            views = new LockedValue[count];
299e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            for (int i = 0; i < count; i++) {
300e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                views[i] = new LockedValue<>();
301e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            }
302e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
303e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
304e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
305e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public void onDataSetChanged() {}
306e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
307e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
308e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public void onDataSetChangedAsync() { }
309e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
310e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
311e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public void onDestroy(Intent intent) { }
312e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
313e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
314e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public int getCount() throws RemoteException {
315e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return mCount;
316e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
317e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
318e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
319e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public RemoteViews getViewAt(int position) throws RemoteException {
320e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            try {
321e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                return views[position].get();
322e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            } catch (Exception e) {
323e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                throw new RemoteException(e.getMessage());
324e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            }
325e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
326e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
327e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
328e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public RemoteViews getLoadingView() throws RemoteException {
329e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            try {
330e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                return loadingView.get();
331e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            } catch (Exception e) {
332e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal                throw new RemoteException(e.getMessage());
333e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            }
334e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
335e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
336e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
337e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public int getViewTypeCount() {
338e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return 1;
339e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
340e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
341e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
342e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public long getItemId(int position) {
343e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return position;
344e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
345e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
346e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
347e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public boolean hasStableIds() {
348e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return false;
349e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
350e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
351e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
352e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public boolean isCreated() {
353e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return false;
354e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
355e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
356e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
357e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private static class DistinctIntent extends Intent {
358e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
359e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        @Override
360e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public boolean filterEquals(Intent other) {
361e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return false;
362e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
363e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
364e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
365e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    private static class LockedValue<T> {
366e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
367e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        private final CountDownLatch mLatch = new CountDownLatch(1);
368e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        private T mValue;
369e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
370e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public void set(T value) {
371e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            mValue = value;
372e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            mLatch.countDown();
373e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
374e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal
375e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        public T get() throws Exception {
376e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            mLatch.await(10, TimeUnit.SECONDS);
377e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal            return mValue;
378e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal        }
379e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal    }
380e1273ebbb7d0d208d0bb0304ccefc5659633068cSunny Goyal}
381