1 /*
2  * Copyright (C) 2020 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 android.app.time;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.StringDef;
22 import android.annotation.SystemApi;
23 import android.os.Bundle;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.Objects;
30 
31 /**
32  * User visible settings that control the behavior of the time zone detector / manual time zone
33  * entry.
34  *
35  * <p>When reading the configuration, values for all settings will be provided. In some cases, such
36  * as when the device behavior relies on optional hardware / OEM configuration, or the value of
37  * several settings, the device behavior may not be directly affected by the setting value.
38  *
39  * <p>Settings can be left absent when updating configuration via {@link
40  * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)} and those settings will not be
41  * changed. Not all configuration settings can be modified by all users: see {@link
42  * TimeManager#getTimeZoneCapabilitiesAndConfig()} and {@link TimeZoneCapabilities} for details.
43  *
44  * @hide
45  */
46 @SystemApi
47 public final class TimeZoneConfiguration implements Parcelable {
48 
49     public static final @NonNull Creator<TimeZoneConfiguration> CREATOR =
50             new Creator<TimeZoneConfiguration>() {
51                 public TimeZoneConfiguration createFromParcel(Parcel in) {
52                     return TimeZoneConfiguration.createFromParcel(in);
53                 }
54 
55                 public TimeZoneConfiguration[] newArray(int size) {
56                     return new TimeZoneConfiguration[size];
57                 }
58             };
59 
60     /**
61      * All configuration properties
62      *
63      * @hide
64      */
65     @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED })
66     @Retention(RetentionPolicy.SOURCE)
67     @interface Setting {}
68 
69     /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */
70     @Setting
71     private static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
72 
73     /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */
74     @Setting
75     private static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
76 
77     @NonNull private final Bundle mBundle;
78 
TimeZoneConfiguration(Builder builder)79     private TimeZoneConfiguration(Builder builder) {
80         this.mBundle = Objects.requireNonNull(builder.mBundle);
81     }
82 
createFromParcel(Parcel in)83     private static TimeZoneConfiguration createFromParcel(Parcel in) {
84         return new TimeZoneConfiguration.Builder()
85                 .setPropertyBundleInternal(in.readBundle())
86                 .build();
87     }
88 
89     @Override
writeToParcel(@onNull Parcel dest, int flags)90     public void writeToParcel(@NonNull Parcel dest, int flags) {
91         dest.writeBundle(mBundle);
92     }
93 
94     /**
95      * Returns {@code true} if all known settings are present.
96      *
97      * @hide
98      */
isComplete()99     public boolean isComplete() {
100         return hasIsAutoDetectionEnabled()
101                 && hasIsGeoDetectionEnabled();
102     }
103 
104     /**
105      * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This
106      * controls whether a device will attempt to determine the time zone automatically using
107      * contextual information if the device supports auto detection.
108      *
109      * <p>See {@link TimeZoneCapabilities#getConfigureAutoDetectionEnabledCapability()} for how to
110      * tell if the setting is meaningful for the current user at this time.
111      *
112      * @throws IllegalStateException if the setting is not present
113      */
isAutoDetectionEnabled()114     public boolean isAutoDetectionEnabled() {
115         enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED);
116         return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED);
117     }
118 
119     /**
120      * Returns {@code true} if the {@link #isAutoDetectionEnabled()} setting is present.
121      *
122      * @hide
123      */
hasIsAutoDetectionEnabled()124     public boolean hasIsAutoDetectionEnabled() {
125         return mBundle.containsKey(SETTING_AUTO_DETECTION_ENABLED);
126     }
127 
128     /**
129      * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This
130      * controls whether the device can use geolocation to determine time zone. This value may only
131      * be used by Android under some circumstances. For example, it is not used when
132      * {@link #isGeoDetectionEnabled()} is {@code false}.
133      *
134      * <p>See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabledCapability()} for how to
135      * tell if the setting is meaningful for the current user at this time.
136      *
137      * @throws IllegalStateException if the setting is not present
138      */
isGeoDetectionEnabled()139     public boolean isGeoDetectionEnabled() {
140         enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED);
141         return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED);
142     }
143 
144     /**
145      * Returns {@code true} if the {@link #isGeoDetectionEnabled()} setting is present.
146      *
147      * @hide
148      */
hasIsGeoDetectionEnabled()149     public boolean hasIsGeoDetectionEnabled() {
150         return mBundle.containsKey(SETTING_GEO_DETECTION_ENABLED);
151     }
152 
153     @Override
describeContents()154     public int describeContents() {
155         return 0;
156     }
157 
158     @Override
equals(@ullable Object o)159     public boolean equals(@Nullable Object o) {
160         if (this == o) {
161             return true;
162         }
163         if (o == null || getClass() != o.getClass()) {
164             return false;
165         }
166         TimeZoneConfiguration that = (TimeZoneConfiguration) o;
167         return mBundle.kindofEquals(that.mBundle);
168     }
169 
170     @Override
hashCode()171     public int hashCode() {
172         return Objects.hash(mBundle);
173     }
174 
175     @Override
toString()176     public String toString() {
177         return "TimeZoneConfiguration{"
178                 + "mBundle=" + mBundle
179                 + '}';
180     }
181 
enforceSettingPresent(@etting String setting)182     private void enforceSettingPresent(@Setting String setting) {
183         if (!mBundle.containsKey(setting)) {
184             throw new IllegalStateException(setting + " is not set");
185         }
186     }
187 
188     /**
189      * A builder for {@link TimeZoneConfiguration} objects.
190      *
191      * @hide
192      */
193     @SystemApi
194     public static final class Builder {
195 
196         private final Bundle mBundle = new Bundle();
197 
198         /**
199          * Creates a new Builder with no settings held.
200          */
Builder()201         public Builder() {
202         }
203 
204         /**
205          * Creates a new Builder by copying the settings from an existing instance.
206          */
Builder(@onNull TimeZoneConfiguration toCopy)207         public Builder(@NonNull TimeZoneConfiguration toCopy) {
208             mergeProperties(toCopy);
209         }
210 
211         /**
212          * Merges {@code other} settings into this instances, replacing existing values in this
213          * where the settings appear in both.
214          *
215          * @hide
216          */
217         @NonNull
mergeProperties(@onNull TimeZoneConfiguration other)218         public Builder mergeProperties(@NonNull TimeZoneConfiguration other) {
219             this.mBundle.putAll(other.mBundle);
220             return this;
221         }
222 
223         @NonNull
setPropertyBundleInternal(@onNull Bundle bundle)224         Builder setPropertyBundleInternal(@NonNull Bundle bundle) {
225             this.mBundle.putAll(bundle);
226             return this;
227         }
228 
229         /**
230          * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting.
231          */
232         @NonNull
setAutoDetectionEnabled(boolean enabled)233         public Builder setAutoDetectionEnabled(boolean enabled) {
234             this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled);
235             return this;
236         }
237 
238         /**
239          * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting.
240          */
241         @NonNull
setGeoDetectionEnabled(boolean enabled)242         public Builder setGeoDetectionEnabled(boolean enabled) {
243             this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled);
244             return this;
245         }
246 
247         /** Returns the {@link TimeZoneConfiguration}. */
248         @NonNull
build()249         public TimeZoneConfiguration build() {
250             return new TimeZoneConfiguration(this);
251         }
252     }
253 }
254 
255