• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 androidx.window.common;
18 
19 import android.annotation.NonNull;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.database.ContentObserver;
23 import android.net.Uri;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.provider.Settings;
27 import android.text.TextUtils;
28 
29 import androidx.window.util.BaseDataProducer;
30 
31 import com.android.internal.R;
32 
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.function.Consumer;
36 
37 /**
38  * Implementation of {@link androidx.window.util.DataProducer} that produces a
39  * {@link String} that can be parsed to a {@link CommonFoldingFeature}.
40  * {@link RawFoldingFeatureProducer} searches for the value in two places. The first check is in
41  * settings where the {@link String} property is saved with the key
42  * {@link RawFoldingFeatureProducer#DISPLAY_FEATURES}. If this value is null or empty then the
43  * value in {@link android.content.res.Resources} is used. If both are empty then
44  * {@link RawFoldingFeatureProducer#getData} returns an empty object.
45  * {@link RawFoldingFeatureProducer} listens to changes in the setting so that it can override
46  * the system {@link CommonFoldingFeature} data.
47  */
48 public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
49     private static final String DISPLAY_FEATURES = "display_features";
50 
51     private final Uri mDisplayFeaturesUri =
52             Settings.Global.getUriFor(DISPLAY_FEATURES);
53 
54     private final ContentResolver mResolver;
55     private final ContentObserver mObserver;
56     private final String mResourceFeature;
57     private boolean mRegisteredObservers;
58 
RawFoldingFeatureProducer(@onNull Context context)59     public RawFoldingFeatureProducer(@NonNull Context context) {
60         mResolver = context.getContentResolver();
61         mObserver = new SettingsObserver();
62         mResourceFeature = context.getResources().getString(R.string.config_display_features);
63     }
64 
65     @Override
66     @NonNull
getData(Consumer<String> dataConsumer)67     public void getData(Consumer<String> dataConsumer) {
68         String displayFeaturesString = getFeatureString();
69         if (displayFeaturesString == null) {
70             dataConsumer.accept("");
71         } else {
72             dataConsumer.accept(displayFeaturesString);
73         }
74     }
75 
76     /**
77      * Returns the {@link String} representation for a {@link CommonFoldingFeature} from settings if
78      * present and falls back to the resource value if empty or {@code null}.
79      */
getFeatureString()80     private String getFeatureString() {
81         String settingsFeature = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
82         if (TextUtils.isEmpty(settingsFeature)) {
83             return mResourceFeature;
84         }
85         return settingsFeature;
86     }
87 
88     @Override
onListenersChanged(Set<Consumer<String>> callbacks)89     protected void onListenersChanged(Set<Consumer<String>> callbacks) {
90         if (callbacks.isEmpty()) {
91             unregisterObserversIfNeeded();
92         } else {
93             registerObserversIfNeeded();
94         }
95     }
96 
97     @NonNull
98     @Override
getCurrentData()99     public Optional<String> getCurrentData() {
100         return Optional.of(getFeatureString());
101     }
102 
103     /**
104      * Registers settings observers, if needed. When settings observers are registered for this
105      * producer callbacks for changes in data will be triggered.
106      */
registerObserversIfNeeded()107     private void registerObserversIfNeeded() {
108         if (mRegisteredObservers) {
109             return;
110         }
111         mRegisteredObservers = true;
112         mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
113                 mObserver /* ContentObserver */);
114     }
115 
116     /**
117      * Unregisters settings observers, if needed. When settings observers are unregistered for this
118      * producer callbacks for changes in data will not be triggered.
119      */
unregisterObserversIfNeeded()120     private void unregisterObserversIfNeeded() {
121         if (!mRegisteredObservers) {
122             return;
123         }
124         mRegisteredObservers = false;
125         mResolver.unregisterContentObserver(mObserver);
126     }
127 
128     private final class SettingsObserver extends ContentObserver {
SettingsObserver()129         SettingsObserver() {
130             super(new Handler(Looper.getMainLooper()));
131         }
132 
133         @Override
onChange(boolean selfChange, Uri uri)134         public void onChange(boolean selfChange, Uri uri) {
135             if (mDisplayFeaturesUri.equals(uri)) {
136                 notifyDataChanged(getFeatureString());
137             }
138         }
139     }
140 }