1/*
2 * Copyright (C) 2017 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 */
16package androidx.work.impl.constraints.trackers;
17
18import android.content.Context;
19import android.support.annotation.RestrictTo;
20import android.util.Log;
21
22import androidx.work.impl.constraints.ConstraintListener;
23
24import java.util.LinkedHashSet;
25import java.util.Set;
26
27/**
28 * A base for tracking constraints and notifying listeners of changes.
29 *
30 * @param <T> the constraint data type observed by this tracker
31 * @hide
32 */
33@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
34public abstract class ConstraintTracker<T> {
35
36    private static final String TAG = "ConstraintTracker";
37
38    protected final Context mAppContext;
39    private final Set<ConstraintListener<T>> mListeners = new LinkedHashSet<>();
40    private T mCurrentState;
41
42    ConstraintTracker(Context context) {
43        mAppContext = context.getApplicationContext();
44    }
45
46    /**
47     * Add the given listener for tracking.
48     * This may cause {@link #getInitialState()} and {@link #startTracking()} to be invoked.
49     * If a state is set, this will immediately notify the given listener.
50     *
51     * @param listener The target listener to start notifying
52     */
53    public void addListener(ConstraintListener<T> listener) {
54        if (mListeners.add(listener)) {
55            if (mListeners.size() == 1) {
56                mCurrentState = getInitialState();
57                Log.d(TAG, String.format("%s: initial state = %s", getClass().getSimpleName(),
58                        mCurrentState));
59                startTracking();
60            }
61            listener.onConstraintChanged(mCurrentState);
62        }
63    }
64
65    /**
66     * Remove the given listener from tracking.
67     *
68     * @param listener The listener to stop notifying.
69     */
70    public void removeListener(ConstraintListener<T> listener) {
71        if (mListeners.remove(listener) && mListeners.isEmpty()) {
72            stopTracking();
73        }
74    }
75
76    /**
77     * Sets the state of the constraint.
78     * If state is has not changed, nothing happens.
79     *
80     * @param newState new state of constraint
81     */
82    public void setState(T newState) {
83        if (mCurrentState == newState
84                || (mCurrentState != null && mCurrentState.equals(newState))) {
85            return;
86        }
87        mCurrentState = newState;
88        for (ConstraintListener<T> listener : mListeners) {
89            listener.onConstraintChanged(mCurrentState);
90        }
91    }
92
93    /**
94     * Determines the initial state of the constraint being tracked.
95     */
96    public abstract T getInitialState();
97
98    /**
99     * Start tracking for constraint state changes.
100     */
101    public abstract void startTracking();
102
103    /**
104     * Stop tracking for constraint state changes.
105     */
106    public abstract void stopTracking();
107}
108