Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1bb318e0 authored by Neil Fuller's avatar Neil Fuller Committed by Android (Google) Code Review
Browse files

Merge "Implement user-scoped geolocation configuration"

parents ee43e2f0 a223581e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -20,5 +20,5 @@ import android.app.timezonedetector.TimeZoneConfiguration;

/** {@hide} */
oneway interface ITimeZoneConfigurationListener {
    void onChange(in TimeZoneConfiguration configuration);
    void onChange();
}
 No newline at end of file
+2 −4
Original line number Diff line number Diff line
@@ -32,17 +32,15 @@ import android.app.timezonedetector.TimeZoneConfiguration;
 * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
 * for more complete documentation.
 *
 *
 * {@hide}
 */
interface ITimeZoneDetectorService {
  TimeZoneCapabilities getCapabilities();

  TimeZoneConfiguration getConfiguration();
  boolean updateConfiguration(in TimeZoneConfiguration configuration);
  void addConfigurationListener(ITimeZoneConfigurationListener listener);
  void removeConfigurationListener(ITimeZoneConfigurationListener listener);

  boolean updateConfiguration(in TimeZoneConfiguration configuration);

  boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
  void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
}
+77 −32
Original line number Diff line number Diff line
@@ -16,9 +16,12 @@

package android.app.timezonedetector;

import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED;
import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;

