Commit d93de0f5 authored by Wenyu Fu's avatar Wenyu Fu Committed by Commit Bot

Add observer for LoadingView

Add observer that will be notified when hideLoadingUI is finished.
Also add junit tests for the widget.

Bug: 1108558
Change-Id: I78e62d10309fd4819bdf5a04cf9fda4de610d7d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2345091
Commit-Queue: Wenyu Fu <wenyufu@chromium.org>
Reviewed-by: default avatarSky Malice <skym@chromium.org>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796621}
parent d76a653a
......@@ -290,6 +290,7 @@ java_library("junit") {
sources = [
"java/src/org/chromium/components/browser_ui/widget/CompositeTouchDelegateUnitTest.java",
"java/src/org/chromium/components/browser_ui/widget/InsetObserverViewTest.java",
"java/src/org/chromium/components/browser_ui/widget/LoadingViewTest.java",
"java/src/org/chromium/components/browser_ui/widget/selectable_list/SelectionDelegateTest.java",
]
deps = [
......
......@@ -12,8 +12,13 @@ import android.util.AttributeSet;
import android.view.View;
import android.widget.ProgressBar;
import androidx.annotation.VisibleForTesting;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
import java.util.ArrayList;
import java.util.List;
/**
* A {@link ProgressBar} that understands the hiding/showing policy defined in Material Design.
*/
......@@ -21,7 +26,21 @@ public class LoadingView extends ProgressBar {
private static final int LOADING_ANIMATION_DELAY_MS = 500;
private static final int MINIMUM_ANIMATION_SHOW_TIME_MS = 500;
/**
* A observer interface that will be notified when the progress bar is hidden.
*/
public interface Observer {
/**
* Notify the listener a call to {@link #hideLoadingUI()} is complete and loading view is
* GONE.
*/
void onHideLoadingUIComplete();
}
private long mStartTime = -1;
private boolean mDisableAnimationForTest;
private final List<Observer> mListeners = new ArrayList<>();
private final Runnable mDelayedShow = new Runnable() {
@Override
......@@ -45,13 +64,18 @@ public class LoadingView extends ProgressBar {
private final Runnable mDelayedHide = new Runnable() {
@Override
public void run() {
if (mDisableAnimationForTest) {
onHideLoadingFinished();
return;
}
animate()
.alpha(0.0f)
.setInterpolator(BakedBezierInterpolator.TRANSFORM_CURVE)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
setVisibility(GONE);
onHideLoadingFinished();
}
});
}
......@@ -97,6 +121,8 @@ public class LoadingView extends ProgressBar {
Math.max(0,
mStartTime + MINIMUM_ANIMATION_SHOW_TIME_MS
- SystemClock.elapsedRealtime()));
} else {
onHideLoadingFinished();
}
}
......@@ -106,5 +132,34 @@ public class LoadingView extends ProgressBar {
public void destroy() {
removeCallbacks(mDelayedShow);
removeCallbacks(mDelayedHide);
mListeners.clear();
}
/**
* Add the listener that will be notified when the spinner is completely hidden with {@link
* #hideLoadingUI()}.
* @param listener {@link Observer} that will be notified when the spinner is
* completely hidden with {@link #hideLoadingUI()}.
*/
public void addObserver(Observer listener) {
mListeners.add(listener);
}
private void onHideLoadingFinished() {
setVisibility(GONE);
for (Observer observer : mListeners) {
observer.onHideLoadingUIComplete();
}
}
/**
* Set disable the fading animation during {@link #hideLoadingUI()}.
* This function is added as a work around for disable animation during unit tests.
* @param disableAnimation Whether the fading animation should be disabled during {@link
* #hideLoadingUI()}.
*/
@VisibleForTesting
public void setDisableAnimationForTest(boolean disableAnimation) {
mDisableAnimationForTest = disableAnimation;
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.components.browser_ui.widget;
import android.app.Activity;
import android.view.View;
import android.widget.FrameLayout;
import androidx.test.filters.SmallTest;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowView;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.CallbackHelper;
import java.util.concurrent.TimeUnit;
/**
* Tests for {@link LoadingView}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE, shadows = {ShadowView.class})
public class LoadingViewTest {
private LoadingView mLoadingView;
private Activity mActivity;
private final CallbackHelper mCallback1 = new CallbackHelper();
private final CallbackHelper mCallback2 = new CallbackHelper();
@Before
public void setUpTest() throws Exception {
mActivity = Robolectric.buildActivity(Activity.class).create().get();
FrameLayout content = new FrameLayout(mActivity);
mActivity.setContentView(content);
mLoadingView = new LoadingView(mActivity);
mLoadingView.setDisableAnimationForTest(true);
content.addView(mLoadingView);
mLoadingView.addObserver(mCallback1::notifyCalled);
mLoadingView.addObserver(mCallback2::notifyCalled);
}
@Test
@SmallTest
public void testLoadingFast() {
mLoadingView.showLoadingUI();
ShadowLooper.idleMainLooper(100, TimeUnit.MILLISECONDS);
Assert.assertEquals("Progress bar should be hidden before 500ms.", View.GONE,
mLoadingView.getVisibility());
mLoadingView.hideLoadingUI();
Assert.assertEquals(
"Progress bar should never be visible.", View.GONE, mLoadingView.getVisibility());
Assert.assertEquals("Callback1 should be executed after loading finishes.", 1,
mCallback1.getCallCount());
Assert.assertEquals("Callback2 should be executed after loading finishes.", 1,
mCallback2.getCallCount());
}
@Test
@SmallTest
public void testLoadingSlow() {
long sleepTime = 500;
mLoadingView.showLoadingUI();
ShadowLooper.idleMainLooper(sleepTime, TimeUnit.MILLISECONDS);
Assert.assertEquals("Progress bar should be visible after 500ms.", View.VISIBLE,
mLoadingView.getVisibility());
mLoadingView.hideLoadingUI();
Assert.assertEquals("Progress bar should still be visible until showing for 500ms.",
View.VISIBLE, mLoadingView.getVisibility());
Assert.assertEquals("Callback1 should not be executed before loading finishes.", 0,
mCallback1.getCallCount());
Assert.assertEquals("Callback2 should not be executed before loading finishes.", 0,
mCallback2.getCallCount());
// The spinner should be displayed for at least 500ms.
ShadowLooper.idleMainLooper(sleepTime, TimeUnit.MILLISECONDS);
Assert.assertEquals("Progress bar should be hidden after 500ms.", View.GONE,
mLoadingView.getVisibility());
Assert.assertEquals("Callback1 should be executed after loading finishes.", 1,
mCallback1.getCallCount());
Assert.assertEquals("Callback2 should be executed after loading finishes.", 1,
mCallback2.getCallCount());
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment