@Override protected void onPostExecute(final DownloadProgress result) { if (result != null) { /* onPostExecute is not always called on UI thread due to an old Android bug. */ HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { Distribute.getInstance().updateProgressDialog(mReleaseDetails, result); } }); } }
@Override public void run() { /* And make sure we run the original command on U.I. thread. */ HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { runIfEnabled(runnable); } }); } }, new Runnable() {
@Override public void onCallSucceeded(final String payload) { /* onPostExecute is not always called on UI thread due to an old Android bug. */ HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { try { handleApiCallSuccess(releaseCallId, payload, ReleaseDetails.parse(payload)); } catch (JSONException e) { onCallFailed(e); } } }); }
@Override public synchronized void thenAccept(final AppCenterConsumer<T> function) { if (isDone()) { HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { function.accept(mResult); } }); } else { if (mConsumers == null) { mConsumers = new LinkedList<>(); } mConsumers.add(function); } }
/** * Hide progress dialog and stop updating. */ @SuppressWarnings("deprecation") private synchronized void hideProgressDialog() { if (mProgressDialog != null) { final android.app.ProgressDialog progressDialog = mProgressDialog; mProgressDialog = null; /* This can be called from background check download task. */ HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { progressDialog.hide(); } }); HandlerUtils.getMainHandler().removeCallbacksAndMessages(HANDLER_TOKEN_CHECK_PROGRESS); } }
/** * Set result. * * @param value result. */ public synchronized void complete(final T value) { if (!isDone()) { mResult = value; mLatch.countDown(); if (mConsumers != null) { HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { /* No need to synchronize anymore as consumers cannot be modified anymore. */ for (AppCenterConsumer<T> function : mConsumers) { function.accept(value); } mConsumers = null; } }); } } }
@Override public void run() { mainThreadFirstRun.set(Thread.currentThread()); HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { mainThreadNestedRun.set(Thread.currentThread()); semaphore.release(); } }); } });
HandlerUtils.runOnUiThread(new Runnable() {
@Override public ServiceCall callAsync(String url, String method, Map<String, String> headers, CallTemplate callTemplate, final ServiceCallback serviceCallback) { final DefaultHttpClientCallTask task = new DefaultHttpClientCallTask(url, method, headers, callTemplate, serviceCallback, this); try { task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } catch (final RejectedExecutionException e) { /* * When executor saturated (shared with app), we should use the retry mechanism * rather than creating more threads to avoid putting too much pressure on the hosting app. * Also we need to return the method before calling the listener, * so we post the callback on handler to make sure of that. */ HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { serviceCallback.onCallFailed(e); } }); } return new ServiceCall() { @Override public void cancel() { /* This doesn't kill the AsyncTask, so we should check the state manually. */ task.cancel(true); } }; }
@Test public void runOnUiThread() { final AtomicReference<Thread> mainThreadFirstRun = new AtomicReference<>(); final AtomicReference<Thread> mainThreadNestedRun = new AtomicReference<>(); final Semaphore semaphore = new Semaphore(0); HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { mainThreadFirstRun.set(Thread.currentThread()); HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { mainThreadNestedRun.set(Thread.currentThread()); semaphore.release(); } }); } }); semaphore.acquireUninterruptibly(); assertNotNull(mainThreadFirstRun.get()); assertEquals(mainThreadFirstRun.get(), mainThreadNestedRun.get()); assertNotEquals(Thread.currentThread(), mainThreadNestedRun.get()); } }
@Before public void setUp() throws Exception { mockStatic(AppCenterLog.class); mockStatic(IdHelper.class, new Returns(UUIDUtils.randomUUID())); mockStatic(DeviceInfoHelper.class); when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenReturn(mock(Device.class)); when(mAppCenterHandler.post(any(Runnable.class))).then(new Answer<Boolean>() { @Override public Boolean answer(InvocationOnMock invocation) { ((Runnable) invocation.getArguments()[0]).run(); return true; } }); mockStatic(HandlerUtils.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) { ((Runnable) invocation.getArguments()[0]).run(); return null; } }).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); } }
HandlerUtils.runOnUiThread(any(Runnable.class));
@Test public void clickedFromBackgroundDisableWhilePostingToUI() { /* Mock activity to contain push */ PushListener pushListener = mock(PushListener.class); Push.setListener(pushListener); Push push = Push.getInstance(); Channel channel = mock(Channel.class); start(push, channel); Activity activity = mock(Activity.class); Intent intent = createPushIntent(null, null, null); when(PushIntentUtils.getMessageId(intent)).thenReturn("some id"); when(activity.getIntent()).thenReturn(intent); /* Disable while posting the command to the U.I. thread. */ activity = mock(Activity.class); when(activity.getIntent()).thenReturn(intent); final AtomicReference<Runnable> runnable = new AtomicReference<>(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) { runnable.set((Runnable) invocation.getArguments()[0]); return null; } }).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); push.onActivityResumed(activity); Push.setEnabled(false); runnable.get().run(); ArgumentCaptor<PushNotification> captor = ArgumentCaptor.forClass(PushNotification.class); verify(pushListener, never()).onPushNotificationReceived(eq(activity), captor.capture()); }
@Test public void resumeWhileStartingAndDisableWhileRunningBrowserCodeOnUI() throws InterruptedException { final AtomicReference<Runnable> runnable = new AtomicReference<>(); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) { runnable.set((Runnable) invocation.getArguments()[0]); return null; } }).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); Distribute.getInstance().onStarting(mAppCenterHandler); Distribute.getInstance().onActivityResumed(mActivity); Distribute.getInstance().onStarted(mContext, mock(Channel.class), "a", null, true); /* Disable and test async behavior of setEnabled. */ final CountDownLatch latch = new CountDownLatch(1); Distribute.setEnabled(false).thenAccept(new AppCenterConsumer<Void>() { @Override public void accept(Void aVoid) { latch.countDown(); } }); runnable.get().run(); assertTrue(latch.await(0, TimeUnit.MILLISECONDS)); verifyStatic(never()); BrowserUtils.openBrowser(anyString(), any(Activity.class)); }
HandlerUtils.runOnUiThread(any(Runnable.class));
@Test @PrepareForTest(HandlerUtils.class) public void multipleCallbacks() { mockStatic(HandlerUtils.class); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) { ((Runnable) invocation.getArguments()[0]).run(); return null; } }).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class)); DefaultAppCenterFuture<Integer> future = new DefaultAppCenterFuture<>(); @SuppressWarnings("unchecked") AppCenterConsumer<Integer> function = mock(AppCenterConsumer.class); future.thenAccept(function); future.thenAccept(function); future.complete(1); verify(function, times(2)).accept(1); /* Works also after completion. */ future.thenAccept(function); future.thenAccept(function); verify(function, times(4)).accept(1); } }
HandlerUtils.runOnUiThread(any(Runnable.class)); doAnswer(runNow).when(mAppCenterHandler).post(any(Runnable.class), any(Runnable.class));
mockStatic(HandlerUtils.class); doAnswer(runNow).when(HandlerUtils.class); HandlerUtils.runOnUiThread(any(Runnable.class));
@Override protected synchronized void applyEnabledState(boolean enabled) { if (enabled) { changeDistributionGroupIdAfterAppUpdateIfNeeded(); /* Enable the distribute info tracker. */ String distributionGroupId = SharedPreferencesManager.getString(PREFERENCE_KEY_DISTRIBUTION_GROUP_ID); mDistributeInfoTracker = new DistributeInfoTracker(distributionGroupId); mChannel.addListener(mDistributeInfoTracker); HandlerUtils.runOnUiThread(new Runnable() { @Override public void run() { resumeDistributeWorkflow(); } }); } else { /* Clean all state on disabling, cancel everything. Keep only redirection parameters. */ mTesterAppOpenedOrAborted = false; mBrowserOpenedOrAborted = false; mWorkflowCompleted = false; cancelPreviousTasks(); SharedPreferencesManager.remove(PREFERENCE_KEY_REQUEST_ID); SharedPreferencesManager.remove(PREFERENCE_KEY_POSTPONE_TIME); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_PACKAGE_HASH_KEY); SharedPreferencesManager.remove(PREFERENCE_KEY_UPDATE_SETUP_FAILED_MESSAGE_KEY); SharedPreferencesManager.remove(PREFERENCE_KEY_TESTER_APP_UPDATE_SETUP_FAILED_MESSAGE_KEY); /* Disable the distribute info tracker. */ mChannel.removeListener(mDistributeInfoTracker); mDistributeInfoTracker = null; } }
HandlerUtils.runOnUiThread(any(Runnable.class)); AppCenterHandler handler = mock(AppCenterHandler.class); Crashes.getInstance().onStarting(handler);