1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.dashboard;
18 
19 import android.util.Log;
20 
21 import androidx.annotation.NonNull;
22 
23 import com.android.settings.core.BasePreferenceController;
24 import com.android.settingslib.utils.ThreadUtils;
25 
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Set;
29 import java.util.concurrent.CountDownLatch;
30 import java.util.concurrent.TimeUnit;
31 
32 /**
33  * Control ui blocker data and check whether it is finished
34  *
35  * @see BasePreferenceController.UiBlocker
36  * @see BasePreferenceController.UiBlockListener
37  */
38 public class UiBlockerController {
39     private static final String TAG = "UiBlockerController";
40     private static final int TIMEOUT_MILLIS = 500;
41 
42     private CountDownLatch mCountDownLatch;
43     private boolean mBlockerFinished;
44     private Set<String> mKeys;
45     private long mTimeoutMillis;
46 
UiBlockerController(@onNull List<String> keys)47     public UiBlockerController(@NonNull List<String> keys) {
48         this(keys, TIMEOUT_MILLIS);
49     }
50 
UiBlockerController(@onNull List<String> keys, long timeout)51     public UiBlockerController(@NonNull List<String> keys, long timeout) {
52         mCountDownLatch = new CountDownLatch(keys.size());
53         mBlockerFinished = keys.isEmpty();
54         mKeys = new HashSet<>(keys);
55         mTimeoutMillis = timeout;
56     }
57 
58     /**
59      * Start background thread, it will invoke {@code finishRunnable} if any condition is met
60      *
61      * 1. Waiting time exceeds {@link #mTimeoutMillis}
62      * 2. All background work that associated with {@link #mCountDownLatch} is finished
63      */
start(Runnable finishRunnable)64     public boolean start(Runnable finishRunnable) {
65         if (mKeys.isEmpty()) {
66             // Don't need to run finishRunnable because it doesn't start
67             return false;
68         }
69         ThreadUtils.postOnBackgroundThread(() -> {
70             try {
71                 mCountDownLatch.await(mTimeoutMillis, TimeUnit.MILLISECONDS);
72             } catch (InterruptedException e) {
73                 Log.w(TAG, "interrupted");
74             }
75             mBlockerFinished = true;
76             ThreadUtils.postOnMainThread(finishRunnable);
77         });
78 
79         return true;
80     }
81 
82     /**
83      * Return {@code true} if all work finished
84      */
isBlockerFinished()85     public boolean isBlockerFinished() {
86         return mBlockerFinished;
87     }
88 
89     /**
90      * Count down latch by {@code key}. It only count down 1 time if same key count down multiple
91      * times.
92      */
countDown(String key)93     public boolean countDown(String key) {
94         if (mKeys.remove(key)) {
95             mCountDownLatch.countDown();
96             return true;
97         }
98 
99         return false;
100     }
101 }
102