Loading packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +4 −17 Original line number Diff line number Diff line Loading @@ -18,14 +18,9 @@ package com.android.systemui.qs.customize; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.app.AlertDialog; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; Loading @@ -36,8 +31,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.LinearLayout; import android.widget.Toolbar; import android.widget.Toolbar.OnMenuItemClickListener; Loading @@ -48,12 +41,10 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSContainerImpl; import com.android.systemui.qs.QSDetailClipper; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback; Loading @@ -73,6 +64,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private final QSDetailClipper mClipper; private final LightBarController mLightBarController; private final TileQueryHelper mTileQueryHelper; private boolean isShown; private QSTileHost mHost; Loading @@ -82,7 +74,6 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private boolean mCustomizing; private NotificationsQuickSettingsContainer mNotifQsContainer; private QS mQs; private boolean mFinishedFetchingTiles = false; private int mX; private int mY; private boolean mOpening; Loading Loading @@ -112,6 +103,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mRecyclerView = findViewById(android.R.id.list); mTileAdapter = new TileAdapter(getContext()); mTileQueryHelper = new TileQueryHelper(context, mTileAdapter); mRecyclerView.setAdapter(mTileAdapter); mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView); GridLayoutManager layout = new GridLayoutManager(getContext(), 3); Loading Loading @@ -193,12 +185,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene } private void queryTiles() { mFinishedFetchingTiles = false; Runnable tileQueryFetchCompletion = () -> { Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(() -> mFinishedFetchingTiles = true); }; new TileQueryHelper(mContext, mHost, mTileAdapter, tileQueryFetchCompletion); mTileQueryHelper.queryTiles(mHost); } public void hide(int x, int y) { Loading Loading @@ -259,7 +246,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene } private void save() { if (mFinishedFetchingTiles) { if (mTileQueryHelper.isFinished()) { mTileAdapter.saveSpecs(mHost); } } Loading packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +54 −45 Original line number Diff line number Diff line Loading @@ -25,60 +25,63 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.service.quicksettings.TileService; import android.text.TextUtils; import android.util.ArraySet; import android.widget.Button; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class TileQueryHelper { private static final String TAG = "TileQueryHelper"; private final ArrayList<TileInfo> mTiles = new ArrayList<>(); private final ArrayList<String> mSpecs = new ArrayList<>(); private final ArraySet<String> mSpecs = new ArraySet<>(); private final Handler mBgHandler; private final Handler mMainHandler; private final Context mContext; private final TileStateListener mListener; private final QSTileHost mHost; private final Runnable mCompletion; public TileQueryHelper(Context context, QSTileHost host, TileStateListener listener, Runnable completion) { private boolean mFinished; public TileQueryHelper(Context context, TileStateListener listener) { mContext = context; mListener = listener; mHost = host; mCompletion = completion; addSystemTiles(); // TODO: Live? mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); mMainHandler = Dependency.get(Dependency.MAIN_HANDLER); } private void addSystemTiles() { public void queryTiles(QSTileHost host) { mTiles.clear(); mSpecs.clear(); mFinished = false; // Enqueue jobs to fetch every system tile and then ever package tile. final Handler qsHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER)); final Handler mainHandler = new Handler(Looper.getMainLooper()); addStockTiles(mainHandler, qsHandler); addPackageTiles(mainHandler, qsHandler); // Then enqueue the completion. It should always be last qsHandler.post(mCompletion); addStockTiles(host); addPackageTiles(host); // TODO: Live? } public boolean isFinished() { return mFinished; } private void addStockTiles(Handler mainHandler, Handler bgHandler) { private void addStockTiles(QSTileHost host) { String possible = mContext.getString(R.string.quick_settings_tiles_stock); String[] possibleTiles = possible.split(","); final ArrayList<QSTile> tilesToAdd = new ArrayList<>(); for (int i = 0; i < possibleTiles.length; i++) { final String spec = possibleTiles[i]; final QSTile tile = mHost.createTile(spec); final QSTile tile = host.createTile(spec); if (tile == null) { continue; } else if (!tile.isAvailable()) { Loading @@ -89,28 +92,25 @@ public class TileQueryHelper { tile.clearState(); tile.refreshState(); tile.setListening(this, false); bgHandler.post(new Runnable() { @Override public void run() { tile.setTileSpec(spec); tilesToAdd.add(tile); } mBgHandler.post(() -> { for (QSTile tile : tilesToAdd) { final QSTile.State state = tile.getState().copy(); // Ignore the current state and get the generic label instead. state.label = tile.getTileLabel(); tile.destroy(); mainHandler.post(new Runnable() { @Override public void run() { addTile(spec, null, state, true); mListener.onTilesChanged(mTiles); addTile(tile.getTileSpec(), null, state, true); } notifyTilesChanged(false); }); } }); } } private void addPackageTiles(Handler mainHandler, Handler bgHandler) { bgHandler.post(() -> { Collection<QSTile> params = mHost.getTiles(); private void addPackageTiles(final QSTileHost host) { mBgHandler.post(() -> { Collection<QSTile> params = host.getTiles(); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser()); Loading Loading @@ -145,9 +145,18 @@ public class TileQueryHelper { icon.mutate(); icon.setTint(mContext.getColor(android.R.color.white)); CharSequence label = info.serviceInfo.loadLabel(pm); addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext); addTile(spec, icon, label != null ? label.toString() : "null", appLabel); } mainHandler.post(() -> mListener.onTilesChanged(mTiles)); notifyTilesChanged(true); }); } private void notifyTilesChanged(final boolean finished) { final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles); mMainHandler.post(() -> { mListener.onTilesChanged(tilesToReturn); mFinished = finished; }); } Loading Loading @@ -177,8 +186,8 @@ public class TileQueryHelper { mSpecs.add(spec); } private void addTile(String spec, Drawable drawable, CharSequence label, CharSequence appLabel, Context context) { private void addTile( String spec, Drawable drawable, CharSequence label, CharSequence appLabel) { QSTile.State state = new QSTile.State(); state.label = label; state.contentDescription = label; Loading packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java +0 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.service.quicksettings.TileService; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener; import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener; import java.util.List; Loading packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +52 −36 Original line number Diff line number Diff line Loading @@ -15,71 +15,87 @@ package com.android.systemui.qs.customize; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.android.systemui.Dependency; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.QSTileHost; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSTileHost; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import android.os.Message; import android.test.suitebuilder.annotation.SmallTest; import java.util.ArrayList; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class TileQueryHelperTest extends SysuiTestCase { @Mock private TileQueryHelper.TileStateListener mListener; @Mock private QSTileHost mQSTileHost; private TestableLooper mBGLooper; private Runnable mLastCallback; private TileQueryHelper mTileQueryHelper; @Before public void setup() { MockitoAnnotations.initMocks(this); mBGLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper()); mTileQueryHelper = new TileQueryHelper(mContext, mListener); } @Test public void testIsFinished_falseBeforeQuerying() { assertFalse(mTileQueryHelper.isFinished()); } @Test public void testIsFinished_trueAfterQuerying() { mTileQueryHelper.queryTiles(mQSTileHost); mBGLooper.processAllMessages(); waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); assertTrue(mTileQueryHelper.isFinished()); } @Test public void testCompletionCalled() { QSTileHost mockHost = mock(QSTileHost.class); TileAdapter mockAdapter = mock(TileAdapter.class); Runnable mockCompletion = mock(Runnable.class); new TileQueryHelper(mContext, mockHost, mockAdapter, mockCompletion); public void testQueryTiles_callsListenerTwice() { mTileQueryHelper.queryTiles(mQSTileHost); mBGLooper.processAllMessages(); verify(mockCompletion).run(); waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); verify(mListener, times(2)).onTilesChanged(any()); } @Test public void testCompletionCalledAfterTilesFetched() { QSTile mockTile = mock(QSTile.class); State mockState = mock(State.class); when(mockState.copy()).thenReturn(mockState); when(mockTile.getState()).thenReturn(mockState); when(mockTile.isAvailable()).thenReturn(true); QSTileHost mockHost = mock(QSTileHost.class); when(mockHost.createTile(any())).thenReturn(mockTile); mBGLooper.setMessageHandler((Message m) -> { mLastCallback = m.getCallback(); return true; }); TileAdapter mockAdapter = mock(TileAdapter.class); Runnable mockCompletion = mock(Runnable.class); new TileQueryHelper(mContext, mockHost, mockAdapter, mockCompletion); // Verify that the last thing in the queue was our callback public void testQueryTiles_isFinishedFalseOnListenerCalls_thenTrueAfterCompletion() { doAnswer(invocation -> { assertFalse(mTileQueryHelper.isFinished()); return null; }).when(mListener).onTilesChanged(any()); mTileQueryHelper.queryTiles(mQSTileHost); mBGLooper.processAllMessages(); assertEquals(mockCompletion, mLastCallback); waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); assertTrue(mTileQueryHelper.isFinished()); } } Loading
packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +4 −17 Original line number Diff line number Diff line Loading @@ -18,14 +18,9 @@ package com.android.systemui.qs.customize; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.app.AlertDialog; import android.content.Context; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; Loading @@ -36,8 +31,6 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.LinearLayout; import android.widget.Toolbar; import android.widget.Toolbar.OnMenuItemClickListener; Loading @@ -48,12 +41,10 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSContainerImpl; import com.android.systemui.qs.QSDetailClipper; import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback; Loading @@ -73,6 +64,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private final QSDetailClipper mClipper; private final LightBarController mLightBarController; private final TileQueryHelper mTileQueryHelper; private boolean isShown; private QSTileHost mHost; Loading @@ -82,7 +74,6 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private boolean mCustomizing; private NotificationsQuickSettingsContainer mNotifQsContainer; private QS mQs; private boolean mFinishedFetchingTiles = false; private int mX; private int mY; private boolean mOpening; Loading Loading @@ -112,6 +103,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mRecyclerView = findViewById(android.R.id.list); mTileAdapter = new TileAdapter(getContext()); mTileQueryHelper = new TileQueryHelper(context, mTileAdapter); mRecyclerView.setAdapter(mTileAdapter); mTileAdapter.getItemTouchHelper().attachToRecyclerView(mRecyclerView); GridLayoutManager layout = new GridLayoutManager(getContext(), 3); Loading Loading @@ -193,12 +185,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene } private void queryTiles() { mFinishedFetchingTiles = false; Runnable tileQueryFetchCompletion = () -> { Handler mainHandler = new Handler(Looper.getMainLooper()); mainHandler.post(() -> mFinishedFetchingTiles = true); }; new TileQueryHelper(mContext, mHost, mTileAdapter, tileQueryFetchCompletion); mTileQueryHelper.queryTiles(mHost); } public void hide(int x, int y) { Loading Loading @@ -259,7 +246,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene } private void save() { if (mFinishedFetchingTiles) { if (mTileQueryHelper.isFinished()) { mTileAdapter.saveSpecs(mHost); } } Loading
packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java +54 −45 Original line number Diff line number Diff line Loading @@ -25,60 +25,63 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.service.quicksettings.TileService; import android.text.TextUtils; import android.util.ArraySet; import android.widget.Button; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class TileQueryHelper { private static final String TAG = "TileQueryHelper"; private final ArrayList<TileInfo> mTiles = new ArrayList<>(); private final ArrayList<String> mSpecs = new ArrayList<>(); private final ArraySet<String> mSpecs = new ArraySet<>(); private final Handler mBgHandler; private final Handler mMainHandler; private final Context mContext; private final TileStateListener mListener; private final QSTileHost mHost; private final Runnable mCompletion; public TileQueryHelper(Context context, QSTileHost host, TileStateListener listener, Runnable completion) { private boolean mFinished; public TileQueryHelper(Context context, TileStateListener listener) { mContext = context; mListener = listener; mHost = host; mCompletion = completion; addSystemTiles(); // TODO: Live? mBgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER)); mMainHandler = Dependency.get(Dependency.MAIN_HANDLER); } private void addSystemTiles() { public void queryTiles(QSTileHost host) { mTiles.clear(); mSpecs.clear(); mFinished = false; // Enqueue jobs to fetch every system tile and then ever package tile. final Handler qsHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER)); final Handler mainHandler = new Handler(Looper.getMainLooper()); addStockTiles(mainHandler, qsHandler); addPackageTiles(mainHandler, qsHandler); // Then enqueue the completion. It should always be last qsHandler.post(mCompletion); addStockTiles(host); addPackageTiles(host); // TODO: Live? } public boolean isFinished() { return mFinished; } private void addStockTiles(Handler mainHandler, Handler bgHandler) { private void addStockTiles(QSTileHost host) { String possible = mContext.getString(R.string.quick_settings_tiles_stock); String[] possibleTiles = possible.split(","); final ArrayList<QSTile> tilesToAdd = new ArrayList<>(); for (int i = 0; i < possibleTiles.length; i++) { final String spec = possibleTiles[i]; final QSTile tile = mHost.createTile(spec); final QSTile tile = host.createTile(spec); if (tile == null) { continue; } else if (!tile.isAvailable()) { Loading @@ -89,28 +92,25 @@ public class TileQueryHelper { tile.clearState(); tile.refreshState(); tile.setListening(this, false); bgHandler.post(new Runnable() { @Override public void run() { tile.setTileSpec(spec); tilesToAdd.add(tile); } mBgHandler.post(() -> { for (QSTile tile : tilesToAdd) { final QSTile.State state = tile.getState().copy(); // Ignore the current state and get the generic label instead. state.label = tile.getTileLabel(); tile.destroy(); mainHandler.post(new Runnable() { @Override public void run() { addTile(spec, null, state, true); mListener.onTilesChanged(mTiles); addTile(tile.getTileSpec(), null, state, true); } notifyTilesChanged(false); }); } }); } } private void addPackageTiles(Handler mainHandler, Handler bgHandler) { bgHandler.post(() -> { Collection<QSTile> params = mHost.getTiles(); private void addPackageTiles(final QSTileHost host) { mBgHandler.post(() -> { Collection<QSTile> params = host.getTiles(); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> services = pm.queryIntentServicesAsUser( new Intent(TileService.ACTION_QS_TILE), 0, ActivityManager.getCurrentUser()); Loading Loading @@ -145,9 +145,18 @@ public class TileQueryHelper { icon.mutate(); icon.setTint(mContext.getColor(android.R.color.white)); CharSequence label = info.serviceInfo.loadLabel(pm); addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext); addTile(spec, icon, label != null ? label.toString() : "null", appLabel); } mainHandler.post(() -> mListener.onTilesChanged(mTiles)); notifyTilesChanged(true); }); } private void notifyTilesChanged(final boolean finished) { final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles); mMainHandler.post(() -> { mListener.onTilesChanged(tilesToReturn); mFinished = finished; }); } Loading Loading @@ -177,8 +186,8 @@ public class TileQueryHelper { mSpecs.add(spec); } private void addTile(String spec, Drawable drawable, CharSequence label, CharSequence appLabel, Context context) { private void addTile( String spec, Drawable drawable, CharSequence label, CharSequence appLabel) { QSTile.State state = new QSTile.State(); state.label = label; state.contentDescription = label; Loading
packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java +0 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.service.quicksettings.TileService; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener; import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener; import java.util.List; Loading
packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java +52 −36 Original line number Diff line number Diff line Loading @@ -15,71 +15,87 @@ package com.android.systemui.qs.customize; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.android.systemui.Dependency; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.State; import com.android.systemui.qs.QSTileHost; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.QSTileHost; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import android.os.Message; import android.test.suitebuilder.annotation.SmallTest; import java.util.ArrayList; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class TileQueryHelperTest extends SysuiTestCase { @Mock private TileQueryHelper.TileStateListener mListener; @Mock private QSTileHost mQSTileHost; private TestableLooper mBGLooper; private Runnable mLastCallback; private TileQueryHelper mTileQueryHelper; @Before public void setup() { MockitoAnnotations.initMocks(this); mBGLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper()); mTileQueryHelper = new TileQueryHelper(mContext, mListener); } @Test public void testIsFinished_falseBeforeQuerying() { assertFalse(mTileQueryHelper.isFinished()); } @Test public void testIsFinished_trueAfterQuerying() { mTileQueryHelper.queryTiles(mQSTileHost); mBGLooper.processAllMessages(); waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); assertTrue(mTileQueryHelper.isFinished()); } @Test public void testCompletionCalled() { QSTileHost mockHost = mock(QSTileHost.class); TileAdapter mockAdapter = mock(TileAdapter.class); Runnable mockCompletion = mock(Runnable.class); new TileQueryHelper(mContext, mockHost, mockAdapter, mockCompletion); public void testQueryTiles_callsListenerTwice() { mTileQueryHelper.queryTiles(mQSTileHost); mBGLooper.processAllMessages(); verify(mockCompletion).run(); waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); verify(mListener, times(2)).onTilesChanged(any()); } @Test public void testCompletionCalledAfterTilesFetched() { QSTile mockTile = mock(QSTile.class); State mockState = mock(State.class); when(mockState.copy()).thenReturn(mockState); when(mockTile.getState()).thenReturn(mockState); when(mockTile.isAvailable()).thenReturn(true); QSTileHost mockHost = mock(QSTileHost.class); when(mockHost.createTile(any())).thenReturn(mockTile); mBGLooper.setMessageHandler((Message m) -> { mLastCallback = m.getCallback(); return true; }); TileAdapter mockAdapter = mock(TileAdapter.class); Runnable mockCompletion = mock(Runnable.class); new TileQueryHelper(mContext, mockHost, mockAdapter, mockCompletion); // Verify that the last thing in the queue was our callback public void testQueryTiles_isFinishedFalseOnListenerCalls_thenTrueAfterCompletion() { doAnswer(invocation -> { assertFalse(mTileQueryHelper.isFinished()); return null; }).when(mListener).onTilesChanged(any()); mTileQueryHelper.queryTiles(mQSTileHost); mBGLooper.processAllMessages(); assertEquals(mockCompletion, mLastCallback); waitForIdleSync(Dependency.get(Dependency.MAIN_HANDLER)); assertTrue(mTileQueryHelper.isFinished()); } }