@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); } }); } }
/** * 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); } }
@Test public void init() { new HandlerUtils(); }
@Test public void getMainThreadHandler() { assertSame(HandlerUtils.sMainHandler, HandlerUtils.getMainHandler()); assertSame(HandlerUtils.getMainHandler(), HandlerUtils.getMainHandler()); }
/** * Update progress dialog for mandatory update. */ synchronized void updateProgressDialog(ReleaseDetails releaseDetails, DownloadProgress downloadProgress) { /* If not canceled and U.I. context did not change. */ if (releaseDetails == mReleaseDetails && mProgressDialog != null) { /* If file size is known update downloadProgress bar. */ if (downloadProgress.getTotalSize() >= 0) { /* When we switch from indeterminate to determinate */ if (mProgressDialog.isIndeterminate()) { /* Configure the progress dialog determinate style. */ mProgressDialog.setProgressPercentFormat(NumberFormat.getPercentInstance()); mProgressDialog.setProgressNumberFormat(mForegroundActivity.getString(R.string.appcenter_distribute_download_progress_number_format)); mProgressDialog.setIndeterminate(false); mProgressDialog.setMax((int) (downloadProgress.getTotalSize() / MEBIBYTE_IN_BYTES)); } mProgressDialog.setProgress((int) (downloadProgress.getCurrentSize() / MEBIBYTE_IN_BYTES)); } /* And schedule the next check. */ HandlerUtils.getMainHandler().postAtTime(new Runnable() { @Override public void run() { checkDownload(mContext, DistributeUtils.getStoredDownloadId(), true); } }, HANDLER_TOKEN_CHECK_PROGRESS, SystemClock.uptimeMillis() + CHECK_PROGRESS_TIME_INTERVAL_IN_MILLIS); } }
@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() {
when(HandlerUtils.getMainHandler()).thenReturn(mHandler);
@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); } }
/** * 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); } }; }
HandlerUtils.runOnUiThread(new Runnable() {
@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(new Runnable() {