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;
17
18import android.content.Context;
19import android.support.annotation.NonNull;
20import android.support.annotation.Nullable;
21import android.support.annotation.VisibleForTesting;
22import android.util.Log;
23
24import androidx.work.Constraints;
25import androidx.work.impl.constraints.controllers.BatteryChargingController;
26import androidx.work.impl.constraints.controllers.BatteryNotLowController;
27import androidx.work.impl.constraints.controllers.ConstraintController;
28import androidx.work.impl.constraints.controllers.NetworkConnectedController;
29import androidx.work.impl.constraints.controllers.NetworkMeteredController;
30import androidx.work.impl.constraints.controllers.NetworkNotRoamingController;
31import androidx.work.impl.constraints.controllers.NetworkUnmeteredController;
32import androidx.work.impl.constraints.controllers.StorageNotLowController;
33import androidx.work.impl.model.WorkSpec;
34
35import java.util.ArrayList;
36import java.util.List;
37
38/**
39 * Tracks {@link WorkSpec}s and their {@link Constraints}, and notifies an optional
40 * {@link WorkConstraintsCallback} when all of their constraints are met or not met.
41 */
42
43public class WorkConstraintsTracker implements ConstraintController.OnConstraintUpdatedCallback {
44
45    private static final String TAG = "WorkConstraintsTracker";
46
47    @Nullable private final WorkConstraintsCallback mCallback;
48    private final ConstraintController[] mConstraintControllers;
49
50    /**
51     * @param context  The application {@link Context}
52     * @param callback The callback is only necessary when you need {@link WorkConstraintsTracker}
53     *                 to notify you about changes in constraints for the list of  {@link
54     *                 WorkSpec}'s that it is tracking.
55     */
56    public WorkConstraintsTracker(Context context, @Nullable WorkConstraintsCallback callback) {
57        Context appContext = context.getApplicationContext();
58        mCallback = callback;
59        mConstraintControllers = new ConstraintController[] {
60                new BatteryChargingController(appContext, this),
61                new BatteryNotLowController(appContext, this),
62                new StorageNotLowController(appContext, this),
63                new NetworkConnectedController(appContext, this),
64                new NetworkUnmeteredController(appContext, this),
65                new NetworkNotRoamingController(appContext, this),
66                new NetworkMeteredController(appContext, this)
67        };
68    }
69
70    @VisibleForTesting
71    WorkConstraintsTracker(
72            @Nullable WorkConstraintsCallback callback,
73            ConstraintController[] controllers) {
74
75        mCallback = callback;
76        mConstraintControllers = controllers;
77    }
78
79    /**
80     * Replaces the list of tracked {@link WorkSpec}s to monitor if their constraints are met.
81     *
82     * @param workSpecs A list of {@link WorkSpec}s to monitor constraints for
83     */
84    public void replace(@NonNull List<WorkSpec> workSpecs) {
85        for (ConstraintController controller : mConstraintControllers) {
86            controller.replace(workSpecs);
87        }
88    }
89
90    /**
91     * Resets and clears all tracked {@link WorkSpec}s.
92     */
93    public void reset() {
94        for (ConstraintController controller : mConstraintControllers) {
95            controller.reset();
96        }
97    }
98
99    /**
100     * Returns <code>true</code> if all the underlying constraints for a given WorkSpec are met.
101     *
102     * @param workSpecId The {@link WorkSpec} id
103     * @return <code>true</code> if all the underlying constraints for a given {@link WorkSpec} are
104     * met.
105     */
106    public boolean areAllConstraintsMet(@NonNull String workSpecId) {
107        for (ConstraintController constraintController : mConstraintControllers) {
108            if (constraintController.isWorkSpecConstrained(workSpecId)) {
109                Log.d(TAG, String.format("Work %s constrained by %s", workSpecId,
110                        constraintController.getClass().getSimpleName()));
111                return false;
112            }
113        }
114        return true;
115    }
116
117    @Override
118    public void onConstraintMet(@NonNull List<String> workSpecIds) {
119        List<String> unconstrainedWorkSpecIds = new ArrayList<>();
120        for (String workSpecId : workSpecIds) {
121            if (areAllConstraintsMet(workSpecId)) {
122                Log.d(TAG, String.format("Constraints met for %s", workSpecId));
123                unconstrainedWorkSpecIds.add(workSpecId);
124            }
125        }
126        if (mCallback != null) {
127            mCallback.onAllConstraintsMet(unconstrainedWorkSpecIds);
128        }
129    }
130
131    @Override
132    public void onConstraintNotMet(@NonNull List<String> workSpecIds) {
133        if (mCallback != null) {
134            mCallback.onAllConstraintsNotMet(workSpecIds);
135        }
136    }
137}
138