@@ -38,9 +41,9 @@ import java.util.Objects;
 *
 * <p>Actions have associated methods, see the documentation for each action for details.
 *
 * <p>For configuration capabilities, the associated current configuration value can be retrieved
 * using {@link TimeZoneDetector#getConfiguration()} and may be changed using
 * {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)}.
 * <p>For configuration settings capabilities, the associated settings value can be found via
 * {@link #getConfiguration()} and may be changed using {@link
 * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} (if the user's capabilities allow).
 *
 * <p>Note: Capabilities are independent of app permissions required to call the associated APIs.
 *
@@ -60,7 +63,8 @@ public final class TimeZoneCapabilities implements Parcelable {
    public static final int CAPABILITY_NOT_SUPPORTED = 10;

    /**
     * Indicates that a capability is supported on this device, but not allowed for the user.
     * Indicates that a capability is supported on this device, but not allowed for the user, e.g.
     * if the capability relates to the ability to modify settings the user is not able to.
     * This could be because of the user's type (e.g. maybe it applies to the primary user only) or
     * device policy. Depending on the capability, this could mean the associated UI
     * should be hidden, or displayed but disabled.
@@ -68,9 +72,11 @@ public final class TimeZoneCapabilities implements Parcelable {
    public static final int CAPABILITY_NOT_ALLOWED = 20;

    /**
     * Indicates that a capability is possessed but not applicable, e.g. if it is configuration,
     * the current configuration or device state renders it irrelevant. The associated UI may be
     * hidden, disabled, or left visible (but ineffective) depending on requirements.
     * Indicates that a capability is possessed but not currently applicable, e.g. if the
     * capability relates to the ability to modify settings, the user has the ability to modify
     * it, but it is currently rendered irrelevant by other settings or other device state (flags,
     * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but
     * ineffective) depending on requirements.
     */
    public static final int CAPABILITY_NOT_APPLICABLE = 30;

@@ -89,13 +95,13 @@ public final class TimeZoneCapabilities implements Parcelable {
            };


    private final @UserIdInt int mUserId;
    @NonNull private final TimeZoneConfiguration mConfiguration;
    private final @CapabilityState int mConfigureAutoDetectionEnabled;
    private final @CapabilityState int mConfigureGeoDetectionEnabled;
    private final @CapabilityState int mSuggestManualTimeZone;

    private TimeZoneCapabilities(@NonNull Builder builder) {
        this.mUserId = builder.mUserId;
        this.mConfiguration = Objects.requireNonNull(builder.mConfiguration);
        this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled;
        this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled;
        this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone;
@@ -103,7 +109,8 @@ public final class TimeZoneCapabilities implements Parcelable {

    @NonNull
    private static TimeZoneCapabilities createFromParcel(Parcel in) {
        return new TimeZoneCapabilities.Builder(in.readInt())
        return new TimeZoneCapabilities.Builder()
                .setConfiguration(in.readParcelable(null))
                .setConfigureAutoDetectionEnabled(in.readInt())
                .setConfigureGeoDetectionEnabled(in.readInt())
                .setSuggestManualTimeZone(in.readInt())
@@ -112,21 +119,24 @@ public final class TimeZoneCapabilities implements Parcelable {

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mUserId);
        dest.writeParcelable(mConfiguration, flags);
        dest.writeInt(mConfigureAutoDetectionEnabled);
        dest.writeInt(mConfigureGeoDetectionEnabled);
        dest.writeInt(mSuggestManualTimeZone);
    }

    /** Returns the user ID the capabilities are for. */
    public @UserIdInt int getUserId() {
        return mUserId;
    /**
     * Returns the user's time zone behavior configuration.
     */
    public @NonNull TimeZoneConfiguration getConfiguration() {
        return mConfiguration;
    }

    /**
     * Returns the user's capability state for controlling whether automatic time zone detection is
     * enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
     * TimeZoneConfiguration#isAutoDetectionEnabled()}.
     * Returns the capability state associated with the user's ability to modify the automatic time
     * zone detection setting. The setting can be updated via {@link
     * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link
     * #getConfiguration()}.
     */
    @CapabilityState
    public int getConfigureAutoDetectionEnabled() {
@@ -134,9 +144,10 @@ public final class TimeZoneCapabilities implements Parcelable {
    }

    /**
     * Returns the user's capability state for controlling whether geolocation can be used to detect
     * time zone via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
     * TimeZoneConfiguration#isGeoDetectionEnabled()}.
     * Returns the capability state associated with the user's ability to modify the geolocation
     * detection setting. The setting can be updated via {@link
     * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link
     * #getConfiguration()}.
     */
    @CapabilityState
    public int getConfigureGeoDetectionEnabled() {
@@ -144,8 +155,8 @@ public final class TimeZoneCapabilities implements Parcelable {
    }

    /**
     * Returns the user's capability state for manually setting the time zone on a device via
     * {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
     * Returns the capability state associated with the user's ability to manually set the time zone
     * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
     *
     * <p>The suggestion will be ignored in all cases unless the value is {@link
     * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
@@ -155,6 +166,38 @@ public final class TimeZoneCapabilities implements Parcelable {
        return mSuggestManualTimeZone;
    }

    /**
     * Constructs a new {@link TimeZoneConfiguration} from an {@code oldConfiguration} and a set of
     * {@code requestedChanges}, if the current capabilities allow. The new configuration is
     * returned and the capabilities are left unchanged. If the capabilities do not permit one or
     * more of the changes then {@code null} is returned.
     */
    @Nullable
    public TimeZoneConfiguration applyUpdate(TimeZoneConfiguration requestedChanges) {
        if (requestedChanges.getUserId() != mConfiguration.getUserId()) {
            throw new IllegalArgumentException("User does not match:"
                    + " this=" + mConfiguration + ", other=" + requestedChanges);
        }

        TimeZoneConfiguration.Builder newConfigBuilder =
                new TimeZoneConfiguration.Builder(mConfiguration);
        if (requestedChanges.hasSetting(SETTING_AUTO_DETECTION_ENABLED)) {
            if (getConfigureAutoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) {
                return null;
            }
            newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled());
        }

        if (requestedChanges.hasSetting(SETTING_GEO_DETECTION_ENABLED)) {
            if (getConfigureGeoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) {
                return null;
            }
            newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled());
        }

        return newConfigBuilder.build();
    }

    @Override
    public int describeContents() {
        return 0;
@@ -169,7 +212,7 @@ public final class TimeZoneCapabilities implements Parcelable {
            return false;
        }
        TimeZoneCapabilities that = (TimeZoneCapabilities) o;
        return mUserId == that.mUserId
        return Objects.equals(mConfiguration, that.mConfiguration)
                && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled
                && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled
                && mSuggestManualTimeZone == that.mSuggestManualTimeZone;
@@ -177,7 +220,7 @@ public final class TimeZoneCapabilities implements Parcelable {

    @Override
    public int hashCode() {
        return Objects.hash(mUserId,
        return Objects.hash(mConfiguration,
                mConfigureAutoDetectionEnabled,
                mConfigureGeoDetectionEnabled,
                mSuggestManualTimeZone);
@@ -186,7 +229,7 @@ public final class TimeZoneCapabilities implements Parcelable {
    @Override
    public String toString() {
        return "TimeZoneDetectorCapabilities{"
                + "mUserId=" + mUserId
                + "mConfiguration=" + mConfiguration
                + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled
                + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled
                + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone
@@ -196,16 +239,18 @@ public final class TimeZoneCapabilities implements Parcelable {
    /** @hide */
    public static class Builder {

        private final @UserIdInt int mUserId;
        private TimeZoneConfiguration mConfiguration;
        private @CapabilityState int mConfigureAutoDetectionEnabled;
        private @CapabilityState int mConfigureGeoDetectionEnabled;
        private @CapabilityState int mSuggestManualTimeZone;

        /**
         * Creates a new Builder with no properties set.
         */
        public Builder(@UserIdInt int userId) {
            mUserId = userId;
        /** Sets the user-visible configuration settings. */
        public Builder setConfiguration(@NonNull TimeZoneConfiguration configuration) {
            if (!configuration.isComplete()) {
                throw new IllegalArgumentException(configuration + " is not complete");
            }
            this.mConfiguration = configuration;
            return this;
        }

        /** Sets the state for the automatic time zone detection enabled config. */
+86 −62
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.app.timezonedetector;

import android.annotation.NonNull;
import android.annotation.StringDef;
import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,21 +28,20 @@ import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
 * Configuration that controls the behavior of the time zone detector associated with a specific
 * user.
 * User visible settings that control the behavior of the time zone detector / manual time zone
 * entry.
 *
 * <p>Configuration consists of a set of known properties. When reading configuration via
 * {@link TimeZoneDetector#getConfiguration()} values for all known properties will be provided. In
 * some cases, such as when the configuration relies on optional hardware, the values may be
 * meaningless / defaulted to safe values.
 * <p>When reading the configuration, values for all settings will be provided. In some cases, such
 * as when the device behavior relies on optional hardware / OEM configuration, or the value of
 * several settings, the device behavior may not be directly affected by the setting value.
 *
 * <p>Configuration properties can be left absent when updating configuration via {@link
 * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those values will not be
 * changed. Not all configuration properties can be modified by all users. See {@link
 * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities}.
 * <p>Settings can be left absent when updating configuration via {@link
 * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those settings will not be
 * changed. Not all configuration settings can be modified by all users: see {@link
 * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities} for details.
 *
 * <p>See {@link #isComplete()} to tell if all known properties are present, and {@link
 * #hasProperty(String)} with {@code PROPERTY_} constants for testing individual properties.
 * <p>See {@link #hasSetting(String)} with {@code PROPERTY_} constants for testing for the presence
 * of individual settings.
 *
 * @hide
 */
@@ -59,80 +59,82 @@ public final class TimeZoneConfiguration implements Parcelable {
            };

    /** All configuration properties */
    @StringDef(PROPERTY_AUTO_DETECTION_ENABLED)
    @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED })
    @Retention(RetentionPolicy.SOURCE)
    @interface Property {}
    @interface Setting {}

    /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */
    @Property
    public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
    @Setting
    public static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";

    /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */
    @Property
    public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
    @Setting
    public static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled";

    private final Bundle mBundle;
    private final @UserIdInt int mUserId;
    @NonNull private final Bundle mBundle;

    private TimeZoneConfiguration(Builder builder) {
        this.mBundle = builder.mBundle;
        this.mUserId = builder.mUserId;
        this.mBundle = Objects.requireNonNull(builder.mBundle);
    }

    private static TimeZoneConfiguration createFromParcel(Parcel in) {
        return new TimeZoneConfiguration.Builder()
        return new TimeZoneConfiguration.Builder(in.readInt())
                .setPropertyBundleInternal(in.readBundle())
                .build();
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeInt(mUserId);
        dest.writeBundle(mBundle);
    }

    /** Returns {@code true} if all known properties are set. */
    /** Returns the ID of the user this configuration is associated with. */
    public @UserIdInt int getUserId() {
        return mUserId;
    }

    /** Returns {@code true} if all known settings are present. */
    public boolean isComplete() {
        return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
                && hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
        return hasSetting(SETTING_AUTO_DETECTION_ENABLED)
                && hasSetting(SETTING_GEO_DETECTION_ENABLED);
    }

    /** Returns true if the specified property is set. */
    public boolean hasProperty(@Property String property) {
        return mBundle.containsKey(property);
    /** Returns true if the specified setting is set. */
    public boolean hasSetting(@Setting String setting) {
        return mBundle.containsKey(setting);
    }

    /**
     * Returns the value of the {@link #PROPERTY_AUTO_DETECTION_ENABLED} property. This
     * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This
     * controls whether a device will attempt to determine the time zone automatically using
     * contextual information.
     * contextual information if the device supports auto detection.
     *
     * <p>This setting is global and can be updated by some users.
     *
     * @throws IllegalStateException if the field has not been set
     * @throws IllegalStateException if the setting has not been set
     */
    public boolean isAutoDetectionEnabled() {
        if (!mBundle.containsKey(PROPERTY_AUTO_DETECTION_ENABLED)) {
            throw new IllegalStateException(PROPERTY_AUTO_DETECTION_ENABLED + " is not set");
        }
        return mBundle.getBoolean(PROPERTY_AUTO_DETECTION_ENABLED);
        enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED);
        return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED);
    }

    /**
     * Returns the value of the {@link #PROPERTY_GEO_DETECTION_ENABLED} property. This
     * controls whether a device can use location to determine time zone. Only used when
     * {@link #isAutoDetectionEnabled()} is true.
     * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This
     * controls whether a device can use geolocation to determine time zone. Only used when
     * {@link #isAutoDetectionEnabled()} is {@code true} and when the user has allowed their
     * location to be used.
     *
     * <p>This setting is user-scoped and can be updated by some users.
     * See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabled()}.
     *
     * @throws IllegalStateException if the field has not been set
     * @throws IllegalStateException if the setting has not been set
     */
    public boolean isGeoDetectionEnabled() {
        if (!mBundle.containsKey(PROPERTY_GEO_DETECTION_ENABLED)) {
            throw new IllegalStateException(PROPERTY_GEO_DETECTION_ENABLED + " is not set");
        }
        return mBundle.getBoolean(PROPERTY_GEO_DETECTION_ENABLED);
    }

    /**
     * Convenience method to merge this with another. The argument configuration properties have
     * precedence.
     */
    public TimeZoneConfiguration with(TimeZoneConfiguration other) {
        return new Builder(this).mergeProperties(other).build();
        enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED);
        return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED);
    }

    @Override
@@ -149,43 +151,61 @@ public final class TimeZoneConfiguration implements Parcelable {
            return false;
        }
        TimeZoneConfiguration that = (TimeZoneConfiguration) o;
        return mBundle.kindofEquals(that.mBundle);
        return mUserId == that.mUserId
                && mBundle.kindofEquals(that.mBundle);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mBundle);
        return Objects.hash(mUserId, mBundle);
    }

    @Override
    public String toString() {
        return "TimeZoneDetectorConfiguration{"
                + "mUserId=" + mUserId
                + "mBundle=" + mBundle
                + '}';
    }

    private void enforceSettingPresent(@Setting String setting) {
        if (!mBundle.containsKey(setting)) {
            throw new IllegalStateException(setting + " is not set");
        }
    }

    /** @hide */
    public static class Builder {

        private Bundle mBundle = new Bundle();
        private final @UserIdInt int mUserId;
        private final Bundle mBundle = new Bundle();

        /**
         * Creates a new Builder with no properties set.
         * Creates a new Builder for a userId with no settings held.
         */
        public Builder() {}
        public Builder(@UserIdInt int userId) {
            mUserId = userId;
        }

        /**
         * Creates a new Builder by copying properties from an existing instance.
         * Creates a new Builder by copying the user ID and settings from an existing instance.
         */
        public Builder(TimeZoneConfiguration toCopy) {
            this.mUserId = toCopy.mUserId;
            mergeProperties(toCopy);
        }

        /**
         * Merges {@code other} properties into this instances, replacing existing values in this
         * where the properties appear in both.
         * Merges {@code other} settings into this instances, replacing existing values in this
         * where the settings appear in both.
         */
        public Builder mergeProperties(TimeZoneConfiguration other) {
            if (mUserId != other.mUserId) {
                throw new IllegalArgumentException(
                        "Cannot merge configurations for different user IDs."
                                + " this.mUserId=" + this.mUserId
                                + ", other.mUserId=" + other.mUserId);
            }
            this.mBundle.putAll(other.mBundle);
            return this;
        }
@@ -195,15 +215,19 @@ public final class TimeZoneConfiguration implements Parcelable {
            return this;
        }

        /** Sets the desired state of the automatic time zone detection property. */
        /**
         * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting.
         */
        public Builder setAutoDetectionEnabled(boolean enabled) {
            this.mBundle.putBoolean(PROPERTY_AUTO_DETECTION_ENABLED, enabled);
            this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled);
            return this;
        }

        /** Sets the desired state of the geolocation time zone detection enabled property. */
        /**
         * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting.
         */
        public Builder setGeoDetectionEnabled(boolean enabled) {
            this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled);
            this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled);
            return this;
        }

+20 −22

File changed.

Preview size limit exceeded, changes collapsed.

Loading