1befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini/*
2befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * Copyright (C) 2013 The Android Open Source Project
3befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini *
4befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * Licensed under the Apache License, Version 2.0 (the "License");
5befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * you may not use this file except in compliance with the License.
6befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * You may obtain a copy of the License at
7befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini *
8befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini *      http://www.apache.org/licenses/LICENSE-2.0
9befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini *
10befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * Unless required by applicable law or agreed to in writing, software
11befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * distributed under the License is distributed on an "AS IS" BASIS,
12befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * See the License for the specific language governing permissions and
14befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * limitations under the License.
15befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini */
16befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.widget;
18befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
19befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpiniimport android.content.Context;
20befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpiniimport android.util.AttributeSet;
21befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpiniimport android.view.View;
22befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpiniimport android.widget.ProgressBar;
23befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
249dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.NonNull;
259dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.Nullable;
269dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas
27befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini/**
28befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * ContentLoadingProgressBar implements a ProgressBar that waits a minimum time to be
29befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * dismissed before showing. Once visible, the progress bar will be visible for
30befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * a minimum amount of time to avoid "flashes" in the UI when an event could take
31befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini * a largely variable time to complete (from none, to a user perceivable amount)
32befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini */
33befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpinipublic class ContentLoadingProgressBar extends ProgressBar {
34befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    private static final int MIN_SHOW_TIME = 500; // ms
35befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    private static final int MIN_DELAY = 500; // ms
36befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
37540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    long mStartTime = -1;
38befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
39540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    boolean mPostedHide = false;
40befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
41540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    boolean mPostedShow = false;
42befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
43540222c3675801eaa141ace1c164c4d3499b4721Aurimas Liutikas    boolean mDismissed = false;
44befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
45befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    private final Runnable mDelayedHide = new Runnable() {
46befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
47befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        @Override
48befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        public void run() {
49befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            mPostedHide = false;
50befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            mStartTime = -1;
51befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            setVisibility(View.GONE);
52befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        }
53befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    };
54befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
55befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    private final Runnable mDelayedShow = new Runnable() {
56befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
57befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        @Override
58befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        public void run() {
59befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            mPostedShow = false;
60befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            if (!mDismissed) {
61befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini                mStartTime = System.currentTimeMillis();
62befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini                setVisibility(View.VISIBLE);
63befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            }
64befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        }
65befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    };
66befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
67852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public ContentLoadingProgressBar(@NonNull Context context) {
68befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        this(context, null);
69befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
70befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
71852749684a10e7d104eed8fb2dcc68b64d079340Jake Wharton    public ContentLoadingProgressBar(@NonNull Context context, @Nullable AttributeSet attrs) {
72befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        super(context, attrs, 0);
73befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
74befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
75befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    @Override
76befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    public void onAttachedToWindow() {
77befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        super.onAttachedToWindow();
78befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        removeCallbacks();
79befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
80befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
81befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    @Override
82befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    public void onDetachedFromWindow() {
83befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        super.onDetachedFromWindow();
84befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        removeCallbacks();
85befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
86befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
87befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    private void removeCallbacks() {
88befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        removeCallbacks(mDelayedHide);
89befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        removeCallbacks(mDelayedShow);
90befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
91befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
92befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    /**
93befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     * Hide the progress view if it is visible. The progress view will not be
94befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     * hidden until it has been shown for at least a minimum show time. If the
95befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     * progress view was not yet visible, cancels showing the progress view.
96befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     */
97aae8f264af78e59a0e28045b5e7a4e95060aad65Kirill Grouchnikov    public synchronized void hide() {
98befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        mDismissed = true;
99befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        removeCallbacks(mDelayedShow);
100aae8f264af78e59a0e28045b5e7a4e95060aad65Kirill Grouchnikov        mPostedShow = false;
101befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        long diff = System.currentTimeMillis() - mStartTime;
102befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
103befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            // The progress spinner has been shown long enough
104befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            // OR was not shown yet. If it wasn't shown yet,
105befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            // it will just never be shown.
106befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            setVisibility(View.GONE);
107befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        } else {
108befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            // The progress spinner is shown, but not long enough,
109befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            // so put a delayed message in to hide it when its been
110befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            // shown long enough.
111befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            if (!mPostedHide) {
112befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini                postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
113befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini                mPostedHide = true;
114befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            }
115befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        }
116befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
117befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini
118befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    /**
119befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     * Show the progress view after waiting for a minimum delay. If
120befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     * during that time, hide() is called, the view is never made visible.
121befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini     */
122aae8f264af78e59a0e28045b5e7a4e95060aad65Kirill Grouchnikov    public synchronized void show() {
123befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        // Reset the start time.
124befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        mStartTime = -1;
125befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        mDismissed = false;
126befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        removeCallbacks(mDelayedHide);
127aae8f264af78e59a0e28045b5e7a4e95060aad65Kirill Grouchnikov        mPostedHide = false;
128befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        if (!mPostedShow) {
129befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            postDelayed(mDelayedShow, MIN_DELAY);
130befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini            mPostedShow = true;
131befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini        }
132befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini    }
133befff92741b1f0629d9a9389a3889cca56d914a2Mindy DelliCarpini}
